mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 07:04:10 +08:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc-merge
This commit is contained in:
commit
6150c32589
521
Documentation/filesystems/spufs.txt
Normal file
521
Documentation/filesystems/spufs.txt
Normal file
@ -0,0 +1,521 @@
|
||||
SPUFS(2) Linux Programmer's Manual SPUFS(2)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
spufs - the SPU file system
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
The SPU file system is used on PowerPC machines that implement the Cell
|
||||
Broadband Engine Architecture in order to access Synergistic Processor
|
||||
Units (SPUs).
|
||||
|
||||
The file system provides a name space similar to posix shared memory or
|
||||
message queues. Users that have write permissions on the file system
|
||||
can use spu_create(2) to establish SPU contexts in the spufs root.
|
||||
|
||||
Every SPU context is represented by a directory containing a predefined
|
||||
set of files. These files can be used for manipulating the state of the
|
||||
logical SPU. Users can change permissions on those files, but not actu-
|
||||
ally add or remove files.
|
||||
|
||||
|
||||
MOUNT OPTIONS
|
||||
uid=<uid>
|
||||
set the user owning the mount point, the default is 0 (root).
|
||||
|
||||
gid=<gid>
|
||||
set the group owning the mount point, the default is 0 (root).
|
||||
|
||||
|
||||
FILES
|
||||
The files in spufs mostly follow the standard behavior for regular sys-
|
||||
tem calls like read(2) or write(2), but often support only a subset of
|
||||
the operations supported on regular file systems. This list details the
|
||||
supported operations and the deviations from the behaviour in the
|
||||
respective man pages.
|
||||
|
||||
All files that support the read(2) operation also support readv(2) and
|
||||
all files that support the write(2) operation also support writev(2).
|
||||
All files support the access(2) and stat(2) family of operations, but
|
||||
only the st_mode, st_nlink, st_uid and st_gid fields of struct stat
|
||||
contain reliable information.
|
||||
|
||||
All files support the chmod(2)/fchmod(2) and chown(2)/fchown(2) opera-
|
||||
tions, but will not be able to grant permissions that contradict the
|
||||
possible operations, e.g. read access on the wbox file.
|
||||
|
||||
The current set of files is:
|
||||
|
||||
|
||||
/mem
|
||||
the contents of the local storage memory of the SPU. This can be
|
||||
accessed like a regular shared memory file and contains both code and
|
||||
data in the address space of the SPU. The possible operations on an
|
||||
open mem file are:
|
||||
|
||||
read(2), pread(2), write(2), pwrite(2), lseek(2)
|
||||
These operate as documented, with the exception that seek(2),
|
||||
write(2) and pwrite(2) are not supported beyond the end of the
|
||||
file. The file size is the size of the local storage of the SPU,
|
||||
which normally is 256 kilobytes.
|
||||
|
||||
mmap(2)
|
||||
Mapping mem into the process address space gives access to the
|
||||
SPU local storage within the process address space. Only
|
||||
MAP_SHARED mappings are allowed.
|
||||
|
||||
|
||||
/mbox
|
||||
The first SPU to CPU communication mailbox. This file is read-only and
|
||||
can be read in units of 32 bits. The file can only be used in non-
|
||||
blocking mode and it even poll() will not block on it. The possible
|
||||
operations on an open mbox file are:
|
||||
|
||||
read(2)
|
||||
If a count smaller than four is requested, read returns -1 and
|
||||
sets errno to EINVAL. If there is no data available in the mail
|
||||
box, the return value is set to -1 and errno becomes EAGAIN.
|
||||
When data has been read successfully, four bytes are placed in
|
||||
the data buffer and the value four is returned.
|
||||
|
||||
|
||||
/ibox
|
||||
The second SPU to CPU communication mailbox. This file is similar to
|
||||
the first mailbox file, but can be read in blocking I/O mode, and the
|
||||
poll familiy of system calls can be used to wait for it. The possible
|
||||
operations on an open ibox file are:
|
||||
|
||||
read(2)
|
||||
If a count smaller than four is requested, read returns -1 and
|
||||
sets errno to EINVAL. If there is no data available in the mail
|
||||
box and the file descriptor has been opened with O_NONBLOCK, the
|
||||
return value is set to -1 and errno becomes EAGAIN.
|
||||
|
||||
If there is no data available in the mail box and the file
|
||||
descriptor has been opened without O_NONBLOCK, the call will
|
||||
block until the SPU writes to its interrupt mailbox channel.
|
||||
When data has been read successfully, four bytes are placed in
|
||||
the data buffer and the value four is returned.
|
||||
|
||||
poll(2)
|
||||
Poll on the ibox file returns (POLLIN | POLLRDNORM) whenever
|
||||
data is available for reading.
|
||||
|
||||
|
||||
/wbox
|
||||
The CPU to SPU communation mailbox. It is write-only can can be written
|
||||
in units of 32 bits. If the mailbox is full, write() will block and
|
||||
poll can be used to wait for it becoming empty again. The possible
|
||||
operations on an open wbox file are: write(2) If a count smaller than
|
||||
four is requested, write returns -1 and sets errno to EINVAL. If there
|
||||
is no space available in the mail box and the file descriptor has been
|
||||
opened with O_NONBLOCK, the return value is set to -1 and errno becomes
|
||||
EAGAIN.
|
||||
|
||||
If there is no space available in the mail box and the file descriptor
|
||||
has been opened without O_NONBLOCK, the call will block until the SPU
|
||||
reads from its PPE mailbox channel. When data has been read success-
|
||||
fully, four bytes are placed in the data buffer and the value four is
|
||||
returned.
|
||||
|
||||
poll(2)
|
||||
Poll on the ibox file returns (POLLOUT | POLLWRNORM) whenever
|
||||
space is available for writing.
|
||||
|
||||
|
||||
/mbox_stat
|
||||
/ibox_stat
|
||||
/wbox_stat
|
||||
Read-only files that contain the length of the current queue, i.e. how
|
||||
many words can be read from mbox or ibox or how many words can be
|
||||
written to wbox without blocking. The files can be read only in 4-byte
|
||||
units and return a big-endian binary integer number. The possible
|
||||
operations on an open *box_stat file are:
|
||||
|
||||
read(2)
|
||||
If a count smaller than four is requested, read returns -1 and
|
||||
sets errno to EINVAL. Otherwise, a four byte value is placed in
|
||||
the data buffer, containing the number of elements that can be
|
||||
read from (for mbox_stat and ibox_stat) or written to (for
|
||||
wbox_stat) the respective mail box without blocking or resulting
|
||||
in EAGAIN.
|
||||
|
||||
|
||||
/npc
|
||||
/decr
|
||||
/decr_status
|
||||
/spu_tag_mask
|
||||
/event_mask
|
||||
/srr0
|
||||
Internal registers of the SPU. The representation is an ASCII string
|
||||
with the numeric value of the next instruction to be executed. These
|
||||
can be used in read/write mode for debugging, but normal operation of
|
||||
programs should not rely on them because access to any of them except
|
||||
npc requires an SPU context save and is therefore very inefficient.
|
||||
|
||||
The contents of these files are:
|
||||
|
||||
npc Next Program Counter
|
||||
|
||||
decr SPU Decrementer
|
||||
|
||||
decr_status Decrementer Status
|
||||
|
||||
spu_tag_mask MFC tag mask for SPU DMA
|
||||
|
||||
event_mask Event mask for SPU interrupts
|
||||
|
||||
srr0 Interrupt Return address register
|
||||
|
||||
|
||||
The possible operations on an open npc, decr, decr_status,
|
||||
spu_tag_mask, event_mask or srr0 file are:
|
||||
|
||||
read(2)
|
||||
When the count supplied to the read call is shorter than the
|
||||
required length for the pointer value plus a newline character,
|
||||
subsequent reads from the same file descriptor will result in
|
||||
completing the string, regardless of changes to the register by
|
||||
a running SPU task. When a complete string has been read, all
|
||||
subsequent read operations will return zero bytes and a new file
|
||||
descriptor needs to be opened to read the value again.
|
||||
|
||||
write(2)
|
||||
A write operation on the file results in setting the register to
|
||||
the value given in the string. The string is parsed from the
|
||||
beginning to the first non-numeric character or the end of the
|
||||
buffer. Subsequent writes to the same file descriptor overwrite
|
||||
the previous setting.
|
||||
|
||||
|
||||
/fpcr
|
||||
This file gives access to the Floating Point Status and Control Regis-
|
||||
ter as a four byte long file. The operations on the fpcr file are:
|
||||
|
||||
read(2)
|
||||
If a count smaller than four is requested, read returns -1 and
|
||||
sets errno to EINVAL. Otherwise, a four byte value is placed in
|
||||
the data buffer, containing the current value of the fpcr regis-
|
||||
ter.
|
||||
|
||||
write(2)
|
||||
If a count smaller than four is requested, write returns -1 and
|
||||
sets errno to EINVAL. Otherwise, a four byte value is copied
|
||||
from the data buffer, updating the value of the fpcr register.
|
||||
|
||||
|
||||
/signal1
|
||||
/signal2
|
||||
The two signal notification channels of an SPU. These are read-write
|
||||
files that operate on a 32 bit word. Writing to one of these files
|
||||
triggers an interrupt on the SPU. The value writting to the signal
|
||||
files can be read from the SPU through a channel read or from host user
|
||||
space through the file. After the value has been read by the SPU, it
|
||||
is reset to zero. The possible operations on an open signal1 or sig-
|
||||
nal2 file are:
|
||||
|
||||
read(2)
|
||||
If a count smaller than four is requested, read returns -1 and
|
||||
sets errno to EINVAL. Otherwise, a four byte value is placed in
|
||||
the data buffer, containing the current value of the specified
|
||||
signal notification register.
|
||||
|
||||
write(2)
|
||||
If a count smaller than four is requested, write returns -1 and
|
||||
sets errno to EINVAL. Otherwise, a four byte value is copied
|
||||
from the data buffer, updating the value of the specified signal
|
||||
notification register. The signal notification register will
|
||||
either be replaced with the input data or will be updated to the
|
||||
bitwise OR or the old value and the input data, depending on the
|
||||
contents of the signal1_type, or signal2_type respectively,
|
||||
file.
|
||||
|
||||
|
||||
/signal1_type
|
||||
/signal2_type
|
||||
These two files change the behavior of the signal1 and signal2 notifi-
|
||||
cation files. The contain a numerical ASCII string which is read as
|
||||
either "1" or "0". In mode 0 (overwrite), the hardware replaces the
|
||||
contents of the signal channel with the data that is written to it. in
|
||||
mode 1 (logical OR), the hardware accumulates the bits that are subse-
|
||||
quently written to it. The possible operations on an open signal1_type
|
||||
or signal2_type file are:
|
||||
|
||||
read(2)
|
||||
When the count supplied to the read call is shorter than the
|
||||
required length for the digit plus a newline character, subse-
|
||||
quent reads from the same file descriptor will result in com-
|
||||
pleting the string. When a complete string has been read, all
|
||||
subsequent read operations will return zero bytes and a new file
|
||||
descriptor needs to be opened to read the value again.
|
||||
|
||||
write(2)
|
||||
A write operation on the file results in setting the register to
|
||||
the value given in the string. The string is parsed from the
|
||||
beginning to the first non-numeric character or the end of the
|
||||
buffer. Subsequent writes to the same file descriptor overwrite
|
||||
the previous setting.
|
||||
|
||||
|
||||
EXAMPLES
|
||||
/etc/fstab entry
|
||||
none /spu spufs gid=spu 0 0
|
||||
|
||||
|
||||
AUTHORS
|
||||
Arnd Bergmann <arndb@de.ibm.com>, Mark Nutter <mnutter@us.ibm.com>,
|
||||
Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
|
||||
|
||||
SEE ALSO
|
||||
capabilities(7), close(2), spu_create(2), spu_run(2), spufs(7)
|
||||
|
||||
|
||||
|
||||
Linux 2005-09-28 SPUFS(2)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
SPU_RUN(2) Linux Programmer's Manual SPU_RUN(2)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
spu_run - execute an spu context
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
#include <sys/spu.h>
|
||||
|
||||
int spu_run(int fd, unsigned int *npc, unsigned int *event);
|
||||
|
||||
DESCRIPTION
|
||||
The spu_run system call is used on PowerPC machines that implement the
|
||||
Cell Broadband Engine Architecture in order to access Synergistic Pro-
|
||||
cessor Units (SPUs). It uses the fd that was returned from spu_cre-
|
||||
ate(2) to address a specific SPU context. When the context gets sched-
|
||||
uled to a physical SPU, it starts execution at the instruction pointer
|
||||
passed in npc.
|
||||
|
||||
Execution of SPU code happens synchronously, meaning that spu_run does
|
||||
not return while the SPU is still running. If there is a need to exe-
|
||||
cute SPU code in parallel with other code on either the main CPU or
|
||||
other SPUs, you need to create a new thread of execution first, e.g.
|
||||
using the pthread_create(3) call.
|
||||
|
||||
When spu_run returns, the current value of the SPU instruction pointer
|
||||
is written back to npc, so you can call spu_run again without updating
|
||||
the pointers.
|
||||
|
||||
event can be a NULL pointer or point to an extended status code that
|
||||
gets filled when spu_run returns. It can be one of the following con-
|
||||
stants:
|
||||
|
||||
SPE_EVENT_DMA_ALIGNMENT
|
||||
A DMA alignment error
|
||||
|
||||
SPE_EVENT_SPE_DATA_SEGMENT
|
||||
A DMA segmentation error
|
||||
|
||||
SPE_EVENT_SPE_DATA_STORAGE
|
||||
A DMA storage error
|
||||
|
||||
If NULL is passed as the event argument, these errors will result in a
|
||||
signal delivered to the calling process.
|
||||
|
||||
RETURN VALUE
|
||||
spu_run returns the value of the spu_status register or -1 to indicate
|
||||
an error and set errno to one of the error codes listed below. The
|
||||
spu_status register value contains a bit mask of status codes and
|
||||
optionally a 14 bit code returned from the stop-and-signal instruction
|
||||
on the SPU. The bit masks for the status codes are:
|
||||
|
||||
0x02 SPU was stopped by stop-and-signal.
|
||||
|
||||
0x04 SPU was stopped by halt.
|
||||
|
||||
0x08 SPU is waiting for a channel.
|
||||
|
||||
0x10 SPU is in single-step mode.
|
||||
|
||||
0x20 SPU has tried to execute an invalid instruction.
|
||||
|
||||
0x40 SPU has tried to access an invalid channel.
|
||||
|
||||
0x3fff0000
|
||||
The bits masked with this value contain the code returned from
|
||||
stop-and-signal.
|
||||
|
||||
There are always one or more of the lower eight bits set or an error
|
||||
code is returned from spu_run.
|
||||
|
||||
ERRORS
|
||||
EAGAIN or EWOULDBLOCK
|
||||
fd is in non-blocking mode and spu_run would block.
|
||||
|
||||
EBADF fd is not a valid file descriptor.
|
||||
|
||||
EFAULT npc is not a valid pointer or status is neither NULL nor a valid
|
||||
pointer.
|
||||
|
||||
EINTR A signal occured while spu_run was in progress. The npc value
|
||||
has been updated to the new program counter value if necessary.
|
||||
|
||||
EINVAL fd is not a file descriptor returned from spu_create(2).
|
||||
|
||||
ENOMEM Insufficient memory was available to handle a page fault result-
|
||||
ing from an MFC direct memory access.
|
||||
|
||||
ENOSYS the functionality is not provided by the current system, because
|
||||
either the hardware does not provide SPUs or the spufs module is
|
||||
not loaded.
|
||||
|
||||
|
||||
NOTES
|
||||
spu_run is meant to be used from libraries that implement a more
|
||||
abstract interface to SPUs, not to be used from regular applications.
|
||||
See http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
|
||||
ommended libraries.
|
||||
|
||||
|
||||
CONFORMING TO
|
||||
This call is Linux specific and only implemented by the ppc64 architec-
|
||||
ture. Programs using this system call are not portable.
|
||||
|
||||
|
||||
BUGS
|
||||
The code does not yet fully implement all features lined out here.
|
||||
|
||||
|
||||
AUTHOR
|
||||
Arnd Bergmann <arndb@de.ibm.com>
|
||||
|
||||
SEE ALSO
|
||||
capabilities(7), close(2), spu_create(2), spufs(7)
|
||||
|
||||
|
||||
|
||||
Linux 2005-09-28 SPU_RUN(2)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
SPU_CREATE(2) Linux Programmer's Manual SPU_CREATE(2)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
spu_create - create a new spu context
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
#include <sys/types.h>
|
||||
#include <sys/spu.h>
|
||||
|
||||
int spu_create(const char *pathname, int flags, mode_t mode);
|
||||
|
||||
DESCRIPTION
|
||||
The spu_create system call is used on PowerPC machines that implement
|
||||
the Cell Broadband Engine Architecture in order to access Synergistic
|
||||
Processor Units (SPUs). It creates a new logical context for an SPU in
|
||||
pathname and returns a handle to associated with it. pathname must
|
||||
point to a non-existing directory in the mount point of the SPU file
|
||||
system (spufs). When spu_create is successful, a directory gets cre-
|
||||
ated on pathname and it is populated with files.
|
||||
|
||||
The returned file handle can only be passed to spu_run(2) or closed,
|
||||
other operations are not defined on it. When it is closed, all associ-
|
||||
ated directory entries in spufs are removed. When the last file handle
|
||||
pointing either inside of the context directory or to this file
|
||||
descriptor is closed, the logical SPU context is destroyed.
|
||||
|
||||
The parameter flags can be zero or any bitwise or'd combination of the
|
||||
following constants:
|
||||
|
||||
SPU_RAWIO
|
||||
Allow mapping of some of the hardware registers of the SPU into
|
||||
user space. This flag requires the CAP_SYS_RAWIO capability, see
|
||||
capabilities(7).
|
||||
|
||||
The mode parameter specifies the permissions used for creating the new
|
||||
directory in spufs. mode is modified with the user's umask(2) value
|
||||
and then used for both the directory and the files contained in it. The
|
||||
file permissions mask out some more bits of mode because they typically
|
||||
support only read or write access. See stat(2) for a full list of the
|
||||
possible mode values.
|
||||
|
||||
|
||||
RETURN VALUE
|
||||
spu_create returns a new file descriptor. It may return -1 to indicate
|
||||
an error condition and set errno to one of the error codes listed
|
||||
below.
|
||||
|
||||
|
||||
ERRORS
|
||||
EACCESS
|
||||
The current user does not have write access on the spufs mount
|
||||
point.
|
||||
|
||||
EEXIST An SPU context already exists at the given path name.
|
||||
|
||||
EFAULT pathname is not a valid string pointer in the current address
|
||||
space.
|
||||
|
||||
EINVAL pathname is not a directory in the spufs mount point.
|
||||
|
||||
ELOOP Too many symlinks were found while resolving pathname.
|
||||
|
||||
EMFILE The process has reached its maximum open file limit.
|
||||
|
||||
ENAMETOOLONG
|
||||
pathname was too long.
|
||||
|
||||
ENFILE The system has reached the global open file limit.
|
||||
|
||||
ENOENT Part of pathname could not be resolved.
|
||||
|
||||
ENOMEM The kernel could not allocate all resources required.
|
||||
|
||||
ENOSPC There are not enough SPU resources available to create a new
|
||||
context or the user specific limit for the number of SPU con-
|
||||
texts has been reached.
|
||||
|
||||
ENOSYS the functionality is not provided by the current system, because
|
||||
either the hardware does not provide SPUs or the spufs module is
|
||||
not loaded.
|
||||
|
||||
ENOTDIR
|
||||
A part of pathname is not a directory.
|
||||
|
||||
|
||||
|
||||
NOTES
|
||||
spu_create is meant to be used from libraries that implement a more
|
||||
abstract interface to SPUs, not to be used from regular applications.
|
||||
See http://www.bsc.es/projects/deepcomputing/linuxoncell/ for the rec-
|
||||
ommended libraries.
|
||||
|
||||
|
||||
FILES
|
||||
pathname must point to a location beneath the mount point of spufs. By
|
||||
convention, it gets mounted in /spu.
|
||||
|
||||
|
||||
CONFORMING TO
|
||||
This call is Linux specific and only implemented by the ppc64 architec-
|
||||
ture. Programs using this system call are not portable.
|
||||
|
||||
|
||||
BUGS
|
||||
The code does not yet fully implement all features lined out here.
|
||||
|
||||
|
||||
AUTHOR
|
||||
Arnd Bergmann <arndb@de.ibm.com>
|
||||
|
||||
SEE ALSO
|
||||
capabilities(7), close(2), spu_run(2), spufs(7)
|
||||
|
||||
|
||||
|
||||
Linux 2005-09-28 SPU_CREATE(2)
|
@ -8,12 +8,18 @@ please mail me.
|
||||
cpu_features.txt
|
||||
- info on how we support a variety of CPUs with minimal compile-time
|
||||
options.
|
||||
eeh-pci-error-recovery.txt
|
||||
- info on PCI Bus EEH Error Recovery
|
||||
hvcs.txt
|
||||
- IBM "Hypervisor Virtual Console Server" Installation Guide
|
||||
mpc52xx.txt
|
||||
- Linux 2.6.x on MPC52xx family
|
||||
ppc_htab.txt
|
||||
- info about the Linux/PPC /proc/ppc_htab entry
|
||||
smp.txt
|
||||
- use and state info about Linux/PPC on MP machines
|
||||
SBC8260_memory_mapping.txt
|
||||
- EST SBC8260 board info
|
||||
smp.txt
|
||||
- use and state info about Linux/PPC on MP machines
|
||||
sound.txt
|
||||
- info on sound support under Linux/PPC
|
||||
zImage_layout.txt
|
||||
|
@ -47,7 +47,7 @@ config PPC
|
||||
|
||||
config EARLY_PRINTK
|
||||
bool
|
||||
default y if PPC64
|
||||
default y
|
||||
|
||||
config COMPAT
|
||||
bool
|
||||
@ -297,6 +297,7 @@ config PPC_PMAC64
|
||||
bool
|
||||
depends on PPC_PMAC && POWER4
|
||||
select U3_DART
|
||||
select MPIC_BROKEN_U3
|
||||
select GENERIC_TBSYNC
|
||||
default y
|
||||
|
||||
@ -325,9 +326,7 @@ config PPC_CELL
|
||||
select MMIO_NVRAM
|
||||
|
||||
config PPC_OF
|
||||
bool
|
||||
depends on PPC_MULTIPLATFORM # for now
|
||||
default y
|
||||
def_bool y
|
||||
|
||||
config XICS
|
||||
depends on PPC_PSERIES
|
||||
@ -376,11 +375,28 @@ config CELL_IIC
|
||||
bool
|
||||
default y
|
||||
|
||||
config CRASH_DUMP
|
||||
bool "kernel crash dumps (EXPERIMENTAL)"
|
||||
depends on PPC_MULTIPLATFORM
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
Build a kernel suitable for use as a kdump capture kernel.
|
||||
The kernel will be linked at a different address than normal, and
|
||||
so can only be used for Kdump.
|
||||
|
||||
Don't change this unless you know what you are doing.
|
||||
|
||||
config IBMVIO
|
||||
depends on PPC_PSERIES || PPC_ISERIES
|
||||
bool
|
||||
default y
|
||||
|
||||
config IBMEBUS
|
||||
depends on PPC_PSERIES
|
||||
bool "Support for GX bus based adapters"
|
||||
help
|
||||
Bus device driver for GX bus based adapters.
|
||||
|
||||
config PPC_MPC106
|
||||
bool
|
||||
default n
|
||||
@ -472,6 +488,7 @@ source arch/powerpc/platforms/embedded6xx/Kconfig
|
||||
source arch/powerpc/platforms/4xx/Kconfig
|
||||
source arch/powerpc/platforms/85xx/Kconfig
|
||||
source arch/powerpc/platforms/8xx/Kconfig
|
||||
source arch/powerpc/platforms/cell/Kconfig
|
||||
|
||||
menu "Kernel options"
|
||||
|
||||
@ -575,11 +592,12 @@ config ARCH_SELECT_MEMORY_MODEL
|
||||
depends on PPC64
|
||||
|
||||
config ARCH_FLATMEM_ENABLE
|
||||
def_bool y
|
||||
depends on PPC64 && !NUMA
|
||||
def_bool y
|
||||
depends on (PPC64 && !NUMA) || PPC32
|
||||
|
||||
config ARCH_SPARSEMEM_ENABLE
|
||||
def_bool y
|
||||
depends on PPC64
|
||||
|
||||
config ARCH_SPARSEMEM_DEFAULT
|
||||
def_bool y
|
||||
|
@ -151,7 +151,7 @@ CPPFLAGS_vmlinux.lds := -Upowerpc
|
||||
# All the instructions talk about "make bzImage".
|
||||
bzImage: zImage
|
||||
|
||||
BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm
|
||||
BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd vmlinux.sm uImage
|
||||
|
||||
.PHONY: $(BOOT_TARGETS)
|
||||
|
||||
|
@ -143,6 +143,36 @@ $(obj)/zImage.initrd: $(obj)/zImage.initrd.vmode $(obj)/addnote
|
||||
@cp -f $< $@
|
||||
$(call if_changed,addnote)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# build u-boot images
|
||||
#-----------------------------------------------------------
|
||||
quiet_cmd_mygzip = GZIP $@
|
||||
cmd_mygzip = gzip -f -9 < $< > $@.$$$$ && mv $@.$$$$ $@
|
||||
|
||||
quiet_cmd_objbin = OBJCOPY $@
|
||||
cmd_objbin = $(OBJCOPY) -O binary $< $@
|
||||
|
||||
quiet_cmd_uimage = UIMAGE $@
|
||||
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A ppc -O linux -T kernel \
|
||||
-C gzip -a 00000000 -e 00000000 -n 'Linux-$(KERNELRELEASE)' \
|
||||
-d $< $@
|
||||
|
||||
MKIMAGE := $(srctree)/scripts/mkuboot.sh
|
||||
targets += uImage
|
||||
extra-y += vmlinux.bin vmlinux.gz
|
||||
|
||||
$(obj)/vmlinux.bin: vmlinux FORCE
|
||||
$(call if_changed,objbin)
|
||||
|
||||
$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
|
||||
$(call if_changed,mygzip)
|
||||
|
||||
$(obj)/uImage: $(obj)/vmlinux.gz
|
||||
$(Q)rm -f $@
|
||||
$(call cmd,uimage)
|
||||
@echo -n ' Image: $@ '
|
||||
@if [ -f $@ ]; then echo 'is ready' ; else echo 'not made'; fi
|
||||
|
||||
install: $(CONFIGURE) $(BOOTIMAGE)
|
||||
sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" "$(BOOTIMAGE)"
|
||||
|
||||
|
1729
arch/powerpc/configs/pmac32_defconfig
Normal file
1729
arch/powerpc/configs/pmac32_defconfig
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,11 +17,11 @@ obj-y += vdso32/
|
||||
obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \
|
||||
signal_64.o ptrace32.o systbl.o \
|
||||
paca.o ioctl32.o cpu_setup_power4.o \
|
||||
firmware.o sysfs.o udbg.o idle_64.o
|
||||
firmware.o sysfs.o idle_64.o
|
||||
obj-$(CONFIG_PPC64) += vdso64/
|
||||
obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o
|
||||
obj-$(CONFIG_POWER4) += idle_power4.o
|
||||
obj-$(CONFIG_PPC_OF) += of_device.o
|
||||
obj-$(CONFIG_PPC_OF) += of_device.o prom_parse.o
|
||||
procfs-$(CONFIG_PPC64) := proc_ppc64.o
|
||||
obj-$(CONFIG_PROC_FS) += $(procfs-y)
|
||||
rtaspci-$(CONFIG_PPC64) := rtas_pci.o
|
||||
@ -30,12 +30,10 @@ obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o
|
||||
obj-$(CONFIG_RTAS_PROC) += rtas-proc.o
|
||||
obj-$(CONFIG_LPARCFG) += lparcfg.o
|
||||
obj-$(CONFIG_IBMVIO) += vio.o
|
||||
obj-$(CONFIG_IBMEBUS) += ibmebus.o
|
||||
obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o
|
||||
obj-$(CONFIG_PPC_PSERIES) += udbg_16550.o
|
||||
obj-$(CONFIG_PPC_MAPLE) += udbg_16550.o
|
||||
udbgscc-$(CONFIG_PPC64) := udbg_scc.o
|
||||
obj-$(CONFIG_PPC_PMAC) += $(udbgscc-y)
|
||||
obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
||||
|
||||
ifeq ($(CONFIG_PPC_MERGE),y)
|
||||
|
||||
@ -48,25 +46,25 @@ extra-$(CONFIG_8xx) := head_8xx.o
|
||||
extra-y += vmlinux.lds
|
||||
|
||||
obj-y += process.o init_task.o time.o \
|
||||
prom.o traps.o setup-common.o
|
||||
prom.o traps.o setup-common.o udbg.o
|
||||
obj-$(CONFIG_PPC32) += entry_32.o setup_32.o misc_32.o systbl.o
|
||||
obj-$(CONFIG_PPC64) += misc_64.o dma_64.o iommu.o
|
||||
obj-$(CONFIG_PPC_OF) += prom_init.o
|
||||
obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o
|
||||
obj-$(CONFIG_MODULES) += ppc_ksyms.o
|
||||
obj-$(CONFIG_BOOTX_TEXT) += btext.o
|
||||
obj-$(CONFIG_6xx) += idle_6xx.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
||||
obj-$(CONFIG_SERIAL_8250) += legacy_serial.o udbg_16550.o
|
||||
module-$(CONFIG_PPC64) += module_64.o
|
||||
obj-$(CONFIG_MODULES) += $(module-y)
|
||||
|
||||
pci64-$(CONFIG_PPC64) += pci_64.o pci_dn.o pci_iommu.o \
|
||||
pci_direct_iommu.o iomap.o
|
||||
obj-$(CONFIG_PCI) += $(pci64-y)
|
||||
|
||||
kexec64-$(CONFIG_PPC64) += machine_kexec_64.o
|
||||
obj-$(CONFIG_KEXEC) += $(kexec64-y)
|
||||
kexec-$(CONFIG_PPC64) := machine_kexec_64.o
|
||||
kexec-$(CONFIG_PPC32) := machine_kexec_32.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o $(kexec-y)
|
||||
|
||||
ifeq ($(CONFIG_PPC_ISERIES),y)
|
||||
$(obj)/head_64.o: $(obj)/lparmap.s
|
||||
|
@ -92,9 +92,9 @@ int main(void)
|
||||
|
||||
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
|
||||
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
|
||||
DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror));
|
||||
#ifdef CONFIG_PPC32
|
||||
DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame));
|
||||
DEFINE(TI_TASK, offsetof(struct thread_info, task));
|
||||
#ifdef CONFIG_PPC32
|
||||
DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
|
||||
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
|
||||
#endif /* CONFIG_PPC32 */
|
||||
@ -131,11 +131,9 @@ int main(void)
|
||||
DEFINE(PACALOWHTLBAREAS, offsetof(struct paca_struct, context.low_htlb_areas));
|
||||
DEFINE(PACAHIGHHTLBAREAS, offsetof(struct paca_struct, context.high_htlb_areas));
|
||||
#endif /* CONFIG_HUGETLB_PAGE */
|
||||
DEFINE(PACADEFAULTDECR, offsetof(struct paca_struct, default_decr));
|
||||
DEFINE(PACA_EXGEN, offsetof(struct paca_struct, exgen));
|
||||
DEFINE(PACA_EXMC, offsetof(struct paca_struct, exmc));
|
||||
DEFINE(PACA_EXSLB, offsetof(struct paca_struct, exslb));
|
||||
DEFINE(PACA_EXDSI, offsetof(struct paca_struct, exdsi));
|
||||
DEFINE(PACAEMERGSP, offsetof(struct paca_struct, emergency_sp));
|
||||
DEFINE(PACALPPACA, offsetof(struct paca_struct, lppaca));
|
||||
DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
|
||||
|
@ -31,15 +31,18 @@ static void draw_byte_32(unsigned char *bits, unsigned int *base, int rb);
|
||||
static void draw_byte_16(unsigned char *bits, unsigned int *base, int rb);
|
||||
static void draw_byte_8(unsigned char *bits, unsigned int *base, int rb);
|
||||
|
||||
static int g_loc_X;
|
||||
static int g_loc_Y;
|
||||
static int g_max_loc_X;
|
||||
static int g_max_loc_Y;
|
||||
#define __force_data __attribute__((__section__(".data")))
|
||||
|
||||
static int dispDeviceRowBytes;
|
||||
static int dispDeviceDepth;
|
||||
static int dispDeviceRect[4];
|
||||
static unsigned char *dispDeviceBase, *logicalDisplayBase;
|
||||
static int g_loc_X __force_data;
|
||||
static int g_loc_Y __force_data;
|
||||
static int g_max_loc_X __force_data;
|
||||
static int g_max_loc_Y __force_data;
|
||||
|
||||
static int dispDeviceRowBytes __force_data;
|
||||
static int dispDeviceDepth __force_data;
|
||||
static int dispDeviceRect[4] __force_data;
|
||||
static unsigned char *dispDeviceBase __force_data;
|
||||
static unsigned char *logicalDisplayBase __force_data;
|
||||
|
||||
unsigned long disp_BAT[2] __initdata = {0, 0};
|
||||
|
||||
@ -47,7 +50,7 @@ unsigned long disp_BAT[2] __initdata = {0, 0};
|
||||
|
||||
static unsigned char vga_font[cmapsz];
|
||||
|
||||
int boot_text_mapped;
|
||||
int boot_text_mapped __force_data = 0;
|
||||
int force_printk_to_btext = 0;
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
@ -57,7 +60,7 @@ int force_printk_to_btext = 0;
|
||||
*
|
||||
* The display is mapped to virtual address 0xD0000000, rather
|
||||
* than 1:1, because some some CHRP machines put the frame buffer
|
||||
* in the region starting at 0xC0000000 (KERNELBASE).
|
||||
* in the region starting at 0xC0000000 (PAGE_OFFSET).
|
||||
* This mapping is temporary and will disappear as soon as the
|
||||
* setup done by MMU_Init() is applied.
|
||||
*
|
||||
@ -66,10 +69,9 @@ int force_printk_to_btext = 0;
|
||||
* is really badly aligned, but I didn't encounter this case
|
||||
* yet.
|
||||
*/
|
||||
void __init
|
||||
btext_prepare_BAT(void)
|
||||
void __init btext_prepare_BAT(void)
|
||||
{
|
||||
unsigned long vaddr = KERNELBASE + 0x10000000;
|
||||
unsigned long vaddr = PAGE_OFFSET + 0x10000000;
|
||||
unsigned long addr;
|
||||
unsigned long lowbits;
|
||||
|
||||
@ -95,12 +97,13 @@ btext_prepare_BAT(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This function will enable the early boot text when doing OF booting. This
|
||||
* way, xmon output should work too
|
||||
|
||||
/* This function can be used to enable the early boot text when doing
|
||||
* OF booting or within bootx init. It must be followed by a btext_unmap()
|
||||
* call before the logical address becomes unuseable
|
||||
*/
|
||||
void __init
|
||||
btext_setup_display(int width, int height, int depth, int pitch,
|
||||
unsigned long address)
|
||||
void __init btext_setup_display(int width, int height, int depth, int pitch,
|
||||
unsigned long address)
|
||||
{
|
||||
g_loc_X = 0;
|
||||
g_loc_Y = 0;
|
||||
@ -116,6 +119,11 @@ btext_setup_display(int width, int height, int depth, int pitch,
|
||||
boot_text_mapped = 1;
|
||||
}
|
||||
|
||||
void __init btext_unmap(void)
|
||||
{
|
||||
boot_text_mapped = 0;
|
||||
}
|
||||
|
||||
/* Here's a small text engine to use during early boot
|
||||
* or for debugging purposes
|
||||
*
|
||||
@ -127,7 +135,7 @@ btext_setup_display(int width, int height, int depth, int pitch,
|
||||
* changes.
|
||||
*/
|
||||
|
||||
void map_boot_text(void)
|
||||
static void map_boot_text(void)
|
||||
{
|
||||
unsigned long base, offset, size;
|
||||
unsigned char *vbase;
|
||||
@ -175,8 +183,9 @@ int btext_initialize(struct device_node *np)
|
||||
if (prop)
|
||||
address = *prop;
|
||||
|
||||
/* FIXME: Add support for PCI reg properties */
|
||||
|
||||
/* FIXME: Add support for PCI reg properties. Right now, only
|
||||
* reliable on macs
|
||||
*/
|
||||
if (address == 0)
|
||||
return -EINVAL;
|
||||
|
||||
@ -184,7 +193,6 @@ int btext_initialize(struct device_node *np)
|
||||
g_loc_Y = 0;
|
||||
g_max_loc_X = width / 8;
|
||||
g_max_loc_Y = height / 16;
|
||||
logicalDisplayBase = (unsigned char *)address;
|
||||
dispDeviceBase = (unsigned char *)address;
|
||||
dispDeviceRowBytes = pitch;
|
||||
dispDeviceDepth = depth;
|
||||
@ -197,14 +205,12 @@ int btext_initialize(struct device_node *np)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init init_boot_display(void)
|
||||
int __init btext_find_display(int allow_nonstdout)
|
||||
{
|
||||
char *name;
|
||||
struct device_node *np = NULL;
|
||||
int rc = -ENODEV;
|
||||
|
||||
printk("trying to initialize btext ...\n");
|
||||
|
||||
name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name != NULL) {
|
||||
np = of_find_node_by_path(name);
|
||||
@ -218,8 +224,8 @@ void __init init_boot_display(void)
|
||||
}
|
||||
if (np)
|
||||
rc = btext_initialize(np);
|
||||
if (rc == 0)
|
||||
return;
|
||||
if (rc == 0 || !allow_nonstdout)
|
||||
return rc;
|
||||
|
||||
for (np = NULL; (np = of_find_node_by_type(np, "display"));) {
|
||||
if (get_property(np, "linux,opened", NULL)) {
|
||||
@ -228,8 +234,9 @@ void __init init_boot_display(void)
|
||||
printk("result: %d\n", rc);
|
||||
}
|
||||
if (rc == 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Calc the base address of a given point (x,y) */
|
||||
@ -277,44 +284,83 @@ EXPORT_SYMBOL(btext_update_display);
|
||||
|
||||
void btext_clearscreen(void)
|
||||
{
|
||||
unsigned long *base = (unsigned long *)calc_base(0, 0);
|
||||
unsigned int *base = (unsigned int *)calc_base(0, 0);
|
||||
unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
|
||||
(dispDeviceDepth >> 3)) >> 3;
|
||||
(dispDeviceDepth >> 3)) >> 2;
|
||||
int i,j;
|
||||
|
||||
for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++)
|
||||
{
|
||||
unsigned long *ptr = base;
|
||||
unsigned int *ptr = base;
|
||||
for(j=width; j; --j)
|
||||
*(ptr++) = 0;
|
||||
base += (dispDeviceRowBytes >> 3);
|
||||
base += (dispDeviceRowBytes >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
void btext_flushscreen(void)
|
||||
{
|
||||
unsigned int *base = (unsigned int *)calc_base(0, 0);
|
||||
unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
|
||||
(dispDeviceDepth >> 3)) >> 2;
|
||||
int i,j;
|
||||
|
||||
for (i=0; i < (dispDeviceRect[3] - dispDeviceRect[1]); i++)
|
||||
{
|
||||
unsigned int *ptr = base;
|
||||
for(j = width; j > 0; j -= 8) {
|
||||
__asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
|
||||
ptr += 8;
|
||||
}
|
||||
base += (dispDeviceRowBytes >> 2);
|
||||
}
|
||||
__asm__ __volatile__ ("sync" ::: "memory");
|
||||
}
|
||||
|
||||
void btext_flushline(void)
|
||||
{
|
||||
unsigned int *base = (unsigned int *)calc_base(0, g_loc_Y << 4);
|
||||
unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
|
||||
(dispDeviceDepth >> 3)) >> 2;
|
||||
int i,j;
|
||||
|
||||
for (i=0; i < 16; i++)
|
||||
{
|
||||
unsigned int *ptr = base;
|
||||
for(j = width; j > 0; j -= 8) {
|
||||
__asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
|
||||
ptr += 8;
|
||||
}
|
||||
base += (dispDeviceRowBytes >> 2);
|
||||
}
|
||||
__asm__ __volatile__ ("sync" ::: "memory");
|
||||
}
|
||||
|
||||
|
||||
#ifndef NO_SCROLL
|
||||
static void scrollscreen(void)
|
||||
{
|
||||
unsigned long *src = (unsigned long *)calc_base(0,16);
|
||||
unsigned long *dst = (unsigned long *)calc_base(0,0);
|
||||
unsigned int *src = (unsigned int *)calc_base(0,16);
|
||||
unsigned int *dst = (unsigned int *)calc_base(0,0);
|
||||
unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) *
|
||||
(dispDeviceDepth >> 3)) >> 3;
|
||||
(dispDeviceDepth >> 3)) >> 2;
|
||||
int i,j;
|
||||
|
||||
for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++)
|
||||
{
|
||||
unsigned long *src_ptr = src;
|
||||
unsigned long *dst_ptr = dst;
|
||||
unsigned int *src_ptr = src;
|
||||
unsigned int *dst_ptr = dst;
|
||||
for(j=width; j; --j)
|
||||
*(dst_ptr++) = *(src_ptr++);
|
||||
src += (dispDeviceRowBytes >> 3);
|
||||
dst += (dispDeviceRowBytes >> 3);
|
||||
src += (dispDeviceRowBytes >> 2);
|
||||
dst += (dispDeviceRowBytes >> 2);
|
||||
}
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned long *dst_ptr = dst;
|
||||
unsigned int *dst_ptr = dst;
|
||||
for(j=width; j; --j)
|
||||
*(dst_ptr++) = 0;
|
||||
dst += (dispDeviceRowBytes >> 3);
|
||||
dst += (dispDeviceRowBytes >> 2);
|
||||
}
|
||||
}
|
||||
#endif /* ndef NO_SCROLL */
|
||||
@ -377,6 +423,14 @@ void btext_drawstring(const char *c)
|
||||
btext_drawchar(*c++);
|
||||
}
|
||||
|
||||
void btext_drawtext(const char *c, unsigned int len)
|
||||
{
|
||||
if (!boot_text_mapped)
|
||||
return;
|
||||
while (len--)
|
||||
btext_drawchar(*c++);
|
||||
}
|
||||
|
||||
void btext_drawhex(unsigned long v)
|
||||
{
|
||||
char *hex_table = "0123456789abcdef";
|
||||
|
@ -78,10 +78,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power3",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* Power3+ */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -93,10 +91,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power3",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* Northstar */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -108,10 +104,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/rs64",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* Pulsar */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -123,10 +117,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/rs64",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* I-star */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -138,10 +130,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/rs64",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* S-star */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -153,10 +143,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power3,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/rs64",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = RS64,
|
||||
},
|
||||
{ /* Power4 */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -168,10 +156,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power4,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power4",
|
||||
.oprofile_model = &op_model_rs64,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
{ /* Power4+ */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -183,10 +169,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_power4,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power4",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
{ /* PPC970 */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -199,10 +183,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_ppc970,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/970",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
#endif /* CONFIG_PPC64 */
|
||||
#if defined(CONFIG_PPC64) || defined(CONFIG_POWER4)
|
||||
@ -221,10 +203,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 8,
|
||||
.cpu_setup = __setup_cpu_ppc970,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/970",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
#endif /* defined(CONFIG_PPC64) || defined(CONFIG_POWER4) */
|
||||
#ifdef CONFIG_PPC64
|
||||
@ -238,10 +218,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 128,
|
||||
.dcache_bsize = 128,
|
||||
.cpu_setup = __setup_cpu_ppc970,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/970",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
{ /* Power5 GR */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -253,27 +231,23 @@ struct cpu_spec cpu_specs[] = {
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_power4,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power5",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
{ /* Power5 GS */
|
||||
.pvr_mask = 0xffff0000,
|
||||
.pvr_value = 0x003b0000,
|
||||
.cpu_name = "POWER5 (gs)",
|
||||
.cpu_name = "POWER5+ (gs)",
|
||||
.cpu_features = CPU_FTRS_POWER5,
|
||||
.cpu_user_features = COMMON_USER_POWER5_PLUS,
|
||||
.icache_bsize = 128,
|
||||
.dcache_bsize = 128,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_power4,
|
||||
#ifdef CONFIG_OPROFILE
|
||||
.oprofile_cpu_type = "ppc64/power5",
|
||||
.oprofile_model = &op_model_power4,
|
||||
#endif
|
||||
.oprofile_cpu_type = "ppc64/power5+",
|
||||
.oprofile_type = POWER4,
|
||||
},
|
||||
{ /* BE DD1.x */
|
||||
{ /* Cell Broadband Engine */
|
||||
.pvr_mask = 0xffff0000,
|
||||
.pvr_value = 0x00700000,
|
||||
.cpu_name = "Cell Broadband Engine",
|
||||
@ -545,7 +519,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7450 2.1 */
|
||||
.pvr_mask = 0xffffffff,
|
||||
@ -556,7 +532,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7450 2.3 and newer */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -567,7 +545,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7455 rev 1.x */
|
||||
.pvr_mask = 0xffffff00,
|
||||
@ -578,7 +558,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7455 rev 2.0 */
|
||||
.pvr_mask = 0xffffffff,
|
||||
@ -589,7 +571,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7455 others */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -600,7 +584,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7447/7457 Rev 1.0 */
|
||||
.pvr_mask = 0xffffffff,
|
||||
@ -611,7 +597,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7447/7457 Rev 1.1 */
|
||||
.pvr_mask = 0xffffffff,
|
||||
@ -622,7 +610,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7447/7457 Rev 1.2 and later */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -633,7 +623,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7447A */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -644,7 +636,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 7448 */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -655,7 +649,9 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 6,
|
||||
.cpu_setup = __setup_cpu_745x
|
||||
.cpu_setup = __setup_cpu_745x,
|
||||
.oprofile_cpu_type = "ppc/7450",
|
||||
.oprofile_type = G4,
|
||||
},
|
||||
{ /* 82xx (8240, 8245, 8260 are all 603e cores) */
|
||||
.pvr_mask = 0x7fff0000,
|
||||
@ -979,6 +975,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 4,
|
||||
.oprofile_cpu_type = "ppc/e500",
|
||||
.oprofile_type = BOOKE,
|
||||
},
|
||||
{ /* e500v2 */
|
||||
.pvr_mask = 0xffff0000,
|
||||
@ -992,6 +990,8 @@ struct cpu_spec cpu_specs[] = {
|
||||
.icache_bsize = 32,
|
||||
.dcache_bsize = 32,
|
||||
.num_pmcs = 4,
|
||||
.oprofile_cpu_type = "ppc/e500",
|
||||
.oprofile_type = BOOKE,
|
||||
},
|
||||
#endif
|
||||
#if !CLASSIC_PPC
|
||||
|
264
arch/powerpc/kernel/crash.c
Normal file
264
arch/powerpc/kernel/crash.c
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Architecture specific (PPC64) functions for kexec based crash dumps.
|
||||
*
|
||||
* Copyright (C) 2005, IBM Corp.
|
||||
*
|
||||
* Created by: Haren Myneni
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/elfcore.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/kdump.h>
|
||||
#include <asm/lmb.h>
|
||||
#include <asm/firmware.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <asm/udbg.h>
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
/* This keeps a track of which one is crashing cpu. */
|
||||
int crashing_cpu = -1;
|
||||
|
||||
static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data,
|
||||
size_t data_len)
|
||||
{
|
||||
struct elf_note note;
|
||||
|
||||
note.n_namesz = strlen(name) + 1;
|
||||
note.n_descsz = data_len;
|
||||
note.n_type = type;
|
||||
memcpy(buf, ¬e, sizeof(note));
|
||||
buf += (sizeof(note) +3)/4;
|
||||
memcpy(buf, name, note.n_namesz);
|
||||
buf += (note.n_namesz + 3)/4;
|
||||
memcpy(buf, data, note.n_descsz);
|
||||
buf += (note.n_descsz + 3)/4;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void final_note(u32 *buf)
|
||||
{
|
||||
struct elf_note note;
|
||||
|
||||
note.n_namesz = 0;
|
||||
note.n_descsz = 0;
|
||||
note.n_type = 0;
|
||||
memcpy(buf, ¬e, sizeof(note));
|
||||
}
|
||||
|
||||
static void crash_save_this_cpu(struct pt_regs *regs, int cpu)
|
||||
{
|
||||
struct elf_prstatus prstatus;
|
||||
u32 *buf;
|
||||
|
||||
if ((cpu < 0) || (cpu >= NR_CPUS))
|
||||
return;
|
||||
|
||||
/* Using ELF notes here is opportunistic.
|
||||
* I need a well defined structure format
|
||||
* for the data I pass, and I need tags
|
||||
* on the data to indicate what information I have
|
||||
* squirrelled away. ELF notes happen to provide
|
||||
* all of that that no need to invent something new.
|
||||
*/
|
||||
buf = &crash_notes[cpu][0];
|
||||
memset(&prstatus, 0, sizeof(prstatus));
|
||||
prstatus.pr_pid = current->pid;
|
||||
elf_core_copy_regs(&prstatus.pr_reg, regs);
|
||||
buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus,
|
||||
sizeof(prstatus));
|
||||
final_note(buf);
|
||||
}
|
||||
|
||||
/* FIXME Merge this with xmon_save_regs ?? */
|
||||
static inline void crash_get_current_regs(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"std 0,0(%2)\n"
|
||||
"std 1,8(%2)\n"
|
||||
"std 2,16(%2)\n"
|
||||
"std 3,24(%2)\n"
|
||||
"std 4,32(%2)\n"
|
||||
"std 5,40(%2)\n"
|
||||
"std 6,48(%2)\n"
|
||||
"std 7,56(%2)\n"
|
||||
"std 8,64(%2)\n"
|
||||
"std 9,72(%2)\n"
|
||||
"std 10,80(%2)\n"
|
||||
"std 11,88(%2)\n"
|
||||
"std 12,96(%2)\n"
|
||||
"std 13,104(%2)\n"
|
||||
"std 14,112(%2)\n"
|
||||
"std 15,120(%2)\n"
|
||||
"std 16,128(%2)\n"
|
||||
"std 17,136(%2)\n"
|
||||
"std 18,144(%2)\n"
|
||||
"std 19,152(%2)\n"
|
||||
"std 20,160(%2)\n"
|
||||
"std 21,168(%2)\n"
|
||||
"std 22,176(%2)\n"
|
||||
"std 23,184(%2)\n"
|
||||
"std 24,192(%2)\n"
|
||||
"std 25,200(%2)\n"
|
||||
"std 26,208(%2)\n"
|
||||
"std 27,216(%2)\n"
|
||||
"std 28,224(%2)\n"
|
||||
"std 29,232(%2)\n"
|
||||
"std 30,240(%2)\n"
|
||||
"std 31,248(%2)\n"
|
||||
"mfmsr %0\n"
|
||||
"std %0, 264(%2)\n"
|
||||
"mfctr %0\n"
|
||||
"std %0, 280(%2)\n"
|
||||
"mflr %0\n"
|
||||
"std %0, 288(%2)\n"
|
||||
"bl 1f\n"
|
||||
"1: mflr %1\n"
|
||||
"std %1, 256(%2)\n"
|
||||
"mtlr %0\n"
|
||||
"mfxer %0\n"
|
||||
"std %0, 296(%2)\n"
|
||||
: "=&r" (tmp1), "=&r" (tmp2)
|
||||
: "b" (regs));
|
||||
}
|
||||
|
||||
/* We may have saved_regs from where the error came from
|
||||
* or it is NULL if via a direct panic().
|
||||
*/
|
||||
static void crash_save_self(struct pt_regs *saved_regs)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int cpu;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
if (saved_regs)
|
||||
memcpy(®s, saved_regs, sizeof(regs));
|
||||
else
|
||||
crash_get_current_regs(®s);
|
||||
crash_save_this_cpu(®s, cpu);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static atomic_t waiting_for_crash_ipi;
|
||||
|
||||
void crash_ipi_callback(struct pt_regs *regs)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
if (cpu == crashing_cpu)
|
||||
return;
|
||||
|
||||
if (!cpu_online(cpu))
|
||||
return;
|
||||
|
||||
if (ppc_md.kexec_cpu_down)
|
||||
ppc_md.kexec_cpu_down(1, 1);
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
crash_save_this_cpu(regs, cpu);
|
||||
atomic_dec(&waiting_for_crash_ipi);
|
||||
kexec_smp_wait();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static void crash_kexec_prepare_cpus(void)
|
||||
{
|
||||
unsigned int msecs;
|
||||
|
||||
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
||||
|
||||
crash_send_ipi(crash_ipi_callback);
|
||||
smp_wmb();
|
||||
|
||||
/*
|
||||
* FIXME: Until we will have the way to stop other CPUSs reliabally,
|
||||
* the crash CPU will send an IPI and wait for other CPUs to
|
||||
* respond. If not, proceed the kexec boot even though we failed to
|
||||
* capture other CPU states.
|
||||
*/
|
||||
msecs = 1000000;
|
||||
while ((atomic_read(&waiting_for_crash_ipi) > 0) && (--msecs > 0)) {
|
||||
barrier();
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
/* Would it be better to replace the trap vector here? */
|
||||
|
||||
/*
|
||||
* FIXME: In case if we do not get all CPUs, one possibility: ask the
|
||||
* user to do soft reset such that we get all.
|
||||
* IPI handler is already set by the panic cpu initially. Therefore,
|
||||
* all cpus could invoke this handler from die() and the panic CPU
|
||||
* will call machine_kexec() directly from this handler to do
|
||||
* kexec boot.
|
||||
*/
|
||||
if (atomic_read(&waiting_for_crash_ipi))
|
||||
printk(KERN_ALERT "done waiting: %d cpus not responding\n",
|
||||
atomic_read(&waiting_for_crash_ipi));
|
||||
/* Leave the IPI callback set */
|
||||
}
|
||||
#else
|
||||
static void crash_kexec_prepare_cpus(void)
|
||||
{
|
||||
/*
|
||||
* move the secondarys to us so that we can copy
|
||||
* the new kernel 0-0x100 safely
|
||||
*
|
||||
* do this if kexec in setup.c ?
|
||||
*/
|
||||
smp_release_cpus();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void default_machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* This function is only called after the system
|
||||
* has paniced or is otherwise in a critical state.
|
||||
* The minimum amount of code to allow a kexec'd kernel
|
||||
* to run successfully needs to happen here.
|
||||
*
|
||||
* In practice this means stopping other cpus in
|
||||
* an SMP system.
|
||||
* The kernel is broken so disable interrupts.
|
||||
*/
|
||||
local_irq_disable();
|
||||
|
||||
if (ppc_md.kexec_cpu_down)
|
||||
ppc_md.kexec_cpu_down(1, 0);
|
||||
|
||||
/*
|
||||
* Make a note of crashing cpu. Will be used in machine_kexec
|
||||
* such that another IPI will not be sent.
|
||||
*/
|
||||
crashing_cpu = smp_processor_id();
|
||||
crash_kexec_prepare_cpus();
|
||||
crash_save_self(regs);
|
||||
}
|
109
arch/powerpc/kernel/crash_dump.c
Normal file
109
arch/powerpc/kernel/crash_dump.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Routines for doing kexec-based kdump.
|
||||
*
|
||||
* Copyright (C) 2005, IBM Corp.
|
||||
*
|
||||
* Created by: Michael Ellerman
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <asm/kdump.h>
|
||||
#include <asm/lmb.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <asm/udbg.h>
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
static void __init create_trampoline(unsigned long addr)
|
||||
{
|
||||
/* The maximum range of a single instruction branch, is the current
|
||||
* instruction's address + (32 MB - 4) bytes. For the trampoline we
|
||||
* need to branch to current address + 32 MB. So we insert a nop at
|
||||
* the trampoline address, then the next instruction (+ 4 bytes)
|
||||
* does a branch to (32 MB - 4). The net effect is that when we
|
||||
* branch to "addr" we jump to ("addr" + 32 MB). Although it requires
|
||||
* two instructions it doesn't require any registers.
|
||||
*/
|
||||
create_instruction(addr, 0x60000000); /* nop */
|
||||
create_branch(addr + 4, addr + PHYSICAL_START, 0);
|
||||
}
|
||||
|
||||
void __init kdump_setup(void)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
DBG(" -> kdump_setup()\n");
|
||||
|
||||
for (i = KDUMP_TRAMPOLINE_START; i < KDUMP_TRAMPOLINE_END; i += 8) {
|
||||
create_trampoline(i);
|
||||
}
|
||||
|
||||
create_trampoline(__pa(system_reset_fwnmi) - PHYSICAL_START);
|
||||
create_trampoline(__pa(machine_check_fwnmi) - PHYSICAL_START);
|
||||
|
||||
DBG(" <- kdump_setup()\n");
|
||||
}
|
||||
|
||||
static int __init parse_elfcorehdr(char *p)
|
||||
{
|
||||
if (p)
|
||||
elfcorehdr_addr = memparse(p, &p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
__setup("elfcorehdr=", parse_elfcorehdr);
|
||||
|
||||
static int __init parse_savemaxmem(char *p)
|
||||
{
|
||||
if (p)
|
||||
saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
__setup("savemaxmem=", parse_savemaxmem);
|
||||
|
||||
/*
|
||||
* copy_oldmem_page - copy one page from "oldmem"
|
||||
* @pfn: page frame number to be copied
|
||||
* @buf: target memory address for the copy; this can be in kernel address
|
||||
* space or user address space (see @userbuf)
|
||||
* @csize: number of bytes to copy
|
||||
* @offset: offset in bytes into the page (based on pfn) to begin the copy
|
||||
* @userbuf: if set, @buf is in user address space, use copy_to_user(),
|
||||
* otherwise @buf is in kernel address space, use memcpy().
|
||||
*
|
||||
* Copy a page from "oldmem". For this page, there is no pte mapped
|
||||
* in the current kernel. We stitch up a pte, similar to kmap_atomic.
|
||||
*/
|
||||
ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
|
||||
size_t csize, unsigned long offset, int userbuf)
|
||||
{
|
||||
void *vaddr;
|
||||
|
||||
if (!csize)
|
||||
return 0;
|
||||
|
||||
vaddr = __ioremap(pfn << PAGE_SHIFT, PAGE_SIZE, 0);
|
||||
|
||||
if (userbuf) {
|
||||
if (copy_to_user((char __user *)buf, (vaddr + offset), csize)) {
|
||||
iounmap(vaddr);
|
||||
return -EFAULT;
|
||||
}
|
||||
} else
|
||||
memcpy(buf, (vaddr + offset), csize);
|
||||
|
||||
iounmap(vaddr);
|
||||
return csize;
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
/* Include the busses we support */
|
||||
#include <linux/pci.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/ibmebus.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <asm/bug.h>
|
||||
|
||||
@ -22,6 +23,10 @@ static struct dma_mapping_ops *get_dma_ops(struct device *dev)
|
||||
#ifdef CONFIG_IBMVIO
|
||||
if (dev->bus == &vio_bus_type)
|
||||
return &vio_dma_ops;
|
||||
#endif
|
||||
#ifdef CONFIG_IBMEBUS
|
||||
if (dev->bus == &ibmebus_bus_type)
|
||||
return &ibmebus_dma_ops;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
@ -47,6 +52,10 @@ int dma_set_mask(struct device *dev, u64 dma_mask)
|
||||
if (dev->bus == &vio_bus_type)
|
||||
return -EIO;
|
||||
#endif /* CONFIG_IBMVIO */
|
||||
#ifdef CONFIG_IBMEBUS
|
||||
if (dev->bus == &ibmebus_bus_type)
|
||||
return -EIO;
|
||||
#endif
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
@ -200,8 +200,6 @@ _GLOBAL(DoSyscall)
|
||||
bl do_show_syscall
|
||||
#endif /* SHOW_SYSCALLS */
|
||||
rlwinm r10,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
|
||||
li r11,0
|
||||
stb r11,TI_SC_NOERR(r10)
|
||||
lwz r11,TI_FLAGS(r10)
|
||||
andi. r11,r11,_TIF_SYSCALL_T_OR_A
|
||||
bne- syscall_dotrace
|
||||
@ -222,25 +220,21 @@ ret_from_syscall:
|
||||
bl do_show_syscall_exit
|
||||
#endif
|
||||
mr r6,r3
|
||||
li r11,-_LAST_ERRNO
|
||||
cmplw 0,r3,r11
|
||||
rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
|
||||
blt+ 30f
|
||||
lbz r11,TI_SC_NOERR(r12)
|
||||
cmpwi r11,0
|
||||
bne 30f
|
||||
neg r3,r3
|
||||
lwz r10,_CCR(r1) /* Set SO bit in CR */
|
||||
oris r10,r10,0x1000
|
||||
stw r10,_CCR(r1)
|
||||
|
||||
/* disable interrupts so current_thread_info()->flags can't change */
|
||||
30: LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
|
||||
LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
|
||||
SYNC
|
||||
MTMSRD(r10)
|
||||
lwz r9,TI_FLAGS(r12)
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
|
||||
li r8,-_LAST_ERRNO
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL)
|
||||
bne- syscall_exit_work
|
||||
cmplw 0,r3,r8
|
||||
blt+ syscall_exit_cont
|
||||
lwz r11,_CCR(r1) /* Load CR */
|
||||
neg r3,r3
|
||||
oris r11,r11,0x1000 /* Set SO bit in CR */
|
||||
stw r11,_CCR(r1)
|
||||
syscall_exit_cont:
|
||||
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
|
||||
/* If the process has its own DBCR0 value, load it up. The single
|
||||
@ -292,46 +286,113 @@ syscall_dotrace:
|
||||
b syscall_dotrace_cont
|
||||
|
||||
syscall_exit_work:
|
||||
stw r6,RESULT(r1) /* Save result */
|
||||
andi. r0,r9,_TIF_RESTOREALL
|
||||
bne- 2f
|
||||
cmplw 0,r3,r8
|
||||
blt+ 1f
|
||||
andi. r0,r9,_TIF_NOERROR
|
||||
bne- 1f
|
||||
lwz r11,_CCR(r1) /* Load CR */
|
||||
neg r3,r3
|
||||
oris r11,r11,0x1000 /* Set SO bit in CR */
|
||||
stw r11,_CCR(r1)
|
||||
|
||||
1: stw r6,RESULT(r1) /* Save result */
|
||||
stw r3,GPR3(r1) /* Update return value */
|
||||
andi. r0,r9,_TIF_SYSCALL_T_OR_A
|
||||
beq 5f
|
||||
ori r10,r10,MSR_EE
|
||||
SYNC
|
||||
MTMSRD(r10) /* re-enable interrupts */
|
||||
2: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
|
||||
beq 4f
|
||||
|
||||
/* Clear per-syscall TIF flags if any are set, but _leave_
|
||||
_TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
|
||||
yet. */
|
||||
|
||||
li r11,_TIF_PERSYSCALL_MASK
|
||||
addi r12,r12,TI_FLAGS
|
||||
3: lwarx r8,0,r12
|
||||
andc r8,r8,r11
|
||||
#ifdef CONFIG_IBM405_ERR77
|
||||
dcbt 0,r12
|
||||
#endif
|
||||
stwcx. r8,0,r12
|
||||
bne- 3b
|
||||
subi r12,r12,TI_FLAGS
|
||||
|
||||
4: /* Anything which requires enabling interrupts? */
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
|
||||
beq 7f
|
||||
|
||||
/* Save NVGPRS if they're not saved already */
|
||||
lwz r4,_TRAP(r1)
|
||||
andi. r4,r4,1
|
||||
beq 4f
|
||||
beq 5f
|
||||
SAVE_NVGPRS(r1)
|
||||
li r4,0xc00
|
||||
stw r4,_TRAP(r1)
|
||||
4:
|
||||
|
||||
/* Re-enable interrupts */
|
||||
5: ori r10,r10,MSR_EE
|
||||
SYNC
|
||||
MTMSRD(r10)
|
||||
|
||||
andi. r0,r9,_TIF_SAVE_NVGPRS
|
||||
bne save_user_nvgprs
|
||||
|
||||
save_user_nvgprs_cont:
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
|
||||
beq 7f
|
||||
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
bl do_syscall_trace_leave
|
||||
REST_NVGPRS(r1)
|
||||
2:
|
||||
lwz r3,GPR3(r1)
|
||||
|
||||
6: lwz r3,GPR3(r1)
|
||||
LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
|
||||
SYNC
|
||||
MTMSRD(r10) /* disable interrupts again */
|
||||
rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
|
||||
lwz r9,TI_FLAGS(r12)
|
||||
5:
|
||||
7:
|
||||
andi. r0,r9,_TIF_NEED_RESCHED
|
||||
bne 1f
|
||||
bne 8f
|
||||
lwz r5,_MSR(r1)
|
||||
andi. r5,r5,MSR_PR
|
||||
beq syscall_exit_cont
|
||||
beq ret_from_except
|
||||
andi. r0,r9,_TIF_SIGPENDING
|
||||
beq syscall_exit_cont
|
||||
beq ret_from_except
|
||||
b do_user_signal
|
||||
1:
|
||||
8:
|
||||
ori r10,r10,MSR_EE
|
||||
SYNC
|
||||
MTMSRD(r10) /* re-enable interrupts */
|
||||
bl schedule
|
||||
b 2b
|
||||
b 6b
|
||||
|
||||
save_user_nvgprs:
|
||||
lwz r8,TI_SIGFRAME(r12)
|
||||
|
||||
.macro savewords start, end
|
||||
1: stw \start,4*(\start)(r8)
|
||||
.section __ex_table,"a"
|
||||
.align 2
|
||||
.long 1b,save_user_nvgprs_fault
|
||||
.previous
|
||||
.if \end - \start
|
||||
savewords "(\start+1)",\end
|
||||
.endif
|
||||
.endm
|
||||
savewords 14,31
|
||||
b save_user_nvgprs_cont
|
||||
|
||||
|
||||
save_user_nvgprs_fault:
|
||||
li r3,11 /* SIGSEGV */
|
||||
lwz r4,TI_TASK(r12)
|
||||
bl force_sigsegv
|
||||
|
||||
rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
|
||||
lwz r9,TI_FLAGS(r12)
|
||||
b save_user_nvgprs_cont
|
||||
|
||||
#ifdef SHOW_SYSCALLS
|
||||
do_show_syscall:
|
||||
#ifdef SHOW_SYSCALLS_TASK
|
||||
@ -401,28 +462,10 @@ show_syscalls_task:
|
||||
#endif /* SHOW_SYSCALLS */
|
||||
|
||||
/*
|
||||
* The sigsuspend and rt_sigsuspend system calls can call do_signal
|
||||
* and thus put the process into the stopped state where we might
|
||||
* want to examine its user state with ptrace. Therefore we need
|
||||
* to save all the nonvolatile registers (r13 - r31) before calling
|
||||
* the C code.
|
||||
* The fork/clone functions need to copy the full register set into
|
||||
* the child process. Therefore we need to save all the nonvolatile
|
||||
* registers (r13 - r31) before calling the C code.
|
||||
*/
|
||||
.globl ppc_sigsuspend
|
||||
ppc_sigsuspend:
|
||||
SAVE_NVGPRS(r1)
|
||||
lwz r0,_TRAP(r1)
|
||||
rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */
|
||||
stw r0,_TRAP(r1) /* register set saved */
|
||||
b sys_sigsuspend
|
||||
|
||||
.globl ppc_rt_sigsuspend
|
||||
ppc_rt_sigsuspend:
|
||||
SAVE_NVGPRS(r1)
|
||||
lwz r0,_TRAP(r1)
|
||||
rlwinm r0,r0,0,0,30
|
||||
stw r0,_TRAP(r1)
|
||||
b sys_rt_sigsuspend
|
||||
|
||||
.globl ppc_fork
|
||||
ppc_fork:
|
||||
SAVE_NVGPRS(r1)
|
||||
@ -447,14 +490,6 @@ ppc_clone:
|
||||
stw r0,_TRAP(r1) /* register set saved */
|
||||
b sys_clone
|
||||
|
||||
.globl ppc_swapcontext
|
||||
ppc_swapcontext:
|
||||
SAVE_NVGPRS(r1)
|
||||
lwz r0,_TRAP(r1)
|
||||
rlwinm r0,r0,0,0,30 /* clear LSB to indicate full */
|
||||
stw r0,_TRAP(r1) /* register set saved */
|
||||
b sys_swapcontext
|
||||
|
||||
/*
|
||||
* Top-level page fault handling.
|
||||
* This is in assembler because if do_page_fault tells us that
|
||||
@ -626,16 +661,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_601)
|
||||
.long ret_from_except
|
||||
#endif
|
||||
|
||||
.globl sigreturn_exit
|
||||
sigreturn_exit:
|
||||
subi r1,r3,STACK_FRAME_OVERHEAD
|
||||
rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
|
||||
lwz r9,TI_FLAGS(r12)
|
||||
andi. r0,r9,_TIF_SYSCALL_T_OR_A
|
||||
beq+ ret_from_except_full
|
||||
bl do_syscall_trace_leave
|
||||
/* fall through */
|
||||
|
||||
.globl ret_from_except_full
|
||||
ret_from_except_full:
|
||||
REST_NVGPRS(r1)
|
||||
@ -658,7 +683,7 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
|
||||
/* Check current_thread_info()->flags */
|
||||
rlwinm r9,r1,0,0,(31-THREAD_SHIFT)
|
||||
lwz r9,TI_FLAGS(r9)
|
||||
andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED)
|
||||
andi. r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL)
|
||||
bne do_work
|
||||
|
||||
restore_user:
|
||||
|
@ -113,9 +113,7 @@ system_call_common:
|
||||
addi r9,r1,STACK_FRAME_OVERHEAD
|
||||
#endif
|
||||
clrrdi r11,r1,THREAD_SHIFT
|
||||
li r12,0
|
||||
ld r10,TI_FLAGS(r11)
|
||||
stb r12,TI_SC_NOERR(r11)
|
||||
andi. r11,r10,_TIF_SYSCALL_T_OR_A
|
||||
bne- syscall_dotrace
|
||||
syscall_dotrace_cont:
|
||||
@ -144,24 +142,12 @@ system_call: /* label this so stack traces look sane */
|
||||
bctrl /* Call handler */
|
||||
|
||||
syscall_exit:
|
||||
#ifdef SHOW_SYSCALLS
|
||||
std r3,GPR3(r1)
|
||||
bl .do_show_syscall_exit
|
||||
ld r3,GPR3(r1)
|
||||
#endif
|
||||
std r3,RESULT(r1)
|
||||
ld r5,_CCR(r1)
|
||||
li r10,-_LAST_ERRNO
|
||||
cmpld r3,r10
|
||||
#ifdef SHOW_SYSCALLS
|
||||
bl .do_show_syscall_exit
|
||||
ld r3,RESULT(r1)
|
||||
#endif
|
||||
clrrdi r12,r1,THREAD_SHIFT
|
||||
bge- syscall_error
|
||||
syscall_error_cont:
|
||||
|
||||
/* check for syscall tracing or audit */
|
||||
ld r9,TI_FLAGS(r12)
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
|
||||
bne- syscall_exit_trace
|
||||
syscall_exit_trace_cont:
|
||||
|
||||
/* disable interrupts so current_thread_info()->flags can't change,
|
||||
and so that we don't get interrupted after loading SRR0/1. */
|
||||
@ -173,8 +159,13 @@ syscall_exit_trace_cont:
|
||||
rotldi r10,r10,16
|
||||
mtmsrd r10,1
|
||||
ld r9,TI_FLAGS(r12)
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
|
||||
li r11,-_LAST_ERRNO
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR)
|
||||
bne- syscall_exit_work
|
||||
cmpld r3,r11
|
||||
ld r5,_CCR(r1)
|
||||
bge- syscall_error
|
||||
syscall_error_cont:
|
||||
ld r7,_NIP(r1)
|
||||
stdcx. r0,0,r1 /* to clear the reservation */
|
||||
andi. r6,r8,MSR_PR
|
||||
@ -193,21 +184,12 @@ syscall_exit_trace_cont:
|
||||
rfid
|
||||
b . /* prevent speculative execution */
|
||||
|
||||
syscall_enosys:
|
||||
li r3,-ENOSYS
|
||||
std r3,RESULT(r1)
|
||||
clrrdi r12,r1,THREAD_SHIFT
|
||||
ld r5,_CCR(r1)
|
||||
|
||||
syscall_error:
|
||||
lbz r11,TI_SC_NOERR(r12)
|
||||
cmpwi 0,r11,0
|
||||
bne- syscall_error_cont
|
||||
neg r3,r3
|
||||
syscall_error:
|
||||
oris r5,r5,0x1000 /* Set SO bit in CR */
|
||||
neg r3,r3
|
||||
std r5,_CCR(r1)
|
||||
b syscall_error_cont
|
||||
|
||||
|
||||
/* Traced system call support */
|
||||
syscall_dotrace:
|
||||
bl .save_nvgprs
|
||||
@ -225,21 +207,69 @@ syscall_dotrace:
|
||||
ld r10,TI_FLAGS(r10)
|
||||
b syscall_dotrace_cont
|
||||
|
||||
syscall_exit_trace:
|
||||
std r3,GPR3(r1)
|
||||
bl .save_nvgprs
|
||||
syscall_enosys:
|
||||
li r3,-ENOSYS
|
||||
b syscall_exit
|
||||
|
||||
syscall_exit_work:
|
||||
/* If TIF_RESTOREALL is set, don't scribble on either r3 or ccr.
|
||||
If TIF_NOERROR is set, just save r3 as it is. */
|
||||
|
||||
andi. r0,r9,_TIF_RESTOREALL
|
||||
bne- 2f
|
||||
cmpld r3,r11 /* r10 is -LAST_ERRNO */
|
||||
blt+ 1f
|
||||
andi. r0,r9,_TIF_NOERROR
|
||||
bne- 1f
|
||||
ld r5,_CCR(r1)
|
||||
neg r3,r3
|
||||
oris r5,r5,0x1000 /* Set SO bit in CR */
|
||||
std r5,_CCR(r1)
|
||||
1: std r3,GPR3(r1)
|
||||
2: andi. r0,r9,(_TIF_PERSYSCALL_MASK)
|
||||
beq 4f
|
||||
|
||||
/* Clear per-syscall TIF flags if any are set, but _leave_
|
||||
_TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
|
||||
yet. */
|
||||
|
||||
li r11,_TIF_PERSYSCALL_MASK
|
||||
addi r12,r12,TI_FLAGS
|
||||
3: ldarx r10,0,r12
|
||||
andc r10,r10,r11
|
||||
stdcx. r10,0,r12
|
||||
bne- 3b
|
||||
subi r12,r12,TI_FLAGS
|
||||
|
||||
4: bl .save_nvgprs
|
||||
/* Anything else left to do? */
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
|
||||
beq .ret_from_except_lite
|
||||
|
||||
/* Re-enable interrupts */
|
||||
mfmsr r10
|
||||
ori r10,r10,MSR_EE
|
||||
mtmsrd r10,1
|
||||
|
||||
andi. r0,r9,_TIF_SAVE_NVGPRS
|
||||
bne save_user_nvgprs
|
||||
|
||||
/* If tracing, re-enable interrupts and do it */
|
||||
save_user_nvgprs_cont:
|
||||
andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
|
||||
beq 5f
|
||||
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
bl .do_syscall_trace_leave
|
||||
REST_NVGPRS(r1)
|
||||
ld r3,GPR3(r1)
|
||||
ld r5,_CCR(r1)
|
||||
clrrdi r12,r1,THREAD_SHIFT
|
||||
b syscall_exit_trace_cont
|
||||
|
||||
/* Stuff to do on exit from a system call. */
|
||||
syscall_exit_work:
|
||||
std r3,GPR3(r1)
|
||||
std r5,_CCR(r1)
|
||||
/* Disable interrupts again and handle other work if any */
|
||||
5: mfmsr r10
|
||||
rldicl r10,r10,48,1
|
||||
rotldi r10,r10,16
|
||||
mtmsrd r10,1
|
||||
|
||||
b .ret_from_except_lite
|
||||
|
||||
/* Save non-volatile GPRs, if not already saved. */
|
||||
@ -252,6 +282,52 @@ _GLOBAL(save_nvgprs)
|
||||
std r0,_TRAP(r1)
|
||||
blr
|
||||
|
||||
|
||||
save_user_nvgprs:
|
||||
ld r10,TI_SIGFRAME(r12)
|
||||
andi. r0,r9,_TIF_32BIT
|
||||
beq- save_user_nvgprs_64
|
||||
|
||||
/* 32-bit save to userspace */
|
||||
|
||||
.macro savewords start, end
|
||||
1: stw \start,4*(\start)(r10)
|
||||
.section __ex_table,"a"
|
||||
.align 3
|
||||
.llong 1b,save_user_nvgprs_fault
|
||||
.previous
|
||||
.if \end - \start
|
||||
savewords "(\start+1)",\end
|
||||
.endif
|
||||
.endm
|
||||
savewords 14,31
|
||||
b save_user_nvgprs_cont
|
||||
|
||||
save_user_nvgprs_64:
|
||||
/* 64-bit save to userspace */
|
||||
|
||||
.macro savelongs start, end
|
||||
1: std \start,8*(\start)(r10)
|
||||
.section __ex_table,"a"
|
||||
.align 3
|
||||
.llong 1b,save_user_nvgprs_fault
|
||||
.previous
|
||||
.if \end - \start
|
||||
savelongs "(\start+1)",\end
|
||||
.endif
|
||||
.endm
|
||||
savelongs 14,31
|
||||
b save_user_nvgprs_cont
|
||||
|
||||
save_user_nvgprs_fault:
|
||||
li r3,11 /* SIGSEGV */
|
||||
ld r4,TI_TASK(r12)
|
||||
bl .force_sigsegv
|
||||
|
||||
clrrdi r12,r1,THREAD_SHIFT
|
||||
ld r9,TI_FLAGS(r12)
|
||||
b save_user_nvgprs_cont
|
||||
|
||||
/*
|
||||
* The sigsuspend and rt_sigsuspend system calls can call do_signal
|
||||
* and thus put the process into the stopped state where we might
|
||||
@ -260,35 +336,6 @@ _GLOBAL(save_nvgprs)
|
||||
* the C code. Similarly, fork, vfork and clone need the full
|
||||
* register state on the stack so that it can be copied to the child.
|
||||
*/
|
||||
_GLOBAL(ppc32_sigsuspend)
|
||||
bl .save_nvgprs
|
||||
bl .compat_sys_sigsuspend
|
||||
b 70f
|
||||
|
||||
_GLOBAL(ppc64_rt_sigsuspend)
|
||||
bl .save_nvgprs
|
||||
bl .sys_rt_sigsuspend
|
||||
b 70f
|
||||
|
||||
_GLOBAL(ppc32_rt_sigsuspend)
|
||||
bl .save_nvgprs
|
||||
bl .compat_sys_rt_sigsuspend
|
||||
70: cmpdi 0,r3,0
|
||||
/* If it returned an error, we need to return via syscall_exit to set
|
||||
the SO bit in cr0 and potentially stop for ptrace. */
|
||||
bne syscall_exit
|
||||
/* If sigsuspend() returns zero, we are going into a signal handler. We
|
||||
may need to call audit_syscall_exit() to mark the exit from sigsuspend() */
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
ld r3,PACACURRENT(r13)
|
||||
ld r4,AUDITCONTEXT(r3)
|
||||
cmpdi 0,r4,0
|
||||
beq .ret_from_except /* No audit_context: Leave immediately. */
|
||||
li r4, 2 /* AUDITSC_FAILURE */
|
||||
li r5,-4 /* It's always -EINTR */
|
||||
bl .audit_syscall_exit
|
||||
#endif
|
||||
b .ret_from_except
|
||||
|
||||
_GLOBAL(ppc_fork)
|
||||
bl .save_nvgprs
|
||||
@ -305,37 +352,6 @@ _GLOBAL(ppc_clone)
|
||||
bl .sys_clone
|
||||
b syscall_exit
|
||||
|
||||
_GLOBAL(ppc32_swapcontext)
|
||||
bl .save_nvgprs
|
||||
bl .compat_sys_swapcontext
|
||||
b 80f
|
||||
|
||||
_GLOBAL(ppc64_swapcontext)
|
||||
bl .save_nvgprs
|
||||
bl .sys_swapcontext
|
||||
b 80f
|
||||
|
||||
_GLOBAL(ppc32_sigreturn)
|
||||
bl .compat_sys_sigreturn
|
||||
b 80f
|
||||
|
||||
_GLOBAL(ppc32_rt_sigreturn)
|
||||
bl .compat_sys_rt_sigreturn
|
||||
b 80f
|
||||
|
||||
_GLOBAL(ppc64_rt_sigreturn)
|
||||
bl .sys_rt_sigreturn
|
||||
|
||||
80: cmpdi 0,r3,0
|
||||
blt syscall_exit
|
||||
clrrdi r4,r1,THREAD_SHIFT
|
||||
ld r4,TI_FLAGS(r4)
|
||||
andi. r4,r4,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
|
||||
beq+ 81f
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
bl .do_syscall_trace_leave
|
||||
81: b .ret_from_except
|
||||
|
||||
_GLOBAL(ret_from_fork)
|
||||
bl .schedule_tail
|
||||
REST_NVGPRS(r1)
|
||||
@ -674,7 +690,7 @@ _GLOBAL(enter_rtas)
|
||||
|
||||
/* Setup our real return addr */
|
||||
SET_REG_TO_LABEL(r4,.rtas_return_loc)
|
||||
SET_REG_TO_CONST(r9,KERNELBASE)
|
||||
SET_REG_TO_CONST(r9,PAGE_OFFSET)
|
||||
sub r4,r4,r9
|
||||
mtlr r4
|
||||
|
||||
@ -702,7 +718,7 @@ _GLOBAL(enter_rtas)
|
||||
_STATIC(rtas_return_loc)
|
||||
/* relocation is off at this point */
|
||||
mfspr r4,SPRN_SPRG3 /* Get PACA */
|
||||
SET_REG_TO_CONST(r5, KERNELBASE)
|
||||
SET_REG_TO_CONST(r5, PAGE_OFFSET)
|
||||
sub r4,r4,r5 /* RELOC the PACA base pointer */
|
||||
|
||||
mfmsr r6
|
||||
|
@ -120,10 +120,25 @@ __start:
|
||||
* because OF may have I/O devices mapped into that area
|
||||
* (particularly on CHRP).
|
||||
*/
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
cmpwi 0,r5,0
|
||||
beq 1f
|
||||
bl prom_init
|
||||
trap
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check for BootX signature when supporting PowerMac and branch to
|
||||
* appropriate trampoline if it's present
|
||||
*/
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
1: lis r31,0x426f
|
||||
ori r31,r31,0x6f58
|
||||
cmpw 0,r3,r31
|
||||
bne 1f
|
||||
bl bootx_init
|
||||
trap
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
1: mr r31,r3 /* save parameters */
|
||||
mr r30,r4
|
||||
@ -153,6 +168,9 @@ __after_mmu_off:
|
||||
bl flush_tlbs
|
||||
|
||||
bl initial_bats
|
||||
#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT)
|
||||
bl setup_disp_bat
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Call setup_cpu for CPU 0 and initialize 6xx Idle
|
||||
@ -450,16 +468,11 @@ SystemCall:
|
||||
* by executing an altivec instruction.
|
||||
*/
|
||||
. = 0xf00
|
||||
b Trap_0f
|
||||
b PerformanceMonitor
|
||||
|
||||
. = 0xf20
|
||||
b AltiVecUnavailable
|
||||
|
||||
Trap_0f:
|
||||
EXCEPTION_PROLOG
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
EXC_XFER_EE(0xf00, unknown_exception)
|
||||
|
||||
/*
|
||||
* Handle TLB miss for instruction on 603/603e.
|
||||
* Note: we get an alternate set of r0 - r3 to use automatically.
|
||||
@ -703,6 +716,11 @@ AltiVecUnavailable:
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
EXC_XFER_EE_LITE(0xf20, altivec_unavailable_exception)
|
||||
|
||||
PerformanceMonitor:
|
||||
EXCEPTION_PROLOG
|
||||
addi r3,r1,STACK_FRAME_OVERHEAD
|
||||
EXC_XFER_STD(0xf00, performance_monitor_exception)
|
||||
|
||||
#ifdef CONFIG_ALTIVEC
|
||||
/* Note that the AltiVec support is closely modeled after the FP
|
||||
* support. Changes to one are likely to be applicable to the
|
||||
@ -1306,6 +1324,32 @@ initial_bats:
|
||||
blr
|
||||
|
||||
|
||||
#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT)
|
||||
setup_disp_bat:
|
||||
/*
|
||||
* setup the display bat prepared for us in prom.c
|
||||
*/
|
||||
mflr r8
|
||||
bl reloc_offset
|
||||
mtlr r8
|
||||
addis r8,r3,disp_BAT@ha
|
||||
addi r8,r8,disp_BAT@l
|
||||
cmpwi cr0,r8,0
|
||||
beqlr
|
||||
lwz r11,0(r8)
|
||||
lwz r8,4(r8)
|
||||
mfspr r9,SPRN_PVR
|
||||
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
|
||||
cmpwi 0,r9,1
|
||||
beq 1f
|
||||
mtspr SPRN_DBAT3L,r8
|
||||
mtspr SPRN_DBAT3U,r11
|
||||
blr
|
||||
1: mtspr SPRN_IBAT3L,r8
|
||||
mtspr SPRN_IBAT3U,r11
|
||||
blr
|
||||
#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */
|
||||
|
||||
#ifdef CONFIG_8260
|
||||
/* Jump into the system reset for the rom.
|
||||
* We first disable the MMU, and then jump to the ROM reset address.
|
||||
|
@ -154,11 +154,15 @@ _GLOBAL(__secondary_hold)
|
||||
bne 100b
|
||||
|
||||
#ifdef CONFIG_HMT
|
||||
b .hmt_init
|
||||
LOADADDR(r4, .hmt_init)
|
||||
mtctr r4
|
||||
bctr
|
||||
#else
|
||||
#ifdef CONFIG_SMP
|
||||
LOADADDR(r4, .pSeries_secondary_smp_init)
|
||||
mtctr r4
|
||||
mr r3,r24
|
||||
b .pSeries_secondary_smp_init
|
||||
bctr
|
||||
#else
|
||||
BUG_OPCODE
|
||||
#endif
|
||||
@ -200,6 +204,20 @@ exception_marker:
|
||||
#define EX_R3 64
|
||||
#define EX_LR 72
|
||||
|
||||
/*
|
||||
* We're short on space and time in the exception prolog, so we can't use
|
||||
* the normal LOADADDR macro. Normally we just need the low halfword of the
|
||||
* address, but for Kdump we need the whole low word.
|
||||
*/
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
#define LOAD_HANDLER(reg, label) \
|
||||
oris reg,reg,(label)@h; /* virt addr of handler ... */ \
|
||||
ori reg,reg,(label)@l; /* .. and the rest */
|
||||
#else
|
||||
#define LOAD_HANDLER(reg, label) \
|
||||
ori reg,reg,(label)@l; /* virt addr of handler ... */
|
||||
#endif
|
||||
|
||||
#define EXCEPTION_PROLOG_PSERIES(area, label) \
|
||||
mfspr r13,SPRN_SPRG3; /* get paca address into r13 */ \
|
||||
std r9,area+EX_R9(r13); /* save r9 - r12 */ \
|
||||
@ -212,7 +230,7 @@ exception_marker:
|
||||
clrrdi r12,r13,32; /* get high part of &label */ \
|
||||
mfmsr r10; \
|
||||
mfspr r11,SPRN_SRR0; /* save SRR0 */ \
|
||||
ori r12,r12,(label)@l; /* virt addr of handler */ \
|
||||
LOAD_HANDLER(r12,label) \
|
||||
ori r10,r10,MSR_IR|MSR_DR|MSR_RI; \
|
||||
mtspr SPRN_SRR0,r12; \
|
||||
mfspr r12,SPRN_SRR1; /* and SRR1 */ \
|
||||
@ -553,6 +571,7 @@ slb_miss_user_pseries:
|
||||
* Vectors for the FWNMI option. Share common code.
|
||||
*/
|
||||
.globl system_reset_fwnmi
|
||||
.align 7
|
||||
system_reset_fwnmi:
|
||||
HMT_MEDIUM
|
||||
mtspr SPRN_SPRG1,r13 /* save r13 */
|
||||
@ -560,6 +579,7 @@ system_reset_fwnmi:
|
||||
EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, system_reset_common)
|
||||
|
||||
.globl machine_check_fwnmi
|
||||
.align 7
|
||||
machine_check_fwnmi:
|
||||
HMT_MEDIUM
|
||||
mtspr SPRN_SPRG1,r13 /* save r13 */
|
||||
@ -726,7 +746,8 @@ iSeries_secondary_smp_loop:
|
||||
decrementer_iSeries_masked:
|
||||
li r11,1
|
||||
stb r11,PACALPPACA+LPPACADECRINT(r13)
|
||||
lwz r12,PACADEFAULTDECR(r13)
|
||||
LOADBASE(r12,tb_ticks_per_jiffy)
|
||||
lwz r12,OFF(tb_ticks_per_jiffy)(r12)
|
||||
mtspr SPRN_DEC,r12
|
||||
/* fall through */
|
||||
|
||||
@ -1345,7 +1366,7 @@ _GLOBAL(do_stab_bolted)
|
||||
* fixed address (the linker can't compute (u64)&initial_stab >>
|
||||
* PAGE_SHIFT).
|
||||
*/
|
||||
. = STAB0_PHYS_ADDR /* 0x6000 */
|
||||
. = STAB0_OFFSET /* 0x6000 */
|
||||
.globl initial_stab
|
||||
initial_stab:
|
||||
.space 4096
|
||||
@ -1485,11 +1506,13 @@ _STATIC(__mmu_off)
|
||||
*
|
||||
*/
|
||||
_GLOBAL(__start_initialization_multiplatform)
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
/*
|
||||
* Are we booted from a PROM Of-type client-interface ?
|
||||
*/
|
||||
cmpldi cr0,r5,0
|
||||
bne .__boot_from_prom /* yes -> prom */
|
||||
#endif
|
||||
|
||||
/* Save parameters */
|
||||
mr r31,r3
|
||||
@ -1510,6 +1533,7 @@ _GLOBAL(__start_initialization_multiplatform)
|
||||
bl .__mmu_off
|
||||
b .__after_prom_start
|
||||
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
_STATIC(__boot_from_prom)
|
||||
/* Save parameters */
|
||||
mr r31,r3
|
||||
@ -1542,6 +1566,7 @@ _STATIC(__boot_from_prom)
|
||||
bl .prom_init
|
||||
/* We never return */
|
||||
trap
|
||||
#endif
|
||||
|
||||
/*
|
||||
* At this point, r3 contains the physical address we are running at,
|
||||
@ -1550,7 +1575,7 @@ _STATIC(__boot_from_prom)
|
||||
_STATIC(__after_prom_start)
|
||||
|
||||
/*
|
||||
* We need to run with __start at physical address 0.
|
||||
* We need to run with __start at physical address PHYSICAL_START.
|
||||
* This will leave some code in the first 256B of
|
||||
* real memory, which are reserved for software use.
|
||||
* The remainder of the first page is loaded with the fixed
|
||||
@ -1565,7 +1590,7 @@ _STATIC(__after_prom_start)
|
||||
mr r26,r3
|
||||
SET_REG_TO_CONST(r27,KERNELBASE)
|
||||
|
||||
li r3,0 /* target addr */
|
||||
LOADADDR(r3, PHYSICAL_START) /* target addr */
|
||||
|
||||
// XXX FIXME: Use phys returned by OF (r30)
|
||||
add r4,r27,r26 /* source addr */
|
||||
@ -1846,7 +1871,7 @@ _STATIC(start_here_multiplatform)
|
||||
mulli r13,r27,PACA_SIZE /* Calculate vaddr of right paca */
|
||||
add r13,r13,r24 /* for this processor. */
|
||||
add r13,r13,r26 /* convert to physical addr */
|
||||
mtspr SPRN_SPRG3,r13 /* PPPBBB: Temp... -Peter */
|
||||
mtspr SPRN_SPRG3,r13
|
||||
|
||||
/* Do very early kernel initializations, including initial hash table,
|
||||
* stab and slb setup before we turn on relocation. */
|
||||
|
396
arch/powerpc/kernel/ibmebus.c
Normal file
396
arch/powerpc/kernel/ibmebus.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* IBM PowerPC IBM eBus Infrastructure Support.
|
||||
*
|
||||
* Copyright (c) 2005 IBM Corporation
|
||||
* Heiko J Schick <schickhj@de.ibm.com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is distributed under a dual license of GPL v2.0 and OpenIB
|
||||
* BSD.
|
||||
*
|
||||
* OpenIB BSD License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/ibmebus.h>
|
||||
#include <asm/abs_addr.h>
|
||||
|
||||
static struct ibmebus_dev ibmebus_bus_device = { /* fake "parent" device */
|
||||
.name = ibmebus_bus_device.ofdev.dev.bus_id,
|
||||
.ofdev.dev.bus_id = "ibmebus",
|
||||
.ofdev.dev.bus = &ibmebus_bus_type,
|
||||
};
|
||||
|
||||
static void *ibmebus_alloc_coherent(struct device *dev,
|
||||
size_t size,
|
||||
dma_addr_t *dma_handle,
|
||||
gfp_t flag)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
mem = kmalloc(size, flag);
|
||||
*dma_handle = (dma_addr_t)mem;
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void ibmebus_free_coherent(struct device *dev,
|
||||
size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle)
|
||||
{
|
||||
kfree(vaddr);
|
||||
}
|
||||
|
||||
static dma_addr_t ibmebus_map_single(struct device *dev,
|
||||
void *ptr,
|
||||
size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
return (dma_addr_t)(ptr);
|
||||
}
|
||||
|
||||
static void ibmebus_unmap_single(struct device *dev,
|
||||
dma_addr_t dma_addr,
|
||||
size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int ibmebus_map_sg(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nents; i++) {
|
||||
sg[i].dma_address = (dma_addr_t)page_address(sg[i].page)
|
||||
+ sg[i].offset;
|
||||
sg[i].dma_length = sg[i].length;
|
||||
}
|
||||
|
||||
return nents;
|
||||
}
|
||||
|
||||
static void ibmebus_unmap_sg(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int ibmebus_dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dma_mapping_ops ibmebus_dma_ops = {
|
||||
.alloc_coherent = ibmebus_alloc_coherent,
|
||||
.free_coherent = ibmebus_free_coherent,
|
||||
.map_single = ibmebus_map_single,
|
||||
.unmap_single = ibmebus_unmap_single,
|
||||
.map_sg = ibmebus_map_sg,
|
||||
.unmap_sg = ibmebus_unmap_sg,
|
||||
.dma_supported = ibmebus_dma_supported,
|
||||
};
|
||||
|
||||
static int ibmebus_bus_probe(struct device *dev)
|
||||
{
|
||||
struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev);
|
||||
struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
|
||||
const struct of_device_id *id;
|
||||
int error = -ENODEV;
|
||||
|
||||
if (!ibmebusdrv->probe)
|
||||
return error;
|
||||
|
||||
id = of_match_device(ibmebusdrv->id_table, &ibmebusdev->ofdev);
|
||||
if (id) {
|
||||
error = ibmebusdrv->probe(ibmebusdev, id);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ibmebus_bus_remove(struct device *dev)
|
||||
{
|
||||
struct ibmebus_dev *ibmebusdev = to_ibmebus_dev(dev);
|
||||
struct ibmebus_driver *ibmebusdrv = to_ibmebus_driver(dev->driver);
|
||||
|
||||
if (ibmebusdrv->remove) {
|
||||
return ibmebusdrv->remove(ibmebusdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit ibmebus_dev_release(struct device *dev)
|
||||
{
|
||||
of_node_put(to_ibmebus_dev(dev)->ofdev.node);
|
||||
kfree(to_ibmebus_dev(dev));
|
||||
}
|
||||
|
||||
static ssize_t ibmebusdev_show_name(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", to_ibmebus_dev(dev)->name);
|
||||
}
|
||||
static DEVICE_ATTR(name, S_IRUSR | S_IRGRP | S_IROTH, ibmebusdev_show_name,
|
||||
NULL);
|
||||
|
||||
static struct ibmebus_dev* __devinit ibmebus_register_device_common(
|
||||
struct ibmebus_dev *dev, char *name)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
dev->name = name;
|
||||
dev->ofdev.dev.parent = &ibmebus_bus_device.ofdev.dev;
|
||||
dev->ofdev.dev.bus = &ibmebus_bus_type;
|
||||
dev->ofdev.dev.release = ibmebus_dev_release;
|
||||
|
||||
/* An ibmebusdev is based on a of_device. We have to change the
|
||||
* bus type to use our own DMA mapping operations.
|
||||
*/
|
||||
if ((err = of_device_register(&dev->ofdev)) != 0) {
|
||||
printk(KERN_ERR "%s: failed to register device (%d).\n",
|
||||
__FUNCTION__, err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_create_file(&dev->ofdev.dev, &dev_attr_name);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct ibmebus_dev* __devinit ibmebus_register_device_node(
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct ibmebus_dev *dev;
|
||||
char *loc_code;
|
||||
int length;
|
||||
|
||||
loc_code = (char *)get_property(dn, "ibm,loc-code", NULL);
|
||||
if (!loc_code) {
|
||||
printk(KERN_WARNING "%s: node %s missing 'ibm,loc-code'\n",
|
||||
__FUNCTION__, dn->name ? dn->name : "<unknown>");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(loc_code) == 0) {
|
||||
printk(KERN_WARNING "%s: 'ibm,loc-code' is invalid\n",
|
||||
__FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = kmalloc(sizeof(struct ibmebus_dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
memset(dev, 0, sizeof(struct ibmebus_dev));
|
||||
|
||||
dev->ofdev.node = of_node_get(dn);
|
||||
|
||||
length = strlen(loc_code);
|
||||
memcpy(dev->ofdev.dev.bus_id, loc_code
|
||||
+ (length - min(length, BUS_ID_SIZE - 1)),
|
||||
min(length, BUS_ID_SIZE - 1));
|
||||
|
||||
/* Register with generic device framework. */
|
||||
if (ibmebus_register_device_common(dev, dn->name) == NULL) {
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void ibmebus_probe_of_nodes(char* name)
|
||||
{
|
||||
struct device_node *dn = NULL;
|
||||
|
||||
while ((dn = of_find_node_by_name(dn, name))) {
|
||||
if (ibmebus_register_device_node(dn) == NULL) {
|
||||
of_node_put(dn);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
of_node_put(dn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void ibmebus_add_devices_by_id(struct of_device_id *idt)
|
||||
{
|
||||
while (strlen(idt->name) > 0) {
|
||||
ibmebus_probe_of_nodes(idt->name);
|
||||
idt++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int ibmebus_match_helper(struct device *dev, void *data)
|
||||
{
|
||||
if (strcmp((char*)data, to_ibmebus_dev(dev)->name) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ibmebus_unregister_device(struct device *dev)
|
||||
{
|
||||
device_remove_file(dev, &dev_attr_name);
|
||||
of_device_unregister(to_of_device(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ibmebus_remove_devices_by_id(struct of_device_id *idt)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
while (strlen(idt->name) > 0) {
|
||||
while ((dev = bus_find_device(&ibmebus_bus_type, NULL,
|
||||
(void*)idt->name,
|
||||
ibmebus_match_helper))) {
|
||||
ibmebus_unregister_device(dev);
|
||||
}
|
||||
idt++;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ibmebus_register_driver(struct ibmebus_driver *drv)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
drv->driver.name = drv->name;
|
||||
drv->driver.bus = &ibmebus_bus_type;
|
||||
drv->driver.probe = ibmebus_bus_probe;
|
||||
drv->driver.remove = ibmebus_bus_remove;
|
||||
|
||||
if ((err = driver_register(&drv->driver) != 0))
|
||||
return err;
|
||||
|
||||
ibmebus_add_devices_by_id(drv->id_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ibmebus_register_driver);
|
||||
|
||||
void ibmebus_unregister_driver(struct ibmebus_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->driver);
|
||||
ibmebus_remove_devices_by_id(drv->id_table);
|
||||
}
|
||||
EXPORT_SYMBOL(ibmebus_unregister_driver);
|
||||
|
||||
int ibmebus_request_irq(struct ibmebus_dev *dev,
|
||||
u32 ist,
|
||||
irqreturn_t (*handler)(int, void*, struct pt_regs *),
|
||||
unsigned long irq_flags, const char * devname,
|
||||
void *dev_id)
|
||||
{
|
||||
unsigned int irq = virt_irq_create_mapping(ist);
|
||||
|
||||
if (irq == NO_IRQ)
|
||||
return -EINVAL;
|
||||
|
||||
irq = irq_offset_up(irq);
|
||||
|
||||
return request_irq(irq, handler,
|
||||
irq_flags, devname, dev_id);
|
||||
}
|
||||
EXPORT_SYMBOL(ibmebus_request_irq);
|
||||
|
||||
void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id)
|
||||
{
|
||||
unsigned int irq = virt_irq_create_mapping(ist);
|
||||
|
||||
irq = irq_offset_up(irq);
|
||||
free_irq(irq, dev_id);
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(ibmebus_free_irq);
|
||||
|
||||
static int ibmebus_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
const struct ibmebus_dev *ebus_dev = to_ibmebus_dev(dev);
|
||||
struct ibmebus_driver *ebus_drv = to_ibmebus_driver(drv);
|
||||
const struct of_device_id *ids = ebus_drv->id_table;
|
||||
const struct of_device_id *found_id;
|
||||
|
||||
if (!ids)
|
||||
return 0;
|
||||
|
||||
found_id = of_match_device(ids, &ebus_dev->ofdev);
|
||||
if (found_id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bus_type ibmebus_bus_type = {
|
||||
.name = "ibmebus",
|
||||
.match = ibmebus_bus_match,
|
||||
};
|
||||
EXPORT_SYMBOL(ibmebus_bus_type);
|
||||
|
||||
static int __init ibmebus_bus_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "IBM eBus Device Driver\n");
|
||||
|
||||
err = bus_register(&ibmebus_bus_type);
|
||||
if (err) {
|
||||
printk(KERN_ERR ":%s: failed to register IBM eBus.\n",
|
||||
__FUNCTION__);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = device_register(&ibmebus_bus_device.ofdev.dev);
|
||||
if (err) {
|
||||
printk(KERN_WARNING "%s: device_register returned %i\n",
|
||||
__FUNCTION__, err);
|
||||
bus_unregister(&ibmebus_bus_type);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
__initcall(ibmebus_bus_init);
|
@ -31,7 +31,6 @@
|
||||
* to reduce code space and undefined function references.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
@ -44,18 +43,12 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/bitops.h>
|
||||
#ifdef CONFIG_PPC64
|
||||
#include <linux/kallsyms.h>
|
||||
#endif
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
@ -66,8 +59,7 @@
|
||||
#include <asm/prom.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/machdep.h>
|
||||
#ifdef CONFIG_PPC64
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
#ifdef CONFIG_PPC_ISERIES
|
||||
#include <asm/paca.h>
|
||||
#endif
|
||||
|
||||
@ -78,10 +70,6 @@ EXPORT_SYMBOL(__irq_offset_value);
|
||||
|
||||
static int ppc_spurious_interrupts;
|
||||
|
||||
#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_SMP)
|
||||
extern void iSeries_smp_message_recv(struct pt_regs *);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
|
||||
|
||||
@ -195,49 +183,6 @@ void fixup_irqs(cpumask_t map)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC_ISERIES
|
||||
void do_IRQ(struct pt_regs *regs)
|
||||
{
|
||||
struct paca_struct *lpaca;
|
||||
|
||||
irq_enter();
|
||||
|
||||
#ifdef CONFIG_DEBUG_STACKOVERFLOW
|
||||
/* Debugging check for stack overflow: is there less than 2KB free? */
|
||||
{
|
||||
long sp;
|
||||
|
||||
sp = __get_SP() & (THREAD_SIZE-1);
|
||||
|
||||
if (unlikely(sp < (sizeof(struct thread_info) + 2048))) {
|
||||
printk("do_IRQ: stack overflow: %ld\n",
|
||||
sp - sizeof(struct thread_info));
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
lpaca = get_paca();
|
||||
#ifdef CONFIG_SMP
|
||||
if (lpaca->lppaca.int_dword.fields.ipi_cnt) {
|
||||
lpaca->lppaca.int_dword.fields.ipi_cnt = 0;
|
||||
iSeries_smp_message_recv(regs);
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
if (hvlpevent_is_pending())
|
||||
process_hvlpevents(regs);
|
||||
|
||||
irq_exit();
|
||||
|
||||
if (lpaca->lppaca.int_dword.fields.decr_int) {
|
||||
lpaca->lppaca.int_dword.fields.decr_int = 0;
|
||||
/* Signal a fake decrementer interrupt */
|
||||
timer_interrupt(regs);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_PPC_ISERIES */
|
||||
|
||||
void do_IRQ(struct pt_regs *regs)
|
||||
{
|
||||
int irq;
|
||||
@ -286,16 +231,24 @@ void do_IRQ(struct pt_regs *regs)
|
||||
} else
|
||||
#endif
|
||||
__do_IRQ(irq, regs);
|
||||
} else
|
||||
#ifdef CONFIG_PPC32
|
||||
if (irq != -2)
|
||||
#endif
|
||||
/* That's not SMP safe ... but who cares ? */
|
||||
ppc_spurious_interrupts++;
|
||||
irq_exit();
|
||||
}
|
||||
} else if (irq != -2)
|
||||
/* That's not SMP safe ... but who cares ? */
|
||||
ppc_spurious_interrupts++;
|
||||
|
||||
#endif /* CONFIG_PPC_ISERIES */
|
||||
irq_exit();
|
||||
|
||||
#ifdef CONFIG_PPC_ISERIES
|
||||
{
|
||||
struct paca_struct *lpaca = get_paca();
|
||||
|
||||
if (lpaca->lppaca.int_dword.fields.decr_int) {
|
||||
lpaca->lppaca.int_dword.fields.decr_int = 0;
|
||||
/* Signal a fake decrementer interrupt */
|
||||
timer_interrupt(regs);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
|
557
arch/powerpc/kernel/legacy_serial.c
Normal file
557
arch/powerpc/kernel/legacy_serial.c
Normal file
@ -0,0 +1,557 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/serial.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) do { printk(fmt); } while(0)
|
||||
#else
|
||||
#define DBG(fmt...) do { } while(0)
|
||||
#endif
|
||||
|
||||
#define MAX_LEGACY_SERIAL_PORTS 8
|
||||
|
||||
static struct plat_serial8250_port
|
||||
legacy_serial_ports[MAX_LEGACY_SERIAL_PORTS+1];
|
||||
static struct legacy_serial_info {
|
||||
struct device_node *np;
|
||||
unsigned int speed;
|
||||
unsigned int clock;
|
||||
phys_addr_t taddr;
|
||||
} legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS];
|
||||
static unsigned int legacy_serial_count;
|
||||
static int legacy_serial_console = -1;
|
||||
|
||||
static int __init add_legacy_port(struct device_node *np, int want_index,
|
||||
int iotype, phys_addr_t base,
|
||||
phys_addr_t taddr, unsigned long irq,
|
||||
unsigned int flags)
|
||||
{
|
||||
u32 *clk, *spd, clock = BASE_BAUD * 16;
|
||||
int index;
|
||||
|
||||
/* get clock freq. if present */
|
||||
clk = (u32 *)get_property(np, "clock-frequency", NULL);
|
||||
if (clk && *clk)
|
||||
clock = *clk;
|
||||
|
||||
/* get default speed if present */
|
||||
spd = (u32 *)get_property(np, "current-speed", NULL);
|
||||
|
||||
/* If we have a location index, then try to use it */
|
||||
if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS)
|
||||
index = want_index;
|
||||
else
|
||||
index = legacy_serial_count;
|
||||
|
||||
/* if our index is still out of range, that mean that
|
||||
* array is full, we could scan for a free slot but that
|
||||
* make little sense to bother, just skip the port
|
||||
*/
|
||||
if (index >= MAX_LEGACY_SERIAL_PORTS)
|
||||
return -1;
|
||||
if (index >= legacy_serial_count)
|
||||
legacy_serial_count = index + 1;
|
||||
|
||||
/* Check if there is a port who already claimed our slot */
|
||||
if (legacy_serial_infos[index].np != 0) {
|
||||
/* if we still have some room, move it, else override */
|
||||
if (legacy_serial_count < MAX_LEGACY_SERIAL_PORTS) {
|
||||
printk(KERN_INFO "Moved legacy port %d -> %d\n",
|
||||
index, legacy_serial_count);
|
||||
legacy_serial_ports[legacy_serial_count] =
|
||||
legacy_serial_ports[index];
|
||||
legacy_serial_infos[legacy_serial_count] =
|
||||
legacy_serial_infos[index];
|
||||
legacy_serial_count++;
|
||||
} else {
|
||||
printk(KERN_INFO "Replacing legacy port %d\n", index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now fill the entry */
|
||||
memset(&legacy_serial_ports[index], 0,
|
||||
sizeof(struct plat_serial8250_port));
|
||||
if (iotype == UPIO_PORT)
|
||||
legacy_serial_ports[index].iobase = base;
|
||||
else
|
||||
legacy_serial_ports[index].mapbase = base;
|
||||
legacy_serial_ports[index].iotype = iotype;
|
||||
legacy_serial_ports[index].uartclk = clock;
|
||||
legacy_serial_ports[index].irq = irq;
|
||||
legacy_serial_ports[index].flags = flags;
|
||||
legacy_serial_infos[index].taddr = taddr;
|
||||
legacy_serial_infos[index].np = of_node_get(np);
|
||||
legacy_serial_infos[index].clock = clock;
|
||||
legacy_serial_infos[index].speed = spd ? *spd : 0;
|
||||
|
||||
printk(KERN_INFO "Found legacy serial port %d for %s\n",
|
||||
index, np->full_name);
|
||||
printk(KERN_INFO " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n",
|
||||
(iotype == UPIO_PORT) ? "port" : "mem",
|
||||
(unsigned long long)base, (unsigned long long)taddr, irq,
|
||||
legacy_serial_ports[index].uartclk,
|
||||
legacy_serial_infos[index].speed);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int __init add_legacy_soc_port(struct device_node *np,
|
||||
struct device_node *soc_dev)
|
||||
{
|
||||
phys_addr_t addr;
|
||||
u32 *addrp;
|
||||
unsigned int flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
|
||||
|
||||
/* We only support ports that have a clock frequency properly
|
||||
* encoded in the device-tree.
|
||||
*/
|
||||
if (get_property(np, "clock-frequency", NULL) == NULL)
|
||||
return -1;
|
||||
|
||||
/* Get the address */
|
||||
addrp = of_get_address(soc_dev, 0, NULL, NULL);
|
||||
if (addrp == NULL)
|
||||
return -1;
|
||||
|
||||
addr = of_translate_address(soc_dev, addrp);
|
||||
|
||||
/* Add port, irq will be dealt with later. We passed a translated
|
||||
* IO port value. It will be fixed up later along with the irq
|
||||
*/
|
||||
return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ISA
|
||||
static int __init add_legacy_isa_port(struct device_node *np,
|
||||
struct device_node *isa_brg)
|
||||
{
|
||||
u32 *reg;
|
||||
char *typep;
|
||||
int index = -1;
|
||||
phys_addr_t taddr;
|
||||
|
||||
/* Get the ISA port number */
|
||||
reg = (u32 *)get_property(np, "reg", NULL);
|
||||
if (reg == NULL)
|
||||
return -1;
|
||||
|
||||
/* Verify it's an IO port, we don't support anything else */
|
||||
if (!(reg[0] & 0x00000001))
|
||||
return -1;
|
||||
|
||||
/* Now look for an "ibm,aix-loc" property that gives us ordering
|
||||
* if any...
|
||||
*/
|
||||
typep = (char *)get_property(np, "ibm,aix-loc", NULL);
|
||||
|
||||
/* If we have a location index, then use it */
|
||||
if (typep && *typep == 'S')
|
||||
index = simple_strtol(typep+1, NULL, 0) - 1;
|
||||
|
||||
/* Translate ISA address */
|
||||
taddr = of_translate_address(np, reg);
|
||||
|
||||
/* Add port, irq will be dealt with later */
|
||||
return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ, UPF_BOOT_AUTOCONF);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static int __init add_legacy_pci_port(struct device_node *np,
|
||||
struct device_node *pci_dev)
|
||||
{
|
||||
phys_addr_t addr, base;
|
||||
u32 *addrp;
|
||||
unsigned int flags;
|
||||
int iotype, index = -1, lindex = 0;
|
||||
|
||||
/* We only support ports that have a clock frequency properly
|
||||
* encoded in the device-tree (that is have an fcode). Anything
|
||||
* else can't be used that early and will be normally probed by
|
||||
* the generic 8250_pci driver later on. The reason is that 8250
|
||||
* compatible UARTs on PCI need all sort of quirks (port offsets
|
||||
* etc...) that this code doesn't know about
|
||||
*/
|
||||
if (get_property(np, "clock-frequency", NULL) == NULL)
|
||||
return -1;
|
||||
|
||||
/* Get the PCI address. Assume BAR 0 */
|
||||
addrp = of_get_pci_address(pci_dev, 0, NULL, &flags);
|
||||
if (addrp == NULL)
|
||||
return -1;
|
||||
|
||||
/* We only support BAR 0 for now */
|
||||
iotype = (flags & IORESOURCE_MEM) ? UPIO_MEM : UPIO_PORT;
|
||||
addr = of_translate_address(pci_dev, addrp);
|
||||
|
||||
/* Set the IO base to the same as the translated address for MMIO,
|
||||
* or to the domain local IO base for PIO (it will be fixed up later)
|
||||
*/
|
||||
if (iotype == UPIO_MEM)
|
||||
base = addr;
|
||||
else
|
||||
base = addrp[2];
|
||||
|
||||
/* Try to guess an index... If we have subdevices of the pci dev,
|
||||
* we get to their "reg" property
|
||||
*/
|
||||
if (np != pci_dev) {
|
||||
u32 *reg = (u32 *)get_property(np, "reg", NULL);
|
||||
if (reg && (*reg < 4))
|
||||
index = lindex = *reg;
|
||||
}
|
||||
|
||||
/* Local index means it's the Nth port in the PCI chip. Unfortunately
|
||||
* the offset to add here is device specific. We know about those
|
||||
* EXAR ports and we default to the most common case. If your UART
|
||||
* doesn't work for these settings, you'll have to add your own special
|
||||
* cases here
|
||||
*/
|
||||
if (device_is_compatible(pci_dev, "pci13a8,152") ||
|
||||
device_is_compatible(pci_dev, "pci13a8,154") ||
|
||||
device_is_compatible(pci_dev, "pci13a8,158")) {
|
||||
addr += 0x200 * lindex;
|
||||
base += 0x200 * lindex;
|
||||
} else {
|
||||
addr += 8 * lindex;
|
||||
base += 8 * lindex;
|
||||
}
|
||||
|
||||
/* Add port, irq will be dealt with later. We passed a translated
|
||||
* IO port value. It will be fixed up later along with the irq
|
||||
*/
|
||||
return add_legacy_port(np, index, iotype, base, addr, NO_IRQ, UPF_BOOT_AUTOCONF);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is called very early, as part of setup_system() or eventually
|
||||
* setup_arch(), basically before anything else in this file. This function
|
||||
* will try to build a list of all the available 8250-compatible serial ports
|
||||
* in the machine using the Open Firmware device-tree. It currently only deals
|
||||
* with ISA and PCI busses but could be extended. It allows a very early boot
|
||||
* console to be initialized, that list is also used later to provide 8250 with
|
||||
* the machine non-PCI ports and to properly pick the default console port
|
||||
*/
|
||||
void __init find_legacy_serial_ports(void)
|
||||
{
|
||||
struct device_node *np, *stdout = NULL;
|
||||
char *path;
|
||||
int index;
|
||||
|
||||
DBG(" -> find_legacy_serial_port()\n");
|
||||
|
||||
/* Now find out if one of these is out firmware console */
|
||||
path = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (path != NULL) {
|
||||
stdout = of_find_node_by_path(path);
|
||||
if (stdout)
|
||||
DBG("stdout is %s\n", stdout->full_name);
|
||||
} else {
|
||||
DBG(" no linux,stdout-path !\n");
|
||||
}
|
||||
|
||||
/* First fill our array with SOC ports */
|
||||
for (np = NULL; (np = of_find_compatible_node(np, "serial", "ns16550")) != NULL;) {
|
||||
struct device_node *soc = of_get_parent(np);
|
||||
if (soc && !strcmp(soc->type, "soc")) {
|
||||
index = add_legacy_soc_port(np, np);
|
||||
if (index >= 0 && np == stdout)
|
||||
legacy_serial_console = index;
|
||||
}
|
||||
of_node_put(soc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ISA
|
||||
/* First fill our array with ISA ports */
|
||||
for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
|
||||
struct device_node *isa = of_get_parent(np);
|
||||
if (isa && !strcmp(isa->name, "isa")) {
|
||||
index = add_legacy_isa_port(np, isa);
|
||||
if (index >= 0 && np == stdout)
|
||||
legacy_serial_console = index;
|
||||
}
|
||||
of_node_put(isa);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* Next, try to locate PCI ports */
|
||||
for (np = NULL; (np = of_find_all_nodes(np));) {
|
||||
struct device_node *pci, *parent = of_get_parent(np);
|
||||
if (parent && !strcmp(parent->name, "isa")) {
|
||||
of_node_put(parent);
|
||||
continue;
|
||||
}
|
||||
if (strcmp(np->name, "serial") && strcmp(np->type, "serial")) {
|
||||
of_node_put(parent);
|
||||
continue;
|
||||
}
|
||||
/* Check for known pciclass, and also check wether we have
|
||||
* a device with child nodes for ports or not
|
||||
*/
|
||||
if (device_is_compatible(np, "pciclass,0700") ||
|
||||
device_is_compatible(np, "pciclass,070002"))
|
||||
pci = np;
|
||||
else if (device_is_compatible(parent, "pciclass,0700") ||
|
||||
device_is_compatible(parent, "pciclass,070002"))
|
||||
pci = parent;
|
||||
else {
|
||||
of_node_put(parent);
|
||||
continue;
|
||||
}
|
||||
index = add_legacy_pci_port(np, pci);
|
||||
if (index >= 0 && np == stdout)
|
||||
legacy_serial_console = index;
|
||||
of_node_put(parent);
|
||||
}
|
||||
#endif
|
||||
|
||||
DBG("legacy_serial_console = %d\n", legacy_serial_console);
|
||||
|
||||
/* udbg is 64 bits only for now, that will change soon though ... */
|
||||
while (legacy_serial_console >= 0) {
|
||||
struct legacy_serial_info *info =
|
||||
&legacy_serial_infos[legacy_serial_console];
|
||||
void __iomem *addr;
|
||||
|
||||
if (info->taddr == 0)
|
||||
break;
|
||||
addr = ioremap(info->taddr, 0x1000);
|
||||
if (addr == NULL)
|
||||
break;
|
||||
if (info->speed == 0)
|
||||
info->speed = udbg_probe_uart_speed(addr, info->clock);
|
||||
DBG("default console speed = %d\n", info->speed);
|
||||
udbg_init_uart(addr, info->speed, info->clock);
|
||||
break;
|
||||
}
|
||||
|
||||
DBG(" <- find_legacy_serial_port()\n");
|
||||
}
|
||||
|
||||
static struct platform_device serial_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_PLATFORM,
|
||||
.dev = {
|
||||
.platform_data = legacy_serial_ports,
|
||||
},
|
||||
};
|
||||
|
||||
static void __init fixup_port_irq(int index,
|
||||
struct device_node *np,
|
||||
struct plat_serial8250_port *port)
|
||||
{
|
||||
DBG("fixup_port_irq(%d)\n", index);
|
||||
|
||||
/* Check for interrupts in that node */
|
||||
if (np->n_intrs > 0) {
|
||||
port->irq = np->intrs[0].line;
|
||||
DBG(" port %d (%s), irq=%d\n",
|
||||
index, np->full_name, port->irq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check for interrupts in the parent */
|
||||
np = of_get_parent(np);
|
||||
if (np == NULL)
|
||||
return;
|
||||
|
||||
if (np->n_intrs > 0) {
|
||||
port->irq = np->intrs[0].line;
|
||||
DBG(" port %d (%s), irq=%d\n",
|
||||
index, np->full_name, port->irq);
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static void __init fixup_port_pio(int index,
|
||||
struct device_node *np,
|
||||
struct plat_serial8250_port *port)
|
||||
{
|
||||
#ifdef CONFIG_PCI
|
||||
struct pci_controller *hose;
|
||||
|
||||
DBG("fixup_port_pio(%d)\n", index);
|
||||
|
||||
hose = pci_find_hose_for_OF_device(np);
|
||||
if (hose) {
|
||||
unsigned long offset = (unsigned long)hose->io_base_virt -
|
||||
#ifdef CONFIG_PPC64
|
||||
pci_io_base;
|
||||
#else
|
||||
isa_io_base;
|
||||
#endif
|
||||
DBG("port %d, IO %lx -> %lx\n",
|
||||
index, port->iobase, port->iobase + offset);
|
||||
port->iobase += offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init fixup_port_mmio(int index,
|
||||
struct device_node *np,
|
||||
struct plat_serial8250_port *port)
|
||||
{
|
||||
DBG("fixup_port_mmio(%d)\n", index);
|
||||
|
||||
port->membase = ioremap(port->mapbase, 0x100);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called as an arch initcall, hopefully before the PCI bus is
|
||||
* probed and/or the 8250 driver loaded since we need to register our
|
||||
* platform devices before 8250 PCI ones are detected as some of them
|
||||
* must properly "override" the platform ones.
|
||||
*
|
||||
* This function fixes up the interrupt value for platform ports as it
|
||||
* couldn't be done earlier before interrupt maps have been parsed. It
|
||||
* also "corrects" the IO address for PIO ports for the same reason,
|
||||
* since earlier, the PHBs virtual IO space wasn't assigned yet. It then
|
||||
* registers all those platform ports for use by the 8250 driver when it
|
||||
* finally loads.
|
||||
*/
|
||||
static int __init serial_dev_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (legacy_serial_count == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Before we register the platfrom serial devices, we need
|
||||
* to fixup their interrutps and their IO ports.
|
||||
*/
|
||||
DBG("Fixing serial ports interrupts and IO ports ...\n");
|
||||
|
||||
for (i = 0; i < legacy_serial_count; i++) {
|
||||
struct plat_serial8250_port *port = &legacy_serial_ports[i];
|
||||
struct device_node *np = legacy_serial_infos[i].np;
|
||||
|
||||
if (port->irq == NO_IRQ)
|
||||
fixup_port_irq(i, np, port);
|
||||
if (port->iotype == UPIO_PORT)
|
||||
fixup_port_pio(i, np, port);
|
||||
if (port->iotype == UPIO_MEM)
|
||||
fixup_port_mmio(i, np, port);
|
||||
}
|
||||
|
||||
DBG("Registering platform serial ports\n");
|
||||
|
||||
return platform_device_register(&serial_device);
|
||||
}
|
||||
arch_initcall(serial_dev_init);
|
||||
|
||||
|
||||
/*
|
||||
* This is called very early, as part of console_init() (typically just after
|
||||
* time_init()). This function is respondible for trying to find a good
|
||||
* default console on serial ports. It tries to match the open firmware
|
||||
* default output with one of the available serial console drivers, either
|
||||
* one of the platform serial ports that have been probed earlier by
|
||||
* find_legacy_serial_ports() or some more platform specific ones.
|
||||
*/
|
||||
static int __init check_legacy_serial_console(void)
|
||||
{
|
||||
struct device_node *prom_stdout = NULL;
|
||||
int speed = 0, offset = 0;
|
||||
char *name;
|
||||
u32 *spd;
|
||||
|
||||
DBG(" -> check_legacy_serial_console()\n");
|
||||
|
||||
/* The user has requested a console so this is already set up. */
|
||||
if (strstr(saved_command_line, "console=")) {
|
||||
DBG(" console was specified !\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!of_chosen) {
|
||||
DBG(" of_chosen is NULL !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (legacy_serial_console < 0) {
|
||||
DBG(" legacy_serial_console not found !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* We are getting a weird phandle from OF ... */
|
||||
/* ... So use the full path instead */
|
||||
name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name == NULL) {
|
||||
DBG(" no linux,stdout-path !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
prom_stdout = of_find_node_by_path(name);
|
||||
if (!prom_stdout) {
|
||||
DBG(" can't find stdout package %s !\n", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
DBG("stdout is %s\n", prom_stdout->full_name);
|
||||
|
||||
name = (char *)get_property(prom_stdout, "name", NULL);
|
||||
if (!name) {
|
||||
DBG(" stdout package has no name !\n");
|
||||
goto not_found;
|
||||
}
|
||||
spd = (u32 *)get_property(prom_stdout, "current-speed", NULL);
|
||||
if (spd)
|
||||
speed = *spd;
|
||||
|
||||
if (0)
|
||||
;
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
else if (strcmp(name, "serial") == 0) {
|
||||
int i;
|
||||
/* Look for it in probed array */
|
||||
for (i = 0; i < legacy_serial_count; i++) {
|
||||
if (prom_stdout != legacy_serial_infos[i].np)
|
||||
continue;
|
||||
offset = i;
|
||||
speed = legacy_serial_infos[i].speed;
|
||||
break;
|
||||
}
|
||||
if (i >= legacy_serial_count)
|
||||
goto not_found;
|
||||
}
|
||||
#endif /* CONFIG_SERIAL_8250_CONSOLE */
|
||||
#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
|
||||
else if (strcmp(name, "ch-a") == 0)
|
||||
offset = 0;
|
||||
else if (strcmp(name, "ch-b") == 0)
|
||||
offset = 1;
|
||||
#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
|
||||
else
|
||||
goto not_found;
|
||||
of_node_put(prom_stdout);
|
||||
|
||||
DBG("Found serial console at ttyS%d\n", offset);
|
||||
|
||||
if (speed) {
|
||||
static char __initdata opt[16];
|
||||
sprintf(opt, "%d", speed);
|
||||
return add_preferred_console("ttyS", offset, opt);
|
||||
} else
|
||||
return add_preferred_console("ttyS", offset, NULL);
|
||||
|
||||
not_found:
|
||||
DBG("No preferred console found !\n");
|
||||
of_node_put(prom_stdout);
|
||||
return -ENODEV;
|
||||
}
|
||||
console_initcall(check_legacy_serial_console);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/iseries/lpar_map.h>
|
||||
|
||||
const struct LparMap __attribute__((__section__(".text"))) xLparMap = {
|
||||
@ -16,16 +16,16 @@ const struct LparMap __attribute__((__section__(".text"))) xLparMap = {
|
||||
.xSegmentTableOffs = STAB0_PAGE,
|
||||
|
||||
.xEsids = {
|
||||
{ .xKernelEsid = GET_ESID(KERNELBASE),
|
||||
.xKernelVsid = KERNEL_VSID(KERNELBASE), },
|
||||
{ .xKernelEsid = GET_ESID(VMALLOCBASE),
|
||||
.xKernelVsid = KERNEL_VSID(VMALLOCBASE), },
|
||||
{ .xKernelEsid = GET_ESID(PAGE_OFFSET),
|
||||
.xKernelVsid = KERNEL_VSID(PAGE_OFFSET), },
|
||||
{ .xKernelEsid = GET_ESID(VMALLOC_START),
|
||||
.xKernelVsid = KERNEL_VSID(VMALLOC_START), },
|
||||
},
|
||||
|
||||
.xRanges = {
|
||||
{ .xPages = HvPagesToMap,
|
||||
.xOffset = 0,
|
||||
.xVPN = KERNEL_VSID(KERNELBASE) << (SID_SHIFT - HW_PAGE_SHIFT),
|
||||
.xVPN = KERNEL_VSID(PAGE_OFFSET) << (SID_SHIFT - HW_PAGE_SHIFT),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
67
arch/powerpc/kernel/machine_kexec.c
Normal file
67
arch/powerpc/kernel/machine_kexec.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Code to handle transition of Linux booting another kernel.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
|
||||
* GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
|
||||
* Copyright (C) 2005 IBM Corporation.
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/threads.h>
|
||||
#include <asm/machdep.h>
|
||||
|
||||
/*
|
||||
* Provide a dummy crash_notes definition until crash dump is implemented.
|
||||
* This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
|
||||
*/
|
||||
note_buf_t crash_notes[NR_CPUS];
|
||||
|
||||
void machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
if (ppc_md.machine_crash_shutdown)
|
||||
ppc_md.machine_crash_shutdown(regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do what every setup is needed on image and the
|
||||
* reboot code buffer to allow us to avoid allocations
|
||||
* later.
|
||||
*/
|
||||
int machine_kexec_prepare(struct kimage *image)
|
||||
{
|
||||
if (ppc_md.machine_kexec_prepare)
|
||||
return ppc_md.machine_kexec_prepare(image);
|
||||
/*
|
||||
* Fail if platform doesn't provide its own machine_kexec_prepare
|
||||
* implementation.
|
||||
*/
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void machine_kexec_cleanup(struct kimage *image)
|
||||
{
|
||||
if (ppc_md.machine_kexec_cleanup)
|
||||
ppc_md.machine_kexec_cleanup(image);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allocate memory (or fail in any way) in machine_kexec().
|
||||
* We are past the point of no return, committed to rebooting now.
|
||||
*/
|
||||
NORET_TYPE void machine_kexec(struct kimage *image)
|
||||
{
|
||||
if (ppc_md.machine_kexec)
|
||||
ppc_md.machine_kexec(image);
|
||||
else {
|
||||
/*
|
||||
* Fall back to normal restart if platform doesn't provide
|
||||
* its own kexec function, and user insist to kexec...
|
||||
*/
|
||||
machine_restart(NULL);
|
||||
}
|
||||
for(;;);
|
||||
}
|
65
arch/powerpc/kernel/machine_kexec_32.c
Normal file
65
arch/powerpc/kernel/machine_kexec_32.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* PPC32 code to handle Linux booting another kernel.
|
||||
*
|
||||
* Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
|
||||
* GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
|
||||
* Copyright (C) 2005 IBM Corporation.
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
typedef NORET_TYPE void (*relocate_new_kernel_t)(
|
||||
unsigned long indirection_page,
|
||||
unsigned long reboot_code_buffer,
|
||||
unsigned long start_address) ATTRIB_NORET;
|
||||
|
||||
/*
|
||||
* This is a generic machine_kexec function suitable at least for
|
||||
* non-OpenFirmware embedded platforms.
|
||||
* It merely copies the image relocation code to the control page and
|
||||
* jumps to it.
|
||||
* A platform specific function may just call this one.
|
||||
*/
|
||||
void default_machine_kexec(struct kimage *image)
|
||||
{
|
||||
const extern unsigned char relocate_new_kernel[];
|
||||
const extern unsigned int relocate_new_kernel_size;
|
||||
unsigned long page_list;
|
||||
unsigned long reboot_code_buffer, reboot_code_buffer_phys;
|
||||
relocate_new_kernel_t rnk;
|
||||
|
||||
/* Interrupts aren't acceptable while we reboot */
|
||||
local_irq_disable();
|
||||
|
||||
page_list = image->head;
|
||||
|
||||
/* we need both effective and real address here */
|
||||
reboot_code_buffer =
|
||||
(unsigned long)page_address(image->control_code_page);
|
||||
reboot_code_buffer_phys = virt_to_phys((void *)reboot_code_buffer);
|
||||
|
||||
/* copy our kernel relocation code to the control code page */
|
||||
memcpy((void *)reboot_code_buffer, relocate_new_kernel,
|
||||
relocate_new_kernel_size);
|
||||
|
||||
flush_icache_range(reboot_code_buffer,
|
||||
reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE);
|
||||
printk(KERN_INFO "Bye!\n");
|
||||
|
||||
/* now call it */
|
||||
rnk = (relocate_new_kernel_t) reboot_code_buffer;
|
||||
(*rnk)(page_list, reboot_code_buffer_phys, image->start);
|
||||
}
|
||||
|
||||
int default_machine_kexec_prepare(struct kimage *image)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* machine_kexec.c - handle transition of Linux booting another kernel
|
||||
* PPC64 code to handle Linux booting another kernel.
|
||||
*
|
||||
* Copyright (C) 2004-2005, IBM Corp.
|
||||
*
|
||||
@ -28,21 +28,7 @@
|
||||
|
||||
#define HASH_GROUP_SIZE 0x80 /* size of each hash group, asm/mmu.h */
|
||||
|
||||
/* Have this around till we move it into crash specific file */
|
||||
note_buf_t crash_notes[NR_CPUS];
|
||||
|
||||
/* Dummy for now. Not sure if we need to have a crash shutdown in here
|
||||
* and if what it will achieve. Letting it be now to compile the code
|
||||
* in generic kexec environment
|
||||
*/
|
||||
void machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
/* do nothing right now */
|
||||
/* smp_relase_cpus() if we want smp on panic kernel */
|
||||
/* cpu_irq_down to isolate us until we are ready */
|
||||
}
|
||||
|
||||
int machine_kexec_prepare(struct kimage *image)
|
||||
int default_machine_kexec_prepare(struct kimage *image)
|
||||
{
|
||||
int i;
|
||||
unsigned long begin, end; /* limits of segment */
|
||||
@ -111,11 +97,6 @@ int machine_kexec_prepare(struct kimage *image)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void machine_kexec_cleanup(struct kimage *image)
|
||||
{
|
||||
/* we do nothing in prepare that needs to be undone */
|
||||
}
|
||||
|
||||
#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE)
|
||||
|
||||
static void copy_segments(unsigned long ind)
|
||||
@ -172,9 +153,8 @@ void kexec_copy_flush(struct kimage *image)
|
||||
* including ones that were in place on the original copy
|
||||
*/
|
||||
for (i = 0; i < nr_segments; i++)
|
||||
flush_icache_range(ranges[i].mem + KERNELBASE,
|
||||
ranges[i].mem + KERNELBASE +
|
||||
ranges[i].memsz);
|
||||
flush_icache_range((unsigned long)__va(ranges[i].mem),
|
||||
(unsigned long)__va(ranges[i].mem + ranges[i].memsz));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@ -283,13 +263,20 @@ extern NORET_TYPE void kexec_sequence(void *newstack, unsigned long start,
|
||||
void (*clear_all)(void)) ATTRIB_NORET;
|
||||
|
||||
/* too late to fail here */
|
||||
void machine_kexec(struct kimage *image)
|
||||
void default_machine_kexec(struct kimage *image)
|
||||
{
|
||||
|
||||
/* prepare control code if any */
|
||||
|
||||
/* shutdown other cpus into our wait loop and quiesce interrupts */
|
||||
kexec_prepare_cpus();
|
||||
/*
|
||||
* If the kexec boot is the normal one, need to shutdown other cpus
|
||||
* into our wait loop and quiesce interrupts.
|
||||
* Otherwise, in the case of crashed mode (crashing_cpu >= 0),
|
||||
* stopping other CPUs and collecting their pt_regs is done before
|
||||
* using debugger IPI.
|
||||
*/
|
||||
|
||||
if (crashing_cpu == -1)
|
||||
kexec_prepare_cpus();
|
||||
|
||||
/* switch to a staticly allocated stack. Based on irq stack code.
|
||||
* XXX: the task struct will likely be invalid once we do the copy!
|
||||
|
@ -5,6 +5,10 @@
|
||||
* Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
|
||||
* and Paul Mackerras.
|
||||
*
|
||||
* kexec bits:
|
||||
* Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
|
||||
* GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
|
||||
*
|
||||
* 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
|
||||
@ -24,6 +28,8 @@
|
||||
#include <asm/ppc_asm.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/kexec.h>
|
||||
|
||||
.text
|
||||
|
||||
@ -1006,3 +1012,110 @@ _GLOBAL(execve)
|
||||
*/
|
||||
_GLOBAL(__main)
|
||||
blr
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
/*
|
||||
* Must be relocatable PIC code callable as a C function.
|
||||
*/
|
||||
.globl relocate_new_kernel
|
||||
relocate_new_kernel:
|
||||
/* r3 = page_list */
|
||||
/* r4 = reboot_code_buffer */
|
||||
/* r5 = start_address */
|
||||
|
||||
li r0, 0
|
||||
|
||||
/*
|
||||
* Set Machine Status Register to a known status,
|
||||
* switch the MMU off and jump to 1: in a single step.
|
||||
*/
|
||||
|
||||
mr r8, r0
|
||||
ori r8, r8, MSR_RI|MSR_ME
|
||||
mtspr SPRN_SRR1, r8
|
||||
addi r8, r4, 1f - relocate_new_kernel
|
||||
mtspr SPRN_SRR0, r8
|
||||
sync
|
||||
rfi
|
||||
|
||||
1:
|
||||
/* from this point address translation is turned off */
|
||||
/* and interrupts are disabled */
|
||||
|
||||
/* set a new stack at the bottom of our page... */
|
||||
/* (not really needed now) */
|
||||
addi r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */
|
||||
stw r0, 0(r1)
|
||||
|
||||
/* Do the copies */
|
||||
li r6, 0 /* checksum */
|
||||
mr r0, r3
|
||||
b 1f
|
||||
|
||||
0: /* top, read another word for the indirection page */
|
||||
lwzu r0, 4(r3)
|
||||
|
||||
1:
|
||||
/* is it a destination page? (r8) */
|
||||
rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */
|
||||
beq 2f
|
||||
|
||||
rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */
|
||||
b 0b
|
||||
|
||||
2: /* is it an indirection page? (r3) */
|
||||
rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */
|
||||
beq 2f
|
||||
|
||||
rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */
|
||||
subi r3, r3, 4
|
||||
b 0b
|
||||
|
||||
2: /* are we done? */
|
||||
rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */
|
||||
beq 2f
|
||||
b 3f
|
||||
|
||||
2: /* is it a source page? (r9) */
|
||||
rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */
|
||||
beq 0b
|
||||
|
||||
rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */
|
||||
|
||||
li r7, PAGE_SIZE / 4
|
||||
mtctr r7
|
||||
subi r9, r9, 4
|
||||
subi r8, r8, 4
|
||||
9:
|
||||
lwzu r0, 4(r9) /* do the copy */
|
||||
xor r6, r6, r0
|
||||
stwu r0, 4(r8)
|
||||
dcbst 0, r8
|
||||
sync
|
||||
icbi 0, r8
|
||||
bdnz 9b
|
||||
|
||||
addi r9, r9, 4
|
||||
addi r8, r8, 4
|
||||
b 0b
|
||||
|
||||
3:
|
||||
|
||||
/* To be certain of avoiding problems with self-modifying code
|
||||
* execute a serializing instruction here.
|
||||
*/
|
||||
isync
|
||||
sync
|
||||
|
||||
/* jump to the entry point, usually the setup routine */
|
||||
mtlr r5
|
||||
blrl
|
||||
|
||||
1: b 1b
|
||||
|
||||
relocate_new_kernel_end:
|
||||
|
||||
.globl relocate_new_kernel_size
|
||||
relocate_new_kernel_size:
|
||||
.long relocate_new_kernel_end - relocate_new_kernel
|
||||
#endif
|
||||
|
@ -80,80 +80,74 @@ static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
|
||||
static ssize_t dev_nvram_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t len;
|
||||
char *tmp_buffer;
|
||||
int size;
|
||||
ssize_t ret;
|
||||
char *tmp = NULL;
|
||||
ssize_t size;
|
||||
|
||||
if (ppc_md.nvram_size == NULL)
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
if (!ppc_md.nvram_size)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
size = ppc_md.nvram_size();
|
||||
if (*ppos >= size || size < 0)
|
||||
goto out;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, buf, count))
|
||||
return -EFAULT;
|
||||
if (*ppos >= size)
|
||||
return 0;
|
||||
if (count > size)
|
||||
count = size;
|
||||
count = min_t(size_t, count, size - *ppos);
|
||||
count = min(count, PAGE_SIZE);
|
||||
|
||||
tmp_buffer = (char *) kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp_buffer) {
|
||||
printk(KERN_ERR "dev_read_nvram: kmalloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = -ENOMEM;
|
||||
tmp = kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
|
||||
len = ppc_md.nvram_read(tmp_buffer, count, ppos);
|
||||
if ((long)len <= 0) {
|
||||
kfree(tmp_buffer);
|
||||
return len;
|
||||
}
|
||||
ret = ppc_md.nvram_read(tmp, count, ppos);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
if (copy_to_user(buf, tmp_buffer, len)) {
|
||||
kfree(tmp_buffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (copy_to_user(buf, tmp, ret))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(tmp_buffer);
|
||||
return len;
|
||||
out:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t dev_nvram_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t len;
|
||||
char * tmp_buffer;
|
||||
int size;
|
||||
ssize_t ret;
|
||||
char *tmp = NULL;
|
||||
ssize_t size;
|
||||
|
||||
if (ppc_md.nvram_size == NULL)
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
if (!ppc_md.nvram_size)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
size = ppc_md.nvram_size();
|
||||
if (*ppos >= size || size < 0)
|
||||
goto out;
|
||||
|
||||
if (!access_ok(VERIFY_READ, buf, count))
|
||||
return -EFAULT;
|
||||
if (*ppos >= size)
|
||||
return 0;
|
||||
if (count > size)
|
||||
count = size;
|
||||
count = min_t(size_t, count, size - *ppos);
|
||||
count = min(count, PAGE_SIZE);
|
||||
|
||||
tmp_buffer = (char *) kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp_buffer) {
|
||||
printk(KERN_ERR "dev_nvram_write: kmalloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(tmp_buffer, buf, count)) {
|
||||
kfree(tmp_buffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
ret = -ENOMEM;
|
||||
tmp = kmalloc(count, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
|
||||
len = ppc_md.nvram_write(tmp_buffer, count, ppos);
|
||||
if ((long)len <= 0) {
|
||||
kfree(tmp_buffer);
|
||||
return len;
|
||||
}
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(tmp, buf, count))
|
||||
goto out;
|
||||
|
||||
ret = ppc_md.nvram_write(tmp, count, ppos);
|
||||
|
||||
out:
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
|
||||
kfree(tmp_buffer);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int dev_nvram_ioctl(struct inode *inode, struct file *file,
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <asm/page.h>
|
||||
#include <asm/lppaca.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
#include <asm/iseries/it_lp_reg_save.h>
|
||||
#include <asm/paca.h>
|
||||
|
||||
|
||||
@ -26,8 +27,7 @@ extern unsigned long __toc_start;
|
||||
|
||||
/* The Paca is an array with one entry per processor. Each contains an
|
||||
* lppaca, which contains the information shared between the
|
||||
* hypervisor and Linux. Each also contains an ItLpRegSave area which
|
||||
* is used by the hypervisor to save registers.
|
||||
* hypervisor and Linux.
|
||||
* On systems with hardware multi-threading, there are two threads
|
||||
* per processor. The Paca array must contain an entry for each thread.
|
||||
* The VPD Areas will give a max logical processors = 2 * max physical
|
||||
@ -37,7 +37,6 @@ extern unsigned long __toc_start;
|
||||
#define PACA_INIT_COMMON(number, start, asrr, asrv) \
|
||||
.lock_token = 0x8000, \
|
||||
.paca_index = (number), /* Paca Index */ \
|
||||
.default_decr = 0x00ff0000, /* Initial Decr */ \
|
||||
.kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL, \
|
||||
.stab_real = (asrr), /* Real pointer to segment table */ \
|
||||
.stab_addr = (asrv), /* Virt pointer to segment table */ \
|
||||
@ -57,11 +56,7 @@ extern unsigned long __toc_start;
|
||||
#ifdef CONFIG_PPC_ISERIES
|
||||
#define PACA_INIT_ISERIES(number) \
|
||||
.lppaca_ptr = &paca[number].lppaca, \
|
||||
.reg_save_ptr = &paca[number].reg_save, \
|
||||
.reg_save = { \
|
||||
.xDesc = 0xd397d9e2, /* "LpRS" */ \
|
||||
.xSize = sizeof(struct ItLpRegSave) \
|
||||
}
|
||||
.reg_save_ptr = &iseries_reg_save[number],
|
||||
|
||||
#define PACA_INIT(number) \
|
||||
{ \
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <asm/udbg.h>
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#define DBG(fmt...) printk(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
@ -251,7 +251,7 @@ void pcibios_free_controller(struct pci_controller *phb)
|
||||
kfree(phb);
|
||||
}
|
||||
|
||||
static void __init pcibios_claim_one_bus(struct pci_bus *b)
|
||||
void __devinit pcibios_claim_one_bus(struct pci_bus *b)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
struct pci_bus *child_bus;
|
||||
@ -323,6 +323,7 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev)
|
||||
addrs = (u32 *) get_property(node, "assigned-addresses", &proplen);
|
||||
if (!addrs)
|
||||
return;
|
||||
DBG(" parse addresses (%d bytes) @ %p\n", proplen, addrs);
|
||||
for (; proplen >= 20; proplen -= 20, addrs += 5) {
|
||||
flags = pci_parse_of_flags(addrs[0]);
|
||||
if (!flags)
|
||||
@ -332,6 +333,9 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev)
|
||||
if (!size)
|
||||
continue;
|
||||
i = addrs[0] & 0xff;
|
||||
DBG(" base: %llx, size: %llx, i: %x\n",
|
||||
(unsigned long long)base, (unsigned long long)size, i);
|
||||
|
||||
if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) {
|
||||
res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2];
|
||||
} else if (i == dev->rom_base_reg) {
|
||||
@ -362,6 +366,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
if (type == NULL)
|
||||
type = "";
|
||||
|
||||
DBG(" create device, devfn: %x, type: %s\n", devfn, type);
|
||||
|
||||
memset(dev, 0, sizeof(struct pci_dev));
|
||||
dev->bus = bus;
|
||||
dev->sysdata = node;
|
||||
@ -381,6 +387,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
dev->class = get_int_prop(node, "class-code", 0);
|
||||
|
||||
DBG(" class: 0x%x\n", dev->class);
|
||||
|
||||
dev->current_state = 4; /* unknown power state */
|
||||
|
||||
if (!strcmp(type, "pci")) {
|
||||
@ -402,6 +410,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
|
||||
pci_parse_of_addrs(node, dev);
|
||||
|
||||
DBG(" adding to system ...\n");
|
||||
|
||||
pci_device_add(dev, bus);
|
||||
|
||||
/* XXX pci_scan_msi_device(dev); */
|
||||
@ -418,15 +428,21 @@ void __devinit of_scan_bus(struct device_node *node,
|
||||
int reglen, devfn;
|
||||
struct pci_dev *dev;
|
||||
|
||||
DBG("of_scan_bus(%s) bus no %d... \n", node->full_name, bus->number);
|
||||
|
||||
while ((child = of_get_next_child(node, child)) != NULL) {
|
||||
DBG(" * %s\n", child->full_name);
|
||||
reg = (u32 *) get_property(child, "reg", ®len);
|
||||
if (reg == NULL || reglen < 20)
|
||||
continue;
|
||||
devfn = (reg[0] >> 8) & 0xff;
|
||||
|
||||
/* create a new pci_dev for this device */
|
||||
dev = of_create_pci_dev(child, bus, devfn);
|
||||
if (!dev)
|
||||
continue;
|
||||
DBG("dev header type: %x\n", dev->hdr_type);
|
||||
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
|
||||
of_scan_pci_bridge(child, dev);
|
||||
@ -446,16 +462,18 @@ void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
unsigned int flags;
|
||||
u64 size;
|
||||
|
||||
DBG("of_scan_pci_bridge(%s)\n", node->full_name);
|
||||
|
||||
/* parse bus-range property */
|
||||
busrange = (u32 *) get_property(node, "bus-range", &len);
|
||||
if (busrange == NULL || len != 8) {
|
||||
printk(KERN_ERR "Can't get bus-range for PCI-PCI bridge %s\n",
|
||||
printk(KERN_DEBUG "Can't get bus-range for PCI-PCI bridge %s\n",
|
||||
node->full_name);
|
||||
return;
|
||||
}
|
||||
ranges = (u32 *) get_property(node, "ranges", &len);
|
||||
if (ranges == NULL) {
|
||||
printk(KERN_ERR "Can't get ranges for PCI-PCI bridge %s\n",
|
||||
printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
|
||||
node->full_name);
|
||||
return;
|
||||
}
|
||||
@ -509,10 +527,13 @@ void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
}
|
||||
sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
|
||||
bus->number);
|
||||
DBG(" bus name: %s\n", bus->name);
|
||||
|
||||
mode = PCI_PROBE_NORMAL;
|
||||
if (ppc_md.pci_probe_mode)
|
||||
mode = ppc_md.pci_probe_mode(bus);
|
||||
DBG(" probe mode: %d\n", mode);
|
||||
|
||||
if (mode == PCI_PROBE_DEVTREE)
|
||||
of_scan_bus(node, bus);
|
||||
else if (mode == PCI_PROBE_NORMAL)
|
||||
@ -528,6 +549,8 @@ void __devinit scan_phb(struct pci_controller *hose)
|
||||
int i, mode;
|
||||
struct resource *res;
|
||||
|
||||
DBG("Scanning PHB %s\n", node ? node->full_name : "<NO NAME>");
|
||||
|
||||
bus = pci_create_bus(NULL, hose->first_busno, hose->ops, node);
|
||||
if (bus == NULL) {
|
||||
printk(KERN_ERR "Failed to create bus for PCI domain %04x\n",
|
||||
@ -552,8 +575,9 @@ void __devinit scan_phb(struct pci_controller *hose)
|
||||
|
||||
mode = PCI_PROBE_NORMAL;
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
if (ppc_md.pci_probe_mode)
|
||||
if (node && ppc_md.pci_probe_mode)
|
||||
mode = ppc_md.pci_probe_mode(bus);
|
||||
DBG(" probe mode: %d\n", mode);
|
||||
if (mode == PCI_PROBE_DEVTREE) {
|
||||
bus->subordinate = hose->last_busno;
|
||||
of_scan_bus(node, bus);
|
||||
@ -842,8 +866,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
|
||||
* Returns a negative error code on failure, zero on success.
|
||||
*/
|
||||
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state,
|
||||
int write_combine)
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
struct resource *rp;
|
||||
@ -896,6 +919,25 @@ static void __devinit pci_process_ISA_OF_ranges(struct device_node *isa_node,
|
||||
unsigned long phb_io_base_phys,
|
||||
void __iomem * phb_io_base_virt)
|
||||
{
|
||||
/* Remove these asap */
|
||||
|
||||
struct pci_address {
|
||||
u32 a_hi;
|
||||
u32 a_mid;
|
||||
u32 a_lo;
|
||||
};
|
||||
|
||||
struct isa_address {
|
||||
u32 a_hi;
|
||||
u32 a_lo;
|
||||
};
|
||||
|
||||
struct isa_range {
|
||||
struct isa_address isa_addr;
|
||||
struct pci_address pci_addr;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
struct isa_range *range;
|
||||
unsigned long pci_addr;
|
||||
unsigned int isa_addr;
|
||||
@ -1223,6 +1265,7 @@ void __devinit pcibios_fixup_device_resources(struct pci_dev *dev,
|
||||
}
|
||||
EXPORT_SYMBOL(pcibios_fixup_device_resources);
|
||||
|
||||
|
||||
static void __devinit do_bus_setup(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
@ -1306,8 +1349,38 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar,
|
||||
*end = rsrc->end + offset;
|
||||
}
|
||||
|
||||
struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
|
||||
{
|
||||
if (!have_of)
|
||||
return NULL;
|
||||
while(node) {
|
||||
struct pci_controller *hose, *tmp;
|
||||
list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
|
||||
if (hose->arch_data == node)
|
||||
return hose;
|
||||
node = node->parent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
|
||||
|
||||
unsigned long pci_address_to_pio(phys_addr_t address)
|
||||
{
|
||||
struct pci_controller *hose, *tmp;
|
||||
|
||||
list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
|
||||
if (address >= hose->io_base_phys &&
|
||||
address < (hose->io_base_phys + hose->pci_io_size)) {
|
||||
unsigned long base =
|
||||
(unsigned long)hose->io_base_virt - pci_io_base;
|
||||
return base + (address - hose->io_base_phys);
|
||||
}
|
||||
}
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_address_to_pio);
|
||||
|
||||
|
||||
#define IOBASE_BRIDGE_NUMBER 0
|
||||
#define IOBASE_MEMORY 1
|
||||
|
@ -43,8 +43,13 @@ static void dummy_perf(struct pt_regs *regs)
|
||||
mtspr(SPRN_MMCR0, mmcr0);
|
||||
}
|
||||
#else
|
||||
/* Ensure exceptions are disabled */
|
||||
static void dummy_perf(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int mmcr0 = mfspr(SPRN_MMCR0);
|
||||
|
||||
mmcr0 &= ~(MMCR0_PMXE);
|
||||
mtspr(SPRN_MMCR0, mmcr0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -76,11 +76,6 @@ EXPORT_SYMBOL(single_step_exception);
|
||||
EXPORT_SYMBOL(sys_sigreturn);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PPC_PREP)
|
||||
EXPORT_SYMBOL(_prep_type);
|
||||
EXPORT_SYMBOL(ucSystemType);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(strcpy);
|
||||
EXPORT_SYMBOL(strncpy);
|
||||
EXPORT_SYMBOL(strcat);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/initrd.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kexec.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
@ -37,6 +38,7 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/kdump.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/mmu.h>
|
||||
@ -55,21 +57,6 @@
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
struct pci_reg_property {
|
||||
struct pci_address addr;
|
||||
u32 size_hi;
|
||||
u32 size_lo;
|
||||
};
|
||||
|
||||
struct isa_reg_property {
|
||||
u32 space;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
|
||||
typedef int interpret_func(struct device_node *, unsigned long *,
|
||||
int, int, int);
|
||||
|
||||
static int __initdata dt_root_addr_cells;
|
||||
static int __initdata dt_root_size_cells;
|
||||
@ -311,6 +298,16 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
int i, j, n, sense;
|
||||
unsigned int *irq, virq;
|
||||
struct device_node *ic;
|
||||
int trace = 0;
|
||||
|
||||
//#define TRACE(fmt...) do { if (trace) { printk(fmt); mdelay(1000); } } while(0)
|
||||
#define TRACE(fmt...)
|
||||
|
||||
if (!strcmp(np->name, "smu-doorbell"))
|
||||
trace = 1;
|
||||
|
||||
TRACE("Finishing SMU doorbell ! num_interrupt_controllers = %d\n",
|
||||
num_interrupt_controllers);
|
||||
|
||||
if (num_interrupt_controllers == 0) {
|
||||
/*
|
||||
@ -345,11 +342,12 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
}
|
||||
|
||||
ints = (unsigned int *) get_property(np, "interrupts", &intlen);
|
||||
TRACE("ints=%p, intlen=%d\n", ints, intlen);
|
||||
if (ints == NULL)
|
||||
return 0;
|
||||
intrcells = prom_n_intr_cells(np);
|
||||
intlen /= intrcells * sizeof(unsigned int);
|
||||
|
||||
TRACE("intrcells=%d, new intlen=%d\n", intrcells, intlen);
|
||||
np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start);
|
||||
if (!np->intrs)
|
||||
return -ENOMEM;
|
||||
@ -360,6 +358,7 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
intrcount = 0;
|
||||
for (i = 0; i < intlen; ++i, ints += intrcells) {
|
||||
n = map_interrupt(&irq, &ic, np, ints, intrcells);
|
||||
TRACE("map, irq=%d, ic=%p, n=%d\n", irq, ic, n);
|
||||
if (n <= 0)
|
||||
continue;
|
||||
|
||||
@ -370,6 +369,7 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
np->intrs[intrcount].sense = map_isa_senses[sense];
|
||||
} else {
|
||||
virq = virt_irq_create_mapping(irq[0]);
|
||||
TRACE("virq=%d\n", virq);
|
||||
#ifdef CONFIG_PPC64
|
||||
if (virq == NO_IRQ) {
|
||||
printk(KERN_CRIT "Could not allocate interrupt"
|
||||
@ -379,6 +379,12 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
#endif
|
||||
np->intrs[intrcount].line = irq_offset_up(virq);
|
||||
sense = (n > 1)? (irq[1] & 3): 1;
|
||||
|
||||
/* Apple uses bits in there in a different way, let's
|
||||
* only keep the real sense bit on macs
|
||||
*/
|
||||
if (_machine == PLATFORM_POWERMAC)
|
||||
sense &= 0x1;
|
||||
np->intrs[intrcount].sense = map_mpic_senses[sense];
|
||||
}
|
||||
|
||||
@ -388,12 +394,13 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
char *name = get_property(ic->parent, "name", NULL);
|
||||
if (name && !strcmp(name, "u3"))
|
||||
np->intrs[intrcount].line += 128;
|
||||
else if (!(name && !strcmp(name, "mac-io")))
|
||||
else if (!(name && (!strcmp(name, "mac-io") ||
|
||||
!strcmp(name, "u4"))))
|
||||
/* ignore other cascaded controllers, such as
|
||||
the k2-sata-root */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_PPC64 */
|
||||
if (n > 2) {
|
||||
printk("hmmm, got %d intr cells for %s:", n,
|
||||
np->full_name);
|
||||
@ -408,234 +415,19 @@ static int __devinit finish_node_interrupts(struct device_node *np,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit interpret_pci_props(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct address_range *adr;
|
||||
struct pci_reg_property *pci_addrs;
|
||||
int i, l, n_addrs;
|
||||
|
||||
pci_addrs = (struct pci_reg_property *)
|
||||
get_property(np, "assigned-addresses", &l);
|
||||
if (!pci_addrs)
|
||||
return 0;
|
||||
|
||||
n_addrs = l / sizeof(*pci_addrs);
|
||||
|
||||
adr = prom_alloc(n_addrs * sizeof(*adr), mem_start);
|
||||
if (!adr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (measure_only)
|
||||
return 0;
|
||||
|
||||
np->addrs = adr;
|
||||
np->n_addrs = n_addrs;
|
||||
|
||||
for (i = 0; i < n_addrs; i++) {
|
||||
adr[i].space = pci_addrs[i].addr.a_hi;
|
||||
adr[i].address = pci_addrs[i].addr.a_lo |
|
||||
((u64)pci_addrs[i].addr.a_mid << 32);
|
||||
adr[i].size = pci_addrs[i].size_lo;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init interpret_dbdma_props(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct reg_property32 *rp;
|
||||
struct address_range *adr;
|
||||
unsigned long base_address;
|
||||
int i, l;
|
||||
struct device_node *db;
|
||||
|
||||
base_address = 0;
|
||||
if (!measure_only) {
|
||||
for (db = np->parent; db != NULL; db = db->parent) {
|
||||
if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
|
||||
base_address = db->addrs[0].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rp = (struct reg_property32 *) get_property(np, "reg", &l);
|
||||
if (rp != 0 && l >= sizeof(struct reg_property32)) {
|
||||
i = 0;
|
||||
adr = (struct address_range *) (*mem_start);
|
||||
while ((l -= sizeof(struct reg_property32)) >= 0) {
|
||||
if (!measure_only) {
|
||||
adr[i].space = 2;
|
||||
adr[i].address = rp[i].address + base_address;
|
||||
adr[i].size = rp[i].size;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
np->addrs = adr;
|
||||
np->n_addrs = i;
|
||||
(*mem_start) += i * sizeof(struct address_range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init interpret_macio_props(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct reg_property32 *rp;
|
||||
struct address_range *adr;
|
||||
unsigned long base_address;
|
||||
int i, l;
|
||||
struct device_node *db;
|
||||
|
||||
base_address = 0;
|
||||
if (!measure_only) {
|
||||
for (db = np->parent; db != NULL; db = db->parent) {
|
||||
if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
|
||||
base_address = db->addrs[0].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rp = (struct reg_property32 *) get_property(np, "reg", &l);
|
||||
if (rp != 0 && l >= sizeof(struct reg_property32)) {
|
||||
i = 0;
|
||||
adr = (struct address_range *) (*mem_start);
|
||||
while ((l -= sizeof(struct reg_property32)) >= 0) {
|
||||
if (!measure_only) {
|
||||
adr[i].space = 2;
|
||||
adr[i].address = rp[i].address + base_address;
|
||||
adr[i].size = rp[i].size;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
np->addrs = adr;
|
||||
np->n_addrs = i;
|
||||
(*mem_start) += i * sizeof(struct address_range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init interpret_isa_props(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct isa_reg_property *rp;
|
||||
struct address_range *adr;
|
||||
int i, l;
|
||||
|
||||
rp = (struct isa_reg_property *) get_property(np, "reg", &l);
|
||||
if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
|
||||
i = 0;
|
||||
adr = (struct address_range *) (*mem_start);
|
||||
while ((l -= sizeof(struct isa_reg_property)) >= 0) {
|
||||
if (!measure_only) {
|
||||
adr[i].space = rp[i].space;
|
||||
adr[i].address = rp[i].address;
|
||||
adr[i].size = rp[i].size;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
np->addrs = adr;
|
||||
np->n_addrs = i;
|
||||
(*mem_start) += i * sizeof(struct address_range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init interpret_root_props(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct address_range *adr;
|
||||
int i, l;
|
||||
unsigned int *rp;
|
||||
int rpsize = (naddrc + nsizec) * sizeof(unsigned int);
|
||||
|
||||
rp = (unsigned int *) get_property(np, "reg", &l);
|
||||
if (rp != 0 && l >= rpsize) {
|
||||
i = 0;
|
||||
adr = (struct address_range *) (*mem_start);
|
||||
while ((l -= rpsize) >= 0) {
|
||||
if (!measure_only) {
|
||||
adr[i].space = 0;
|
||||
adr[i].address = rp[naddrc - 1];
|
||||
adr[i].size = rp[naddrc + nsizec - 1];
|
||||
}
|
||||
++i;
|
||||
rp += naddrc + nsizec;
|
||||
}
|
||||
np->addrs = adr;
|
||||
np->n_addrs = i;
|
||||
(*mem_start) += i * sizeof(struct address_range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit finish_node(struct device_node *np,
|
||||
unsigned long *mem_start,
|
||||
interpret_func *ifunc,
|
||||
int naddrc, int nsizec,
|
||||
int measure_only)
|
||||
{
|
||||
struct device_node *child;
|
||||
int *ip, rc = 0;
|
||||
|
||||
/* get the device addresses and interrupts */
|
||||
if (ifunc != NULL)
|
||||
rc = ifunc(np, mem_start, naddrc, nsizec, measure_only);
|
||||
if (rc)
|
||||
goto out;
|
||||
int rc = 0;
|
||||
|
||||
rc = finish_node_interrupts(np, mem_start, measure_only);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* Look for #address-cells and #size-cells properties. */
|
||||
ip = (int *) get_property(np, "#address-cells", NULL);
|
||||
if (ip != NULL)
|
||||
naddrc = *ip;
|
||||
ip = (int *) get_property(np, "#size-cells", NULL);
|
||||
if (ip != NULL)
|
||||
nsizec = *ip;
|
||||
|
||||
if (!strcmp(np->name, "device-tree") || np->parent == NULL)
|
||||
ifunc = interpret_root_props;
|
||||
else if (np->type == 0)
|
||||
ifunc = NULL;
|
||||
else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
|
||||
ifunc = interpret_pci_props;
|
||||
else if (!strcmp(np->type, "dbdma"))
|
||||
ifunc = interpret_dbdma_props;
|
||||
else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props)
|
||||
ifunc = interpret_macio_props;
|
||||
else if (!strcmp(np->type, "isa"))
|
||||
ifunc = interpret_isa_props;
|
||||
else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3"))
|
||||
ifunc = interpret_root_props;
|
||||
else if (!((ifunc == interpret_dbdma_props
|
||||
|| ifunc == interpret_macio_props)
|
||||
&& (!strcmp(np->type, "escc")
|
||||
|| !strcmp(np->type, "media-bay"))))
|
||||
ifunc = NULL;
|
||||
|
||||
for (child = np->child; child != NULL; child = child->sibling) {
|
||||
rc = finish_node(child, mem_start, ifunc,
|
||||
naddrc, nsizec, measure_only);
|
||||
rc = finish_node(child, mem_start, measure_only);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
@ -697,10 +489,10 @@ void __init finish_device_tree(void)
|
||||
* reason and then remove those additional 16 bytes
|
||||
*/
|
||||
size = 16;
|
||||
finish_node(allnodes, &size, NULL, 0, 0, 1);
|
||||
finish_node(allnodes, &size, 1);
|
||||
size -= 16;
|
||||
end = start = (unsigned long) __va(lmb_alloc(size, 128));
|
||||
finish_node(allnodes, &end, NULL, 0, 0, 0);
|
||||
finish_node(allnodes, &end, 0);
|
||||
BUG_ON(end != start + size);
|
||||
|
||||
DBG(" <- finish_device_tree\n");
|
||||
@ -1197,6 +989,16 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
|
||||
}
|
||||
#endif /* CONFIG_PPC_RTAS */
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-base", NULL);
|
||||
if (lprop)
|
||||
crashk_res.start = *lprop;
|
||||
|
||||
lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-size", NULL);
|
||||
if (lprop)
|
||||
crashk_res.end = crashk_res.start + *lprop - 1;
|
||||
#endif
|
||||
|
||||
/* break now */
|
||||
return 1;
|
||||
}
|
||||
@ -1263,7 +1065,9 @@ static int __init early_init_dt_scan_memory(unsigned long node,
|
||||
} else if (strcmp(type, "memory") != 0)
|
||||
return 0;
|
||||
|
||||
reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l);
|
||||
reg = (cell_t *)of_get_flat_dt_prop(node, "linux,usable-memory", &l);
|
||||
if (reg == NULL)
|
||||
reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l);
|
||||
if (reg == NULL)
|
||||
return 0;
|
||||
|
||||
@ -1335,11 +1139,14 @@ void __init early_init_devtree(void *params)
|
||||
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
|
||||
lmb_enforce_memory_limit(memory_limit);
|
||||
lmb_analyze();
|
||||
lmb_reserve(0, __pa(klimit));
|
||||
|
||||
DBG("Phys. mem: %lx\n", lmb_phys_mem_size());
|
||||
|
||||
/* Reserve LMB regions used by kernel, initrd, dt, etc... */
|
||||
lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
lmb_reserve(0, KDUMP_RESERVE_LIMIT);
|
||||
#endif
|
||||
early_reserve_mem();
|
||||
|
||||
DBG("Scanning CPUs ...\n");
|
||||
@ -1802,7 +1609,6 @@ static void of_node_release(struct kref *kref)
|
||||
prop = next;
|
||||
}
|
||||
kfree(node->intrs);
|
||||
kfree(node->addrs);
|
||||
kfree(node->full_name);
|
||||
kfree(node->data);
|
||||
kfree(node);
|
||||
@ -1884,9 +1690,7 @@ void of_detach_node(const struct device_node *np)
|
||||
* This should probably be split up into smaller chunks.
|
||||
*/
|
||||
|
||||
static int of_finish_dynamic_node(struct device_node *node,
|
||||
unsigned long *unused1, int unused2,
|
||||
int unused3, int unused4)
|
||||
static int of_finish_dynamic_node(struct device_node *node)
|
||||
{
|
||||
struct device_node *parent = of_get_parent(node);
|
||||
int err = 0;
|
||||
@ -1907,7 +1711,8 @@ static int of_finish_dynamic_node(struct device_node *node,
|
||||
return -ENODEV;
|
||||
|
||||
/* fix up new node's linux_phandle field */
|
||||
if ((ibm_phandle = (unsigned int *)get_property(node, "ibm,phandle", NULL)))
|
||||
if ((ibm_phandle = (unsigned int *)get_property(node,
|
||||
"ibm,phandle", NULL)))
|
||||
node->linux_phandle = *ibm_phandle;
|
||||
|
||||
out:
|
||||
@ -1922,7 +1727,9 @@ static int prom_reconfig_notifier(struct notifier_block *nb,
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_ADD:
|
||||
err = finish_node(node, NULL, of_finish_dynamic_node, 0, 0, 0);
|
||||
err = of_finish_dynamic_node(node);
|
||||
if (!err)
|
||||
finish_node(node, NULL, 0);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "finish_node returned %d\n", err);
|
||||
err = NOTIFY_BAD;
|
||||
@ -1996,175 +1803,4 @@ int prom_add_property(struct device_node* np, struct property* prop)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I quickly hacked that one, check against spec ! */
|
||||
static inline unsigned long
|
||||
bus_space_to_resource_flags(unsigned int bus_space)
|
||||
{
|
||||
u8 space = (bus_space >> 24) & 0xf;
|
||||
if (space == 0)
|
||||
space = 0x02;
|
||||
if (space == 0x02)
|
||||
return IORESOURCE_MEM;
|
||||
else if (space == 0x01)
|
||||
return IORESOURCE_IO;
|
||||
else {
|
||||
printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
|
||||
bus_space);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static struct resource *find_parent_pci_resource(struct pci_dev* pdev,
|
||||
struct address_range *range)
|
||||
{
|
||||
unsigned long mask;
|
||||
int i;
|
||||
|
||||
/* Check this one */
|
||||
mask = bus_space_to_resource_flags(range->space);
|
||||
for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
|
||||
if ((pdev->resource[i].flags & mask) == mask &&
|
||||
pdev->resource[i].start <= range->address &&
|
||||
pdev->resource[i].end > range->address) {
|
||||
if ((range->address + range->size - 1) > pdev->resource[i].end) {
|
||||
/* Add better message */
|
||||
printk(KERN_WARNING "PCI/OF resource overlap !\n");
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == DEVICE_COUNT_RESOURCE)
|
||||
return NULL;
|
||||
return &pdev->resource[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Request an OF device resource. Currently handles child of PCI devices,
|
||||
* or other nodes attached to the root node. Ultimately, put some
|
||||
* link to resources in the OF node.
|
||||
*/
|
||||
struct resource *request_OF_resource(struct device_node* node, int index,
|
||||
const char* name_postfix)
|
||||
{
|
||||
struct pci_dev* pcidev;
|
||||
u8 pci_bus, pci_devfn;
|
||||
unsigned long iomask;
|
||||
struct device_node* nd;
|
||||
struct resource* parent;
|
||||
struct resource *res = NULL;
|
||||
int nlen, plen;
|
||||
|
||||
if (index >= node->n_addrs)
|
||||
goto fail;
|
||||
|
||||
/* Sanity check on bus space */
|
||||
iomask = bus_space_to_resource_flags(node->addrs[index].space);
|
||||
if (iomask & IORESOURCE_MEM)
|
||||
parent = &iomem_resource;
|
||||
else if (iomask & IORESOURCE_IO)
|
||||
parent = &ioport_resource;
|
||||
else
|
||||
goto fail;
|
||||
|
||||
/* Find a PCI parent if any */
|
||||
nd = node;
|
||||
pcidev = NULL;
|
||||
while (nd) {
|
||||
if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
|
||||
pcidev = pci_find_slot(pci_bus, pci_devfn);
|
||||
if (pcidev) break;
|
||||
nd = nd->parent;
|
||||
}
|
||||
if (pcidev)
|
||||
parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
|
||||
if (!parent) {
|
||||
printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
|
||||
node->name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = __request_region(parent, node->addrs[index].address,
|
||||
node->addrs[index].size, NULL);
|
||||
if (!res)
|
||||
goto fail;
|
||||
nlen = strlen(node->name);
|
||||
plen = name_postfix ? strlen(name_postfix) : 0;
|
||||
res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
|
||||
if (res->name) {
|
||||
strcpy((char *)res->name, node->name);
|
||||
if (plen)
|
||||
strcpy((char *)res->name+nlen, name_postfix);
|
||||
}
|
||||
return res;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(request_OF_resource);
|
||||
|
||||
int release_OF_resource(struct device_node *node, int index)
|
||||
{
|
||||
struct pci_dev* pcidev;
|
||||
u8 pci_bus, pci_devfn;
|
||||
unsigned long iomask, start, end;
|
||||
struct device_node* nd;
|
||||
struct resource* parent;
|
||||
struct resource *res = NULL;
|
||||
|
||||
if (index >= node->n_addrs)
|
||||
return -EINVAL;
|
||||
|
||||
/* Sanity check on bus space */
|
||||
iomask = bus_space_to_resource_flags(node->addrs[index].space);
|
||||
if (iomask & IORESOURCE_MEM)
|
||||
parent = &iomem_resource;
|
||||
else if (iomask & IORESOURCE_IO)
|
||||
parent = &ioport_resource;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Find a PCI parent if any */
|
||||
nd = node;
|
||||
pcidev = NULL;
|
||||
while(nd) {
|
||||
if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
|
||||
pcidev = pci_find_slot(pci_bus, pci_devfn);
|
||||
if (pcidev) break;
|
||||
nd = nd->parent;
|
||||
}
|
||||
if (pcidev)
|
||||
parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
|
||||
if (!parent) {
|
||||
printk(KERN_WARNING "release_OF_resource(%s), parent not found\n",
|
||||
node->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Find us in the parent and its childs */
|
||||
res = parent->child;
|
||||
start = node->addrs[index].address;
|
||||
end = start + node->addrs[index].size - 1;
|
||||
while (res) {
|
||||
if (res->start == start && res->end == end &&
|
||||
(res->flags & IORESOURCE_BUSY))
|
||||
break;
|
||||
if (res->start <= start && res->end >= end)
|
||||
res = res->child;
|
||||
else
|
||||
res = res->sibling;
|
||||
}
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
if (res->name) {
|
||||
kfree(res->name);
|
||||
res->name = NULL;
|
||||
}
|
||||
release_resource(res);
|
||||
kfree(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(release_OF_resource);
|
||||
#endif /* CONFIG_PCI */
|
||||
|
@ -192,6 +192,11 @@ static unsigned long __initdata alloc_bottom;
|
||||
static unsigned long __initdata rmo_top;
|
||||
static unsigned long __initdata ram_top;
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
static unsigned long __initdata prom_crashk_base;
|
||||
static unsigned long __initdata prom_crashk_size;
|
||||
#endif
|
||||
|
||||
static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE];
|
||||
static int __initdata mem_reserve_cnt;
|
||||
|
||||
@ -553,7 +558,8 @@ unsigned long prom_memparse(const char *ptr, const char **retptr)
|
||||
static void __init early_cmdline_parse(void)
|
||||
{
|
||||
struct prom_t *_prom = &RELOC(prom);
|
||||
char *opt, *p;
|
||||
const char *opt;
|
||||
char *p;
|
||||
int l = 0;
|
||||
|
||||
RELOC(prom_cmd_line[0]) = 0;
|
||||
@ -590,6 +596,34 @@ static void __init early_cmdline_parse(void)
|
||||
RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
/*
|
||||
* crashkernel=size@addr specifies the location to reserve for
|
||||
* crash kernel.
|
||||
*/
|
||||
opt = strstr(RELOC(prom_cmd_line), RELOC("crashkernel="));
|
||||
if (opt) {
|
||||
opt += 12;
|
||||
RELOC(prom_crashk_size) = prom_memparse(opt, &opt);
|
||||
|
||||
if (ALIGN(RELOC(prom_crashk_size), 0x1000000) !=
|
||||
RELOC(prom_crashk_size)) {
|
||||
prom_printf("Warning: crashkernel size is not "
|
||||
"aligned to 16MB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* At present, the crash kernel always run at 32MB.
|
||||
* Just ignore whatever user passed.
|
||||
*/
|
||||
RELOC(prom_crashk_base) = 0x2000000;
|
||||
if (*opt == '@') {
|
||||
prom_printf("Warning: PPC64 kdump kernel always runs "
|
||||
"at 32 MB\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
@ -1011,6 +1045,12 @@ static void __init prom_init_mem(void)
|
||||
prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high));
|
||||
prom_printf(" rmo_top : %x\n", RELOC(rmo_top));
|
||||
prom_printf(" ram_top : %x\n", RELOC(ram_top));
|
||||
#ifdef CONFIG_KEXEC
|
||||
if (RELOC(prom_crashk_base)) {
|
||||
prom_printf(" crashk_base : %x\n", RELOC(prom_crashk_base));
|
||||
prom_printf(" crashk_size : %x\n", RELOC(prom_crashk_size));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1500,6 +1540,8 @@ static int __init prom_find_machine_type(void)
|
||||
#ifdef CONFIG_PPC64
|
||||
if (strstr(p, RELOC("Momentum,Maple")))
|
||||
return PLATFORM_MAPLE;
|
||||
if (strstr(p, RELOC("IBM,CPB")))
|
||||
return PLATFORM_CELL;
|
||||
#endif
|
||||
i += sl + 1;
|
||||
}
|
||||
@ -1994,7 +2036,7 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
|
||||
if (r3 && r4 && r4 != 0xdeadbeef) {
|
||||
unsigned long val;
|
||||
|
||||
RELOC(prom_initrd_start) = (r3 >= KERNELBASE) ? __pa(r3) : r3;
|
||||
RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3;
|
||||
RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4;
|
||||
|
||||
val = RELOC(prom_initrd_start);
|
||||
@ -2094,6 +2136,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
|
||||
*/
|
||||
prom_init_mem();
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
if (RELOC(prom_crashk_base))
|
||||
reserve_mem(RELOC(prom_crashk_base), RELOC(prom_crashk_size));
|
||||
#endif
|
||||
/*
|
||||
* Determine which cpu is actually running right _now_
|
||||
*/
|
||||
@ -2150,6 +2196,16 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
if (RELOC(prom_crashk_base)) {
|
||||
prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-base",
|
||||
PTRRELOC(&prom_crashk_base),
|
||||
sizeof(RELOC(prom_crashk_base)));
|
||||
prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-size",
|
||||
PTRRELOC(&prom_crashk_size),
|
||||
sizeof(RELOC(prom_crashk_size)));
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Fixup any known bugs in the device-tree
|
||||
*/
|
||||
|
547
arch/powerpc/kernel/prom_parse.c
Normal file
547
arch/powerpc/kernel/prom_parse.c
Normal file
@ -0,0 +1,547 @@
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) do { printk(fmt); } while(0)
|
||||
#else
|
||||
#define DBG(fmt...) do { } while(0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
#define PRu64 "%lx"
|
||||
#else
|
||||
#define PRu64 "%llx"
|
||||
#endif
|
||||
|
||||
/* Max address size we deal with */
|
||||
#define OF_MAX_ADDR_CELLS 4
|
||||
#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
|
||||
(ns) > 0)
|
||||
|
||||
/* Debug utility */
|
||||
#ifdef DEBUG
|
||||
static void of_dump_addr(const char *s, u32 *addr, int na)
|
||||
{
|
||||
printk("%s", s);
|
||||
while(na--)
|
||||
printk(" %08x", *(addr++));
|
||||
printk("\n");
|
||||
}
|
||||
#else
|
||||
static void of_dump_addr(const char *s, u32 *addr, int na) { }
|
||||
#endif
|
||||
|
||||
/* Read a big address */
|
||||
static inline u64 of_read_addr(u32 *cell, int size)
|
||||
{
|
||||
u64 r = 0;
|
||||
while (size--)
|
||||
r = (r << 32) | *(cell++);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Callbacks for bus specific translators */
|
||||
struct of_bus {
|
||||
const char *name;
|
||||
const char *addresses;
|
||||
int (*match)(struct device_node *parent);
|
||||
void (*count_cells)(struct device_node *child,
|
||||
int *addrc, int *sizec);
|
||||
u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna);
|
||||
int (*translate)(u32 *addr, u64 offset, int na);
|
||||
unsigned int (*get_flags)(u32 *addr);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Default translator (generic bus)
|
||||
*/
|
||||
|
||||
static void of_bus_default_count_cells(struct device_node *dev,
|
||||
int *addrc, int *sizec)
|
||||
{
|
||||
if (addrc)
|
||||
*addrc = prom_n_addr_cells(dev);
|
||||
if (sizec)
|
||||
*sizec = prom_n_size_cells(dev);
|
||||
}
|
||||
|
||||
static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
|
||||
{
|
||||
u64 cp, s, da;
|
||||
|
||||
cp = of_read_addr(range, na);
|
||||
s = of_read_addr(range + na + pna, ns);
|
||||
da = of_read_addr(addr, na);
|
||||
|
||||
DBG("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n",
|
||||
cp, s, da);
|
||||
|
||||
if (da < cp || da >= (cp + s))
|
||||
return OF_BAD_ADDR;
|
||||
return da - cp;
|
||||
}
|
||||
|
||||
static int of_bus_default_translate(u32 *addr, u64 offset, int na)
|
||||
{
|
||||
u64 a = of_read_addr(addr, na);
|
||||
memset(addr, 0, na * 4);
|
||||
a += offset;
|
||||
if (na > 1)
|
||||
addr[na - 2] = a >> 32;
|
||||
addr[na - 1] = a & 0xffffffffu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int of_bus_default_get_flags(u32 *addr)
|
||||
{
|
||||
return IORESOURCE_MEM;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PCI bus specific translator
|
||||
*/
|
||||
|
||||
static int of_bus_pci_match(struct device_node *np)
|
||||
{
|
||||
return !strcmp(np->type, "pci");
|
||||
}
|
||||
|
||||
static void of_bus_pci_count_cells(struct device_node *np,
|
||||
int *addrc, int *sizec)
|
||||
{
|
||||
if (addrc)
|
||||
*addrc = 3;
|
||||
if (sizec)
|
||||
*sizec = 2;
|
||||
}
|
||||
|
||||
static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna)
|
||||
{
|
||||
u64 cp, s, da;
|
||||
|
||||
/* Check address type match */
|
||||
if ((addr[0] ^ range[0]) & 0x03000000)
|
||||
return OF_BAD_ADDR;
|
||||
|
||||
/* Read address values, skipping high cell */
|
||||
cp = of_read_addr(range + 1, na - 1);
|
||||
s = of_read_addr(range + na + pna, ns);
|
||||
da = of_read_addr(addr + 1, na - 1);
|
||||
|
||||
DBG("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da);
|
||||
|
||||
if (da < cp || da >= (cp + s))
|
||||
return OF_BAD_ADDR;
|
||||
return da - cp;
|
||||
}
|
||||
|
||||
static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
|
||||
{
|
||||
return of_bus_default_translate(addr + 1, offset, na - 1);
|
||||
}
|
||||
|
||||
static unsigned int of_bus_pci_get_flags(u32 *addr)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
u32 w = addr[0];
|
||||
|
||||
switch((w >> 24) & 0x03) {
|
||||
case 0x01:
|
||||
flags |= IORESOURCE_IO;
|
||||
case 0x02: /* 32 bits */
|
||||
case 0x03: /* 64 bits */
|
||||
flags |= IORESOURCE_MEM;
|
||||
}
|
||||
if (w & 0x40000000)
|
||||
flags |= IORESOURCE_PREFETCH;
|
||||
return flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* ISA bus specific translator
|
||||
*/
|
||||
|
||||
static int of_bus_isa_match(struct device_node *np)
|
||||
{
|
||||
return !strcmp(np->name, "isa");
|
||||
}
|
||||
|
||||
static void of_bus_isa_count_cells(struct device_node *child,
|
||||
int *addrc, int *sizec)
|
||||
{
|
||||
if (addrc)
|
||||
*addrc = 2;
|
||||
if (sizec)
|
||||
*sizec = 1;
|
||||
}
|
||||
|
||||
static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna)
|
||||
{
|
||||
u64 cp, s, da;
|
||||
|
||||
/* Check address type match */
|
||||
if ((addr[0] ^ range[0]) & 0x00000001)
|
||||
return OF_BAD_ADDR;
|
||||
|
||||
/* Read address values, skipping high cell */
|
||||
cp = of_read_addr(range + 1, na - 1);
|
||||
s = of_read_addr(range + na + pna, ns);
|
||||
da = of_read_addr(addr + 1, na - 1);
|
||||
|
||||
DBG("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da);
|
||||
|
||||
if (da < cp || da >= (cp + s))
|
||||
return OF_BAD_ADDR;
|
||||
return da - cp;
|
||||
}
|
||||
|
||||
static int of_bus_isa_translate(u32 *addr, u64 offset, int na)
|
||||
{
|
||||
return of_bus_default_translate(addr + 1, offset, na - 1);
|
||||
}
|
||||
|
||||
static unsigned int of_bus_isa_get_flags(u32 *addr)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
u32 w = addr[0];
|
||||
|
||||
if (w & 1)
|
||||
flags |= IORESOURCE_IO;
|
||||
else
|
||||
flags |= IORESOURCE_MEM;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Array of bus specific translators
|
||||
*/
|
||||
|
||||
static struct of_bus of_busses[] = {
|
||||
/* PCI */
|
||||
{
|
||||
.name = "pci",
|
||||
.addresses = "assigned-addresses",
|
||||
.match = of_bus_pci_match,
|
||||
.count_cells = of_bus_pci_count_cells,
|
||||
.map = of_bus_pci_map,
|
||||
.translate = of_bus_pci_translate,
|
||||
.get_flags = of_bus_pci_get_flags,
|
||||
},
|
||||
/* ISA */
|
||||
{
|
||||
.name = "isa",
|
||||
.addresses = "reg",
|
||||
.match = of_bus_isa_match,
|
||||
.count_cells = of_bus_isa_count_cells,
|
||||
.map = of_bus_isa_map,
|
||||
.translate = of_bus_isa_translate,
|
||||
.get_flags = of_bus_isa_get_flags,
|
||||
},
|
||||
/* Default */
|
||||
{
|
||||
.name = "default",
|
||||
.addresses = "reg",
|
||||
.match = NULL,
|
||||
.count_cells = of_bus_default_count_cells,
|
||||
.map = of_bus_default_map,
|
||||
.translate = of_bus_default_translate,
|
||||
.get_flags = of_bus_default_get_flags,
|
||||
},
|
||||
};
|
||||
|
||||
static struct of_bus *of_match_bus(struct device_node *np)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
|
||||
if (!of_busses[i].match || of_busses[i].match(np))
|
||||
return &of_busses[i];
|
||||
BUG();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
|
||||
struct of_bus *pbus, u32 *addr,
|
||||
int na, int ns, int pna)
|
||||
{
|
||||
u32 *ranges;
|
||||
unsigned int rlen;
|
||||
int rone;
|
||||
u64 offset = OF_BAD_ADDR;
|
||||
|
||||
/* Normally, an absence of a "ranges" property means we are
|
||||
* crossing a non-translatable boundary, and thus the addresses
|
||||
* below the current not cannot be converted to CPU physical ones.
|
||||
* Unfortunately, while this is very clear in the spec, it's not
|
||||
* what Apple understood, and they do have things like /uni-n or
|
||||
* /ht nodes with no "ranges" property and a lot of perfectly
|
||||
* useable mapped devices below them. Thus we treat the absence of
|
||||
* "ranges" as equivalent to an empty "ranges" property which means
|
||||
* a 1:1 translation at that level. It's up to the caller not to try
|
||||
* to translate addresses that aren't supposed to be translated in
|
||||
* the first place. --BenH.
|
||||
*/
|
||||
ranges = (u32 *)get_property(parent, "ranges", &rlen);
|
||||
if (ranges == NULL || rlen == 0) {
|
||||
offset = of_read_addr(addr, na);
|
||||
memset(addr, 0, pna * 4);
|
||||
DBG("OF: no ranges, 1:1 translation\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
DBG("OF: walking ranges...\n");
|
||||
|
||||
/* Now walk through the ranges */
|
||||
rlen /= 4;
|
||||
rone = na + pna + ns;
|
||||
for (; rlen >= rone; rlen -= rone, ranges += rone) {
|
||||
offset = bus->map(addr, ranges, na, ns, pna);
|
||||
if (offset != OF_BAD_ADDR)
|
||||
break;
|
||||
}
|
||||
if (offset == OF_BAD_ADDR) {
|
||||
DBG("OF: not found !\n");
|
||||
return 1;
|
||||
}
|
||||
memcpy(addr, ranges + na, 4 * pna);
|
||||
|
||||
finish:
|
||||
of_dump_addr("OF: parent translation for:", addr, pna);
|
||||
DBG("OF: with offset: "PRu64"\n", offset);
|
||||
|
||||
/* Translate it into parent bus space */
|
||||
return pbus->translate(addr, offset, pna);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Translate an address from the device-tree into a CPU physical address,
|
||||
* this walks up the tree and applies the various bus mappings on the
|
||||
* way.
|
||||
*
|
||||
* Note: We consider that crossing any level with #size-cells == 0 to mean
|
||||
* that translation is impossible (that is we are not dealing with a value
|
||||
* that can be mapped to a cpu physical address). This is not really specified
|
||||
* that way, but this is traditionally the way IBM at least do things
|
||||
*/
|
||||
u64 of_translate_address(struct device_node *dev, u32 *in_addr)
|
||||
{
|
||||
struct device_node *parent = NULL;
|
||||
struct of_bus *bus, *pbus;
|
||||
u32 addr[OF_MAX_ADDR_CELLS];
|
||||
int na, ns, pna, pns;
|
||||
u64 result = OF_BAD_ADDR;
|
||||
|
||||
DBG("OF: ** translation for device %s **\n", dev->full_name);
|
||||
|
||||
/* Increase refcount at current level */
|
||||
of_node_get(dev);
|
||||
|
||||
/* Get parent & match bus type */
|
||||
parent = of_get_parent(dev);
|
||||
if (parent == NULL)
|
||||
goto bail;
|
||||
bus = of_match_bus(parent);
|
||||
|
||||
/* Cound address cells & copy address locally */
|
||||
bus->count_cells(dev, &na, &ns);
|
||||
if (!OF_CHECK_COUNTS(na, ns)) {
|
||||
printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
|
||||
dev->full_name);
|
||||
goto bail;
|
||||
}
|
||||
memcpy(addr, in_addr, na * 4);
|
||||
|
||||
DBG("OF: bus is %s (na=%d, ns=%d) on %s\n",
|
||||
bus->name, na, ns, parent->full_name);
|
||||
of_dump_addr("OF: translating address:", addr, na);
|
||||
|
||||
/* Translate */
|
||||
for (;;) {
|
||||
/* Switch to parent bus */
|
||||
of_node_put(dev);
|
||||
dev = parent;
|
||||
parent = of_get_parent(dev);
|
||||
|
||||
/* If root, we have finished */
|
||||
if (parent == NULL) {
|
||||
DBG("OF: reached root node\n");
|
||||
result = of_read_addr(addr, na);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get new parent bus and counts */
|
||||
pbus = of_match_bus(parent);
|
||||
pbus->count_cells(dev, &pna, &pns);
|
||||
if (!OF_CHECK_COUNTS(pna, pns)) {
|
||||
printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
|
||||
dev->full_name);
|
||||
break;
|
||||
}
|
||||
|
||||
DBG("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
|
||||
pbus->name, pna, pns, parent->full_name);
|
||||
|
||||
/* Apply bus translation */
|
||||
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna))
|
||||
break;
|
||||
|
||||
/* Complete the move up one level */
|
||||
na = pna;
|
||||
ns = pns;
|
||||
bus = pbus;
|
||||
|
||||
of_dump_addr("OF: one level translation:", addr, na);
|
||||
}
|
||||
bail:
|
||||
of_node_put(parent);
|
||||
of_node_put(dev);
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(of_translate_address);
|
||||
|
||||
u32 *of_get_address(struct device_node *dev, int index, u64 *size,
|
||||
unsigned int *flags)
|
||||
{
|
||||
u32 *prop;
|
||||
unsigned int psize;
|
||||
struct device_node *parent;
|
||||
struct of_bus *bus;
|
||||
int onesize, i, na, ns;
|
||||
|
||||
/* Get parent & match bus type */
|
||||
parent = of_get_parent(dev);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
bus = of_match_bus(parent);
|
||||
bus->count_cells(dev, &na, &ns);
|
||||
of_node_put(parent);
|
||||
if (!OF_CHECK_COUNTS(na, ns))
|
||||
return NULL;
|
||||
|
||||
/* Get "reg" or "assigned-addresses" property */
|
||||
prop = (u32 *)get_property(dev, bus->addresses, &psize);
|
||||
if (prop == NULL)
|
||||
return NULL;
|
||||
psize /= 4;
|
||||
|
||||
onesize = na + ns;
|
||||
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
|
||||
if (i == index) {
|
||||
if (size)
|
||||
*size = of_read_addr(prop + na, ns);
|
||||
if (flags)
|
||||
*flags = bus->get_flags(prop);
|
||||
return prop;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_address);
|
||||
|
||||
u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
|
||||
unsigned int *flags)
|
||||
{
|
||||
u32 *prop;
|
||||
unsigned int psize;
|
||||
struct device_node *parent;
|
||||
struct of_bus *bus;
|
||||
int onesize, i, na, ns;
|
||||
|
||||
/* Get parent & match bus type */
|
||||
parent = of_get_parent(dev);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
bus = of_match_bus(parent);
|
||||
if (strcmp(bus->name, "pci"))
|
||||
return NULL;
|
||||
bus->count_cells(dev, &na, &ns);
|
||||
of_node_put(parent);
|
||||
if (!OF_CHECK_COUNTS(na, ns))
|
||||
return NULL;
|
||||
|
||||
/* Get "reg" or "assigned-addresses" property */
|
||||
prop = (u32 *)get_property(dev, bus->addresses, &psize);
|
||||
if (prop == NULL)
|
||||
return NULL;
|
||||
psize /= 4;
|
||||
|
||||
onesize = na + ns;
|
||||
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
|
||||
if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) {
|
||||
if (size)
|
||||
*size = of_read_addr(prop + na, ns);
|
||||
if (flags)
|
||||
*flags = bus->get_flags(prop);
|
||||
return prop;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_pci_address);
|
||||
|
||||
static int __of_address_to_resource(struct device_node *dev, u32 *addrp,
|
||||
u64 size, unsigned int flags,
|
||||
struct resource *r)
|
||||
{
|
||||
u64 taddr;
|
||||
|
||||
if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
|
||||
return -EINVAL;
|
||||
taddr = of_translate_address(dev, addrp);
|
||||
if (taddr == OF_BAD_ADDR)
|
||||
return -EINVAL;
|
||||
memset(r, 0, sizeof(struct resource));
|
||||
if (flags & IORESOURCE_IO) {
|
||||
unsigned long port;
|
||||
port = pci_address_to_pio(taddr);
|
||||
if (port == (unsigned long)-1)
|
||||
return -EINVAL;
|
||||
r->start = port;
|
||||
r->end = port + size - 1;
|
||||
} else {
|
||||
r->start = taddr;
|
||||
r->end = taddr + size - 1;
|
||||
}
|
||||
r->flags = flags;
|
||||
r->name = dev->name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int of_address_to_resource(struct device_node *dev, int index,
|
||||
struct resource *r)
|
||||
{
|
||||
u32 *addrp;
|
||||
u64 size;
|
||||
unsigned int flags;
|
||||
|
||||
addrp = of_get_address(dev, index, &size, &flags);
|
||||
if (addrp == NULL)
|
||||
return -EINVAL;
|
||||
return __of_address_to_resource(dev, addrp, size, flags, r);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_address_to_resource);
|
||||
|
||||
int of_pci_address_to_resource(struct device_node *dev, int bar,
|
||||
struct resource *r)
|
||||
{
|
||||
u32 *addrp;
|
||||
u64 size;
|
||||
unsigned int flags;
|
||||
|
||||
addrp = of_get_pci_address(dev, bar, &size, &flags);
|
||||
if (addrp == NULL)
|
||||
return -EINVAL;
|
||||
return __of_address_to_resource(dev, addrp, size, flags, r);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
|
@ -188,39 +188,19 @@ int is_python(struct device_node *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_phb_reg_prop(struct device_node *dev,
|
||||
unsigned int addr_size_words,
|
||||
struct reg_property64 *reg)
|
||||
static void python_countermeasures(struct device_node *dev)
|
||||
{
|
||||
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;
|
||||
struct resource registers;
|
||||
void __iomem *chip_regs;
|
||||
volatile u32 val;
|
||||
|
||||
if (get_phb_reg_prop(dev, addr_size_words, ®_struct))
|
||||
if (of_address_to_resource(dev, 0, ®isters)) {
|
||||
printk(KERN_ERR "Can't get address for Python workarounds !\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Python's register file is 1 MB in size. */
|
||||
chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000);
|
||||
chip_regs = ioremap(registers.start & ~(0xfffffUL), 0x100000);
|
||||
|
||||
/*
|
||||
* Firmware doesn't always clear this bit which is critical
|
||||
@ -301,11 +281,10 @@ static int phb_set_bus_ranges(struct device_node *dev,
|
||||
}
|
||||
|
||||
static int __devinit setup_phb(struct device_node *dev,
|
||||
struct pci_controller *phb,
|
||||
unsigned int addr_size_words)
|
||||
struct pci_controller *phb)
|
||||
{
|
||||
if (is_python(dev))
|
||||
python_countermeasures(dev, addr_size_words);
|
||||
python_countermeasures(dev);
|
||||
|
||||
if (phb_set_bus_ranges(dev, phb))
|
||||
return 1;
|
||||
@ -320,8 +299,8 @@ 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 root_size_cells = 0;
|
||||
unsigned int *opprop = NULL;
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
|
||||
@ -343,10 +322,11 @@ unsigned long __init find_and_init_phbs(void)
|
||||
phb = pcibios_alloc_controller(node);
|
||||
if (!phb)
|
||||
continue;
|
||||
setup_phb(node, phb, root_size_cells);
|
||||
setup_phb(node, phb);
|
||||
pci_process_bridge_OF_ranges(phb, node, 0);
|
||||
pci_setup_phb_io(phb, index == 0);
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
/* XXX This code need serious fixing ... --BenH */
|
||||
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]);
|
||||
@ -381,22 +361,17 @@ unsigned long __init find_and_init_phbs(void)
|
||||
|
||||
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;
|
||||
int primary;
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
|
||||
primary = list_empty(&hose_list);
|
||||
phb = pcibios_alloc_controller(dn);
|
||||
if (!phb)
|
||||
return NULL;
|
||||
setup_phb(dn, phb, root_size_cells);
|
||||
setup_phb(dn, phb);
|
||||
pci_process_bridge_OF_ranges(phb, dn, primary);
|
||||
|
||||
pci_setup_phb_io_dynamic(phb, primary);
|
||||
of_node_put(root);
|
||||
|
||||
pci_devs_phb_init_dynamic(phb);
|
||||
scan_phb(phb);
|
||||
|
@ -93,8 +93,8 @@ EXPORT_SYMBOL(ppc_do_canonicalize_irqs);
|
||||
/* also used by kexec */
|
||||
void machine_shutdown(void)
|
||||
{
|
||||
if (ppc_md.nvram_sync)
|
||||
ppc_md.nvram_sync();
|
||||
if (ppc_md.machine_shutdown)
|
||||
ppc_md.machine_shutdown();
|
||||
}
|
||||
|
||||
void machine_restart(char *cmd)
|
||||
@ -294,129 +294,6 @@ struct seq_operations cpuinfo_op = {
|
||||
.show = show_cpuinfo,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
static int __init set_preferred_console(void)
|
||||
{
|
||||
struct device_node *prom_stdout = NULL;
|
||||
char *name;
|
||||
u32 *spd;
|
||||
int offset = 0;
|
||||
|
||||
DBG(" -> set_preferred_console()\n");
|
||||
|
||||
/* The user has requested a console so this is already set up. */
|
||||
if (strstr(saved_command_line, "console=")) {
|
||||
DBG(" console was specified !\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!of_chosen) {
|
||||
DBG(" of_chosen is NULL !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* We are getting a weird phandle from OF ... */
|
||||
/* ... So use the full path instead */
|
||||
name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
|
||||
if (name == NULL) {
|
||||
DBG(" no linux,stdout-path !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
prom_stdout = of_find_node_by_path(name);
|
||||
if (!prom_stdout) {
|
||||
DBG(" can't find stdout package %s !\n", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
DBG("stdout is %s\n", prom_stdout->full_name);
|
||||
|
||||
name = (char *)get_property(prom_stdout, "name", NULL);
|
||||
if (!name) {
|
||||
DBG(" stdout package has no name !\n");
|
||||
goto not_found;
|
||||
}
|
||||
spd = (u32 *)get_property(prom_stdout, "current-speed", NULL);
|
||||
|
||||
if (0)
|
||||
;
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
else if (strcmp(name, "serial") == 0) {
|
||||
int i;
|
||||
u32 *reg = (u32 *)get_property(prom_stdout, "reg", &i);
|
||||
if (i > 8) {
|
||||
switch (reg[1]) {
|
||||
case 0x3f8:
|
||||
offset = 0;
|
||||
break;
|
||||
case 0x2f8:
|
||||
offset = 1;
|
||||
break;
|
||||
case 0x898:
|
||||
offset = 2;
|
||||
break;
|
||||
case 0x890:
|
||||
offset = 3;
|
||||
break;
|
||||
default:
|
||||
/* We dont recognise the serial port */
|
||||
goto not_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_SERIAL_8250_CONSOLE */
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
else if (strcmp(name, "vty") == 0) {
|
||||
u32 *reg = (u32 *)get_property(prom_stdout, "reg", NULL);
|
||||
char *compat = (char *)get_property(prom_stdout, "compatible", NULL);
|
||||
|
||||
if (reg && compat && (strcmp(compat, "hvterm-protocol") == 0)) {
|
||||
/* Host Virtual Serial Interface */
|
||||
switch (reg[0]) {
|
||||
case 0x30000000:
|
||||
offset = 0;
|
||||
break;
|
||||
case 0x30000001:
|
||||
offset = 1;
|
||||
break;
|
||||
default:
|
||||
goto not_found;
|
||||
}
|
||||
of_node_put(prom_stdout);
|
||||
DBG("Found hvsi console at offset %d\n", offset);
|
||||
return add_preferred_console("hvsi", offset, NULL);
|
||||
} else {
|
||||
/* pSeries LPAR virtual console */
|
||||
of_node_put(prom_stdout);
|
||||
DBG("Found hvc console\n");
|
||||
return add_preferred_console("hvc", 0, NULL);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PPC_PSERIES */
|
||||
#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
|
||||
else if (strcmp(name, "ch-a") == 0)
|
||||
offset = 0;
|
||||
else if (strcmp(name, "ch-b") == 0)
|
||||
offset = 1;
|
||||
#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
|
||||
else
|
||||
goto not_found;
|
||||
of_node_put(prom_stdout);
|
||||
|
||||
DBG("Found serial console at ttyS%d\n", offset);
|
||||
|
||||
if (spd) {
|
||||
static char __initdata opt[16];
|
||||
sprintf(opt, "%d", *spd);
|
||||
return add_preferred_console("ttyS", offset, opt);
|
||||
} else
|
||||
return add_preferred_console("ttyS", offset, NULL);
|
||||
|
||||
not_found:
|
||||
DBG("No preferred console found !\n");
|
||||
of_node_put(prom_stdout);
|
||||
return -ENODEV;
|
||||
}
|
||||
console_initcall(set_preferred_console);
|
||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
|
||||
|
||||
void __init check_for_initrd(void)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
@ -442,7 +319,7 @@ void __init check_for_initrd(void)
|
||||
/* If we were passed an initrd, set the ROOT_DEV properly if the values
|
||||
* look sensible. If not, clear initrd reference.
|
||||
*/
|
||||
if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE &&
|
||||
if (is_kernel_addr(initrd_start) && is_kernel_addr(initrd_end) &&
|
||||
initrd_end > initrd_start)
|
||||
ROOT_DEV = Root_RAM0;
|
||||
else
|
||||
|
@ -39,6 +39,8 @@
|
||||
#include <asm/nvram.h>
|
||||
#include <asm/xmon.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/serial.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
@ -172,12 +174,23 @@ void __init platform_init(void)
|
||||
*/
|
||||
void __init machine_init(unsigned long dt_ptr, unsigned long phys)
|
||||
{
|
||||
/* If btext is enabled, we might have a BAT setup for early display,
|
||||
* thus we do enable some very basic udbg output
|
||||
*/
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
udbg_putc = btext_drawchar;
|
||||
#endif
|
||||
|
||||
/* Do some early initialization based on the flat device tree */
|
||||
early_init_devtree(__va(dt_ptr));
|
||||
|
||||
/* Check default command line */
|
||||
#ifdef CONFIG_CMDLINE
|
||||
strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line));
|
||||
if (cmd_line[0] == 0)
|
||||
strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line));
|
||||
#endif /* CONFIG_CMDLINE */
|
||||
|
||||
/* Base init based on machine type */
|
||||
platform_init();
|
||||
|
||||
#ifdef CONFIG_6xx
|
||||
@ -282,25 +295,22 @@ void __init setup_arch(char **cmdline_p)
|
||||
|
||||
unflatten_device_tree();
|
||||
check_for_initrd();
|
||||
|
||||
if (ppc_md.init_early)
|
||||
ppc_md.init_early();
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250
|
||||
find_legacy_serial_ports();
|
||||
#endif
|
||||
finish_device_tree();
|
||||
|
||||
smp_setup_cpu_maps();
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
init_boot_display();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
/* This could be called "early setup arch", it must be done
|
||||
* now because xmon need it
|
||||
*/
|
||||
if (_machine == _MACH_Pmac)
|
||||
pmac_feature_init(); /* New cool way */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_XMON_DEFAULT
|
||||
xmon_init(1);
|
||||
#endif
|
||||
/* Register early console */
|
||||
register_early_udbg_console();
|
||||
|
||||
#if defined(CONFIG_KGDB)
|
||||
if (ppc_md.kgdb_map_scc)
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/kdump.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pgtable.h>
|
||||
@ -268,6 +269,10 @@ void __init early_setup(unsigned long dt_ptr)
|
||||
}
|
||||
ppc_md = **mach;
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
kdump_setup();
|
||||
#endif
|
||||
|
||||
DBG("Found, Initializing memory management...\n");
|
||||
|
||||
/*
|
||||
@ -317,6 +322,7 @@ void early_setup_secondary(void)
|
||||
void smp_release_cpus(void)
|
||||
{
|
||||
extern unsigned long __secondary_hold_spinloop;
|
||||
unsigned long *ptr;
|
||||
|
||||
DBG(" -> smp_release_cpus()\n");
|
||||
|
||||
@ -327,7 +333,9 @@ void smp_release_cpus(void)
|
||||
* This is useless but harmless on iSeries, secondaries are already
|
||||
* waiting on their paca spinloops. */
|
||||
|
||||
__secondary_hold_spinloop = 1;
|
||||
ptr = (unsigned long *)((unsigned long)&__secondary_hold_spinloop
|
||||
- PHYSICAL_START);
|
||||
*ptr = 1;
|
||||
mb();
|
||||
|
||||
DBG(" <- smp_release_cpus()\n");
|
||||
@ -459,16 +467,21 @@ void __init setup_system(void)
|
||||
*/
|
||||
ppc_md.init_early();
|
||||
|
||||
/*
|
||||
* We can discover serial ports now since the above did setup the
|
||||
* hash table management for us, thus ioremap works. We do that early
|
||||
* so that further code can be debugged
|
||||
*/
|
||||
#ifdef CONFIG_SERIAL_8250
|
||||
find_legacy_serial_ports();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "Finish" the device-tree, that is do the actual parsing of
|
||||
* some of the properties like the interrupt map
|
||||
*/
|
||||
finish_device_tree();
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
init_boot_display();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize xmon
|
||||
*/
|
||||
@ -507,6 +520,9 @@ void __init setup_system(void)
|
||||
ppc64_caches.iline_size);
|
||||
printk("htab_address = 0x%p\n", htab_address);
|
||||
printk("htab_hash_mask = 0x%lx\n", htab_hash_mask);
|
||||
#if PHYSICAL_START > 0
|
||||
printk("physical_start = 0x%x\n", PHYSICAL_START);
|
||||
#endif
|
||||
printk("-----------------------------------------------------\n");
|
||||
|
||||
mm_init_ppc64();
|
||||
@ -657,187 +673,6 @@ void ppc64_terminate_msg(unsigned int src, const char *msg)
|
||||
printk("[terminate]%04x %s\n", src, msg);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PPC_ISERIES
|
||||
/*
|
||||
* This function can be used by platforms to "find" legacy serial ports.
|
||||
* It works for "serial" nodes under an "isa" node, and will try to
|
||||
* respect the "ibm,aix-loc" property if any. It works with up to 8
|
||||
* ports.
|
||||
*/
|
||||
|
||||
#define MAX_LEGACY_SERIAL_PORTS 8
|
||||
static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1];
|
||||
static unsigned int old_serial_count;
|
||||
|
||||
void __init generic_find_legacy_serial_ports(u64 *physport,
|
||||
unsigned int *default_speed)
|
||||
{
|
||||
struct device_node *np;
|
||||
u32 *sizeprop;
|
||||
|
||||
struct isa_reg_property {
|
||||
u32 space;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
struct pci_reg_property {
|
||||
struct pci_address addr;
|
||||
u32 size_hi;
|
||||
u32 size_lo;
|
||||
};
|
||||
|
||||
DBG(" -> generic_find_legacy_serial_port()\n");
|
||||
|
||||
*physport = 0;
|
||||
if (default_speed)
|
||||
*default_speed = 0;
|
||||
|
||||
np = of_find_node_by_path("/");
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
/* First fill our array */
|
||||
for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
|
||||
struct device_node *isa, *pci;
|
||||
struct isa_reg_property *reg;
|
||||
unsigned long phys_size, addr_size, io_base;
|
||||
u32 *rangesp;
|
||||
u32 *interrupts, *clk, *spd;
|
||||
char *typep;
|
||||
int index, rlen, rentsize;
|
||||
|
||||
/* Ok, first check if it's under an "isa" parent */
|
||||
isa = of_get_parent(np);
|
||||
if (!isa || strcmp(isa->name, "isa")) {
|
||||
DBG("%s: no isa parent found\n", np->full_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Now look for an "ibm,aix-loc" property that gives us ordering
|
||||
* if any...
|
||||
*/
|
||||
typep = (char *)get_property(np, "ibm,aix-loc", NULL);
|
||||
|
||||
/* Get the ISA port number */
|
||||
reg = (struct isa_reg_property *)get_property(np, "reg", NULL);
|
||||
if (reg == NULL)
|
||||
goto next_port;
|
||||
/* We assume the interrupt number isn't translated ... */
|
||||
interrupts = (u32 *)get_property(np, "interrupts", NULL);
|
||||
/* get clock freq. if present */
|
||||
clk = (u32 *)get_property(np, "clock-frequency", NULL);
|
||||
/* get default speed if present */
|
||||
spd = (u32 *)get_property(np, "current-speed", NULL);
|
||||
/* Default to locate at end of array */
|
||||
index = old_serial_count; /* end of the array by default */
|
||||
|
||||
/* If we have a location index, then use it */
|
||||
if (typep && *typep == 'S') {
|
||||
index = simple_strtol(typep+1, NULL, 0) - 1;
|
||||
/* if index is out of range, use end of array instead */
|
||||
if (index >= MAX_LEGACY_SERIAL_PORTS)
|
||||
index = old_serial_count;
|
||||
/* if our index is still out of range, that mean that
|
||||
* array is full, we could scan for a free slot but that
|
||||
* make little sense to bother, just skip the port
|
||||
*/
|
||||
if (index >= MAX_LEGACY_SERIAL_PORTS)
|
||||
goto next_port;
|
||||
if (index >= old_serial_count)
|
||||
old_serial_count = index + 1;
|
||||
/* Check if there is a port who already claimed our slot */
|
||||
if (serial_ports[index].iobase != 0) {
|
||||
/* if we still have some room, move it, else override */
|
||||
if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) {
|
||||
DBG("Moved legacy port %d -> %d\n", index,
|
||||
old_serial_count);
|
||||
serial_ports[old_serial_count++] =
|
||||
serial_ports[index];
|
||||
} else {
|
||||
DBG("Replacing legacy port %d\n", index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index >= MAX_LEGACY_SERIAL_PORTS)
|
||||
goto next_port;
|
||||
if (index >= old_serial_count)
|
||||
old_serial_count = index + 1;
|
||||
|
||||
/* Now fill the entry */
|
||||
memset(&serial_ports[index], 0, sizeof(struct plat_serial8250_port));
|
||||
serial_ports[index].uartclk = clk ? *clk : BASE_BAUD * 16;
|
||||
serial_ports[index].iobase = reg->address;
|
||||
serial_ports[index].irq = interrupts ? interrupts[0] : 0;
|
||||
serial_ports[index].flags = ASYNC_BOOT_AUTOCONF;
|
||||
|
||||
DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n",
|
||||
index,
|
||||
serial_ports[index].iobase,
|
||||
serial_ports[index].irq,
|
||||
serial_ports[index].uartclk);
|
||||
|
||||
/* Get phys address of IO reg for port 1 */
|
||||
if (index != 0)
|
||||
goto next_port;
|
||||
|
||||
pci = of_get_parent(isa);
|
||||
if (!pci) {
|
||||
DBG("%s: no pci parent found\n", np->full_name);
|
||||
goto next_port;
|
||||
}
|
||||
|
||||
rangesp = (u32 *)get_property(pci, "ranges", &rlen);
|
||||
if (rangesp == NULL) {
|
||||
of_node_put(pci);
|
||||
goto next_port;
|
||||
}
|
||||
rlen /= 4;
|
||||
|
||||
/* we need the #size-cells of the PCI bridge node itself */
|
||||
phys_size = 1;
|
||||
sizeprop = (u32 *)get_property(pci, "#size-cells", NULL);
|
||||
if (sizeprop != NULL)
|
||||
phys_size = *sizeprop;
|
||||
/* we need the parent #addr-cells */
|
||||
addr_size = prom_n_addr_cells(pci);
|
||||
rentsize = 3 + addr_size + phys_size;
|
||||
io_base = 0;
|
||||
for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) {
|
||||
if (((rangesp[0] >> 24) & 0x3) != 1)
|
||||
continue; /* not IO space */
|
||||
io_base = rangesp[3];
|
||||
if (addr_size == 2)
|
||||
io_base = (io_base << 32) | rangesp[4];
|
||||
}
|
||||
if (io_base != 0) {
|
||||
*physport = io_base + reg->address;
|
||||
if (default_speed && spd)
|
||||
*default_speed = *spd;
|
||||
}
|
||||
of_node_put(pci);
|
||||
next_port:
|
||||
of_node_put(isa);
|
||||
}
|
||||
|
||||
DBG(" <- generic_find_legacy_serial_port()\n");
|
||||
}
|
||||
|
||||
static struct platform_device serial_device = {
|
||||
.name = "serial8250",
|
||||
.id = PLAT8250_DEV_PLATFORM,
|
||||
.dev = {
|
||||
.platform_data = serial_ports,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init serial_dev_init(void)
|
||||
{
|
||||
return platform_device_register(&serial_device);
|
||||
}
|
||||
arch_initcall(serial_dev_init);
|
||||
|
||||
#endif /* CONFIG_PPC_ISERIES */
|
||||
|
||||
int check_legacy_ioport(unsigned long base_port)
|
||||
{
|
||||
if (ppc_md.check_legacy_ioport == NULL)
|
||||
|
@ -76,7 +76,6 @@
|
||||
* registers from *regs. This is what we need
|
||||
* to do when a signal has been delivered.
|
||||
*/
|
||||
#define sigreturn_exit(regs) return 0
|
||||
|
||||
#define GP_REGS_SIZE min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32))
|
||||
#undef __SIGNAL_FRAMESIZE
|
||||
@ -156,9 +155,17 @@ static inline int save_general_regs(struct pt_regs *regs,
|
||||
elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= PT_RESULT; i ++)
|
||||
if (!FULL_REGS(regs)) {
|
||||
set_thread_flag(TIF_SAVE_NVGPRS);
|
||||
current_thread_info()->nvgprs_frame = frame->mc_gregs;
|
||||
}
|
||||
|
||||
for (i = 0; i <= PT_RESULT; i ++) {
|
||||
if (i == 14 && !FULL_REGS(regs))
|
||||
i = 32;
|
||||
if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -179,8 +186,6 @@ static inline int restore_general_regs(struct pt_regs *regs,
|
||||
|
||||
#else /* CONFIG_PPC64 */
|
||||
|
||||
extern void sigreturn_exit(struct pt_regs *);
|
||||
|
||||
#define GP_REGS_SIZE min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
|
||||
|
||||
static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
|
||||
@ -214,6 +219,15 @@ static inline int get_old_sigaction(struct k_sigaction *new_ka,
|
||||
static inline int save_general_regs(struct pt_regs *regs,
|
||||
struct mcontext __user *frame)
|
||||
{
|
||||
if (!FULL_REGS(regs)) {
|
||||
/* Zero out the unsaved GPRs to avoid information
|
||||
leak, and set TIF_SAVE_NVGPRS to ensure that the
|
||||
registers do actually get saved later. */
|
||||
memset(®s->gpr[14], 0, 18 * sizeof(unsigned long));
|
||||
current_thread_info()->nvgprs_frame = &frame->mc_gregs;
|
||||
set_thread_flag(TIF_SAVE_NVGPRS);
|
||||
}
|
||||
|
||||
return __copy_to_user(&frame->mc_gregs, regs, GP_REGS_SIZE);
|
||||
}
|
||||
|
||||
@ -256,8 +270,10 @@ long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7,
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
if (do_signal(&saveset, regs))
|
||||
sigreturn_exit(regs);
|
||||
if (do_signal(&saveset, regs)) {
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,8 +308,10 @@ long sys_rt_sigsuspend(
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
if (do_signal(&saveset, regs))
|
||||
sigreturn_exit(regs);
|
||||
if (do_signal(&saveset, regs)) {
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,9 +409,6 @@ struct rt_sigframe {
|
||||
static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame,
|
||||
int sigret)
|
||||
{
|
||||
#ifdef CONFIG_PPC32
|
||||
CHECK_FULL_REGS(regs);
|
||||
#endif
|
||||
/* Make sure floating point registers are stored in regs */
|
||||
flush_fp_to_thread(current);
|
||||
|
||||
@ -828,12 +843,6 @@ static int handle_rt_signal(unsigned long sig, struct k_sigaction *ka,
|
||||
regs->gpr[6] = (unsigned long) rt_sf;
|
||||
regs->nip = (unsigned long) ka->sa.sa_handler;
|
||||
regs->trap = 0;
|
||||
#ifdef CONFIG_PPC64
|
||||
regs->result = 0;
|
||||
|
||||
if (test_thread_flag(TIF_SINGLESTEP))
|
||||
ptrace_notify(SIGTRAP);
|
||||
#endif
|
||||
return 1;
|
||||
|
||||
badframe:
|
||||
@ -911,8 +920,8 @@ long sys_swapcontext(struct ucontext __user *old_ctx,
|
||||
*/
|
||||
if (do_setcontext(new_ctx, regs, 0))
|
||||
do_exit(SIGSEGV);
|
||||
sigreturn_exit(regs);
|
||||
/* doesn't actually return back to here */
|
||||
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -945,12 +954,11 @@ long sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
|
||||
* nobody does any...
|
||||
*/
|
||||
compat_sys_sigaltstack((u32)(u64)&rt_sf->uc.uc_stack, 0, 0, 0, 0, 0, regs);
|
||||
return (int)regs->result;
|
||||
#else
|
||||
do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
|
||||
sigreturn_exit(regs); /* doesn't return here */
|
||||
return 0;
|
||||
#endif
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
force_sig(SIGSEGV, current);
|
||||
@ -1041,9 +1049,7 @@ int sys_debug_setcontext(struct ucontext __user *ctx,
|
||||
*/
|
||||
do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]);
|
||||
|
||||
sigreturn_exit(regs);
|
||||
/* doesn't actually return back to here */
|
||||
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
@ -1107,12 +1113,6 @@ static int handle_signal(unsigned long sig, struct k_sigaction *ka,
|
||||
regs->gpr[4] = (unsigned long) sc;
|
||||
regs->nip = (unsigned long) ka->sa.sa_handler;
|
||||
regs->trap = 0;
|
||||
#ifdef CONFIG_PPC64
|
||||
regs->result = 0;
|
||||
|
||||
if (test_thread_flag(TIF_SINGLESTEP))
|
||||
ptrace_notify(SIGTRAP);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
|
||||
@ -1160,12 +1160,8 @@ long sys_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
|
||||
|| restore_user_regs(regs, sr, 1))
|
||||
goto badframe;
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
return (int)regs->result;
|
||||
#else
|
||||
sigreturn_exit(regs); /* doesn't return */
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
|
@ -96,8 +96,10 @@ long sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, int p3, int
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
if (do_signal(&saveset, regs))
|
||||
if (do_signal(&saveset, regs)) {
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +154,14 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
|
||||
err |= __put_user(0, &sc->v_regs);
|
||||
#endif /* CONFIG_ALTIVEC */
|
||||
err |= __put_user(&sc->gp_regs, &sc->regs);
|
||||
if (!FULL_REGS(regs)) {
|
||||
/* Zero out the unsaved GPRs to avoid information
|
||||
leak, and set TIF_SAVE_NVGPRS to ensure that the
|
||||
registers do actually get saved later. */
|
||||
memset(®s->gpr[14], 0, 18 * sizeof(unsigned long));
|
||||
set_thread_flag(TIF_SAVE_NVGPRS);
|
||||
current_thread_info()->nvgprs_frame = &sc->gp_regs;
|
||||
}
|
||||
err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE);
|
||||
err |= __copy_to_user(&sc->fp_regs, ¤t->thread.fpr, FP_REGS_SIZE);
|
||||
err |= __put_user(signr, &sc->signal);
|
||||
@ -340,6 +350,7 @@ int sys_swapcontext(struct ucontext __user *old_ctx,
|
||||
do_exit(SIGSEGV);
|
||||
|
||||
/* This returns like rt_sigreturn */
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -372,7 +383,8 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5,
|
||||
*/
|
||||
do_sigaltstack(&uc->uc_stack, NULL, regs->gpr[1]);
|
||||
|
||||
return regs->result;
|
||||
set_thread_flag(TIF_RESTOREALL);
|
||||
return 0;
|
||||
|
||||
badframe:
|
||||
#if DEBUG_SIG
|
||||
@ -454,9 +466,6 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info,
|
||||
if (err)
|
||||
goto badframe;
|
||||
|
||||
if (test_thread_flag(TIF_SINGLESTEP))
|
||||
ptrace_notify(SIGTRAP);
|
||||
|
||||
return 1;
|
||||
|
||||
badframe:
|
||||
@ -502,6 +511,8 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
|
||||
* we only get here if there is a handler, we dont restart.
|
||||
*/
|
||||
regs->result = -EINTR;
|
||||
regs->gpr[3] = EINTR;
|
||||
regs->ccr |= 0x10000000;
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
/* ERESTARTSYS means to restart the syscall if there is no
|
||||
@ -509,6 +520,8 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
|
||||
*/
|
||||
if (!(ka->sa.sa_flags & SA_RESTART)) {
|
||||
regs->result = -EINTR;
|
||||
regs->gpr[3] = EINTR;
|
||||
regs->ccr |= 0x10000000;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/topology.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/atomic.h>
|
||||
@ -75,6 +76,8 @@ void smp_call_function_interrupt(void);
|
||||
|
||||
int smt_enabled_at_boot = 1;
|
||||
|
||||
static void (*crash_ipi_function_ptr)(struct pt_regs *) = NULL;
|
||||
|
||||
#ifdef CONFIG_MPIC
|
||||
int __init smp_mpic_probe(void)
|
||||
{
|
||||
@ -123,11 +126,16 @@ void smp_message_recv(int msg, struct pt_regs *regs)
|
||||
/* XXX Do we have to do this? */
|
||||
set_need_resched();
|
||||
break;
|
||||
#ifdef CONFIG_DEBUGGER
|
||||
case PPC_MSG_DEBUGGER_BREAK:
|
||||
if (crash_ipi_function_ptr) {
|
||||
crash_ipi_function_ptr(regs);
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_DEBUGGER
|
||||
debugger_ipi(regs);
|
||||
break;
|
||||
#endif
|
||||
#endif /* CONFIG_DEBUGGER */
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
printk("SMP %d: smp_message_recv(): unknown msg %d\n",
|
||||
smp_processor_id(), msg);
|
||||
@ -147,6 +155,17 @@ void smp_send_debugger_break(int cpu)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *))
|
||||
{
|
||||
crash_ipi_function_ptr = crash_ipi_callback;
|
||||
if (crash_ipi_callback) {
|
||||
mb();
|
||||
smp_ops->message_pass(MSG_ALL_BUT_SELF, PPC_MSG_DEBUGGER_BREAK);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop_this_cpu(void *dummy)
|
||||
{
|
||||
local_irq_disable();
|
||||
@ -452,10 +471,6 @@ int __devinit __cpu_up(unsigned int cpu)
|
||||
if (smp_ops->cpu_bootable && !smp_ops->cpu_bootable(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
paca[cpu].default_decr = tb_ticks_per_jiffy;
|
||||
#endif
|
||||
|
||||
/* Make sure callin-map entry is 0 (can be leftover a CPU
|
||||
* hotplug
|
||||
*/
|
||||
@ -554,6 +569,8 @@ void __init smp_cpus_done(unsigned int max_cpus)
|
||||
smp_ops->setup_cpu(boot_cpuid);
|
||||
|
||||
set_cpus_allowed(current, old_mask);
|
||||
|
||||
dump_numa_cpu_topology();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
@ -43,9 +43,6 @@
|
||||
#include <asm/time.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
extern unsigned long wall_jiffies;
|
||||
|
||||
|
||||
/*
|
||||
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
|
||||
*
|
||||
@ -311,31 +308,6 @@ int sys_olduname(struct oldold_utsname __user *name)
|
||||
return error? -EFAULT: 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
time_t sys64_time(time_t __user * tloc)
|
||||
{
|
||||
time_t secs;
|
||||
time_t usecs;
|
||||
|
||||
long tb_delta = tb_ticks_since(tb_last_stamp);
|
||||
tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy;
|
||||
|
||||
secs = xtime.tv_sec;
|
||||
usecs = (xtime.tv_nsec/1000) + tb_delta / tb_ticks_per_usec;
|
||||
while (usecs >= USEC_PER_SEC) {
|
||||
++secs;
|
||||
usecs -= USEC_PER_SEC;
|
||||
}
|
||||
|
||||
if (tloc) {
|
||||
if (put_user(secs,tloc))
|
||||
secs = -EFAULT;
|
||||
}
|
||||
|
||||
return secs;
|
||||
}
|
||||
#endif
|
||||
|
||||
long ppc_fadvise64_64(int fd, int advice, u32 offset_high, u32 offset_low,
|
||||
u32 len_high, u32 len_low)
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ SYSCALL(link)
|
||||
SYSCALL(unlink)
|
||||
COMPAT_SYS(execve)
|
||||
SYSCALL(chdir)
|
||||
SYSX(sys64_time,compat_sys_time,sys_time)
|
||||
COMPAT_SYS(time)
|
||||
SYSCALL(mknod)
|
||||
SYSCALL(chmod)
|
||||
SYSCALL(lchown)
|
||||
@ -113,7 +113,7 @@ SYSCALL(sgetmask)
|
||||
COMPAT_SYS(ssetmask)
|
||||
SYSCALL(setreuid)
|
||||
SYSCALL(setregid)
|
||||
SYSX(sys_ni_syscall,ppc32_sigsuspend,ppc_sigsuspend)
|
||||
SYS32ONLY(sigsuspend)
|
||||
COMPAT_SYS(sigpending)
|
||||
COMPAT_SYS(sethostname)
|
||||
COMPAT_SYS(setrlimit)
|
||||
@ -160,7 +160,7 @@ SYSCALL(swapoff)
|
||||
COMPAT_SYS(sysinfo)
|
||||
COMPAT_SYS(ipc)
|
||||
SYSCALL(fsync)
|
||||
SYSX(sys_ni_syscall,ppc32_sigreturn,sys_sigreturn)
|
||||
SYS32ONLY(sigreturn)
|
||||
PPC_SYS(clone)
|
||||
COMPAT_SYS(setdomainname)
|
||||
PPC_SYS(newuname)
|
||||
@ -213,13 +213,13 @@ COMPAT_SYS(nfsservctl)
|
||||
SYSCALL(setresgid)
|
||||
SYSCALL(getresgid)
|
||||
COMPAT_SYS(prctl)
|
||||
SYSX(ppc64_rt_sigreturn,ppc32_rt_sigreturn,sys_rt_sigreturn)
|
||||
COMPAT_SYS(rt_sigreturn)
|
||||
COMPAT_SYS(rt_sigaction)
|
||||
COMPAT_SYS(rt_sigprocmask)
|
||||
COMPAT_SYS(rt_sigpending)
|
||||
COMPAT_SYS(rt_sigtimedwait)
|
||||
COMPAT_SYS(rt_sigqueueinfo)
|
||||
SYSX(ppc64_rt_sigsuspend,ppc32_rt_sigsuspend,ppc_rt_sigsuspend)
|
||||
COMPAT_SYS(rt_sigsuspend)
|
||||
COMPAT_SYS(pread64)
|
||||
COMPAT_SYS(pwrite64)
|
||||
SYSCALL(chown)
|
||||
@ -290,7 +290,7 @@ COMPAT_SYS(clock_settime)
|
||||
COMPAT_SYS(clock_gettime)
|
||||
COMPAT_SYS(clock_getres)
|
||||
COMPAT_SYS(clock_nanosleep)
|
||||
SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext)
|
||||
COMPAT_SYS(swapcontext)
|
||||
COMPAT_SYS(tgkill)
|
||||
COMPAT_SYS(utimes)
|
||||
COMPAT_SYS(statfs64)
|
||||
@ -319,3 +319,5 @@ COMPAT_SYS(ioprio_get)
|
||||
SYSCALL(inotify_init)
|
||||
SYSCALL(inotify_add_watch)
|
||||
SYSCALL(inotify_rm_watch)
|
||||
SYSCALL(spu_run)
|
||||
SYSCALL(spu_create)
|
||||
|
@ -699,10 +699,6 @@ void __init time_init(void)
|
||||
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &res);
|
||||
tb_to_xs = res.result_low;
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
get_paca()->default_decr = tb_ticks_per_jiffy;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compute scale factor for sched_clock.
|
||||
* The calibrate_decr() function has set tb_ticks_per_sec,
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/prctl.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/kexec.h>
|
||||
|
||||
#include <asm/kdebug.h>
|
||||
#include <asm/pgtable.h>
|
||||
@ -95,7 +96,7 @@ static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
int die(const char *str, struct pt_regs *regs, long err)
|
||||
{
|
||||
static int die_counter;
|
||||
static int die_counter, crash_dump_start = 0;
|
||||
int nl = 0;
|
||||
|
||||
if (debugger(regs))
|
||||
@ -156,7 +157,21 @@ int die(const char *str, struct pt_regs *regs, long err)
|
||||
print_modules();
|
||||
show_regs(regs);
|
||||
bust_spinlocks(0);
|
||||
|
||||
if (!crash_dump_start && kexec_should_crash(current)) {
|
||||
crash_dump_start = 1;
|
||||
spin_unlock_irq(&die_lock);
|
||||
crash_kexec(regs);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
spin_unlock_irq(&die_lock);
|
||||
if (crash_dump_start)
|
||||
/*
|
||||
* Only for soft-reset: Other CPUs will be responded to an IPI
|
||||
* sent by first kexec CPU.
|
||||
*/
|
||||
for(;;)
|
||||
;
|
||||
|
||||
if (in_interrupt())
|
||||
panic("Fatal exception in interrupt");
|
||||
@ -215,8 +230,10 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
|
||||
void system_reset_exception(struct pt_regs *regs)
|
||||
{
|
||||
/* See if any machine dependent calls */
|
||||
if (ppc_md.system_reset_exception)
|
||||
ppc_md.system_reset_exception(regs);
|
||||
if (ppc_md.system_reset_exception) {
|
||||
if (ppc_md.system_reset_exception(regs))
|
||||
return;
|
||||
}
|
||||
|
||||
die("System Reset", regs, SIGABRT);
|
||||
|
||||
@ -886,12 +903,10 @@ void altivec_unavailable_exception(struct pt_regs *regs)
|
||||
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PPC64) || defined(CONFIG_E500)
|
||||
void performance_monitor_exception(struct pt_regs *regs)
|
||||
{
|
||||
perf_irq(regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_8xx
|
||||
void SoftwareEmulation(struct pt_regs *regs)
|
||||
|
@ -16,8 +16,8 @@
|
||||
#include <linux/console.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
void (*udbg_putc)(unsigned char c);
|
||||
unsigned char (*udbg_getc)(void);
|
||||
void (*udbg_putc)(char c);
|
||||
int (*udbg_getc)(void);
|
||||
int (*udbg_getc_poll)(void);
|
||||
|
||||
/* udbg library, used by xmon et al */
|
||||
@ -57,8 +57,8 @@ int udbg_write(const char *s, int n)
|
||||
|
||||
int udbg_read(char *buf, int buflen)
|
||||
{
|
||||
char c, *p = buf;
|
||||
int i;
|
||||
char *p = buf;
|
||||
int i, c;
|
||||
|
||||
if (!udbg_getc)
|
||||
return 0;
|
||||
@ -66,8 +66,11 @@ int udbg_read(char *buf, int buflen)
|
||||
for (i = 0; i < buflen; ++i) {
|
||||
do {
|
||||
c = udbg_getc();
|
||||
if (c == -1 && i == 0)
|
||||
return -1;
|
||||
|
||||
} while (c == 0x11 || c == 0x13);
|
||||
if (c == 0)
|
||||
if (c == 0 || c == -1)
|
||||
break;
|
||||
*p++ = c;
|
||||
}
|
||||
@ -78,7 +81,7 @@ int udbg_read(char *buf, int buflen)
|
||||
#define UDBG_BUFSIZE 256
|
||||
void udbg_printf(const char *fmt, ...)
|
||||
{
|
||||
unsigned char buf[UDBG_BUFSIZE];
|
||||
char buf[UDBG_BUFSIZE];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
@ -87,6 +90,12 @@ void udbg_printf(const char *fmt, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void __init udbg_progress(char *s, unsigned short hex)
|
||||
{
|
||||
udbg_puts(s);
|
||||
udbg_puts("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Early boot console based on udbg
|
||||
*/
|
||||
@ -99,7 +108,7 @@ static void udbg_console_write(struct console *con, const char *s,
|
||||
static struct console udbg_console = {
|
||||
.name = "udbg",
|
||||
.write = udbg_console_write,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.flags = CON_PRINTBUFFER | CON_ENABLED,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
@ -107,15 +116,19 @@ static int early_console_initialized;
|
||||
|
||||
void __init disable_early_printk(void)
|
||||
{
|
||||
#if 1
|
||||
if (!early_console_initialized)
|
||||
return;
|
||||
unregister_console(&udbg_console);
|
||||
early_console_initialized = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* called by setup_system */
|
||||
void register_early_udbg_console(void)
|
||||
{
|
||||
if (early_console_initialized)
|
||||
return;
|
||||
early_console_initialized = 1;
|
||||
register_console(&udbg_console);
|
||||
}
|
||||
|
@ -43,9 +43,11 @@ struct NS16550 {
|
||||
#define LSR_TEMT 0x40 /* Xmitter empty */
|
||||
#define LSR_ERR 0x80 /* Error */
|
||||
|
||||
#define LCR_DLAB 0x80
|
||||
|
||||
static volatile struct NS16550 __iomem *udbg_comport;
|
||||
|
||||
static void udbg_550_putc(unsigned char c)
|
||||
static void udbg_550_putc(char c)
|
||||
{
|
||||
if (udbg_comport) {
|
||||
while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
|
||||
@ -67,39 +69,80 @@ static int udbg_550_getc_poll(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned char udbg_550_getc(void)
|
||||
static int udbg_550_getc(void)
|
||||
{
|
||||
if (udbg_comport) {
|
||||
while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
|
||||
/* wait for char */;
|
||||
return in_8(&udbg_comport->rbr);
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void udbg_init_uart(void __iomem *comport, unsigned int speed)
|
||||
void udbg_init_uart(void __iomem *comport, unsigned int speed,
|
||||
unsigned int clock)
|
||||
{
|
||||
u16 dll = speed ? (115200 / speed) : 12;
|
||||
unsigned int dll, base_bauds = clock / 16;
|
||||
|
||||
if (speed == 0)
|
||||
speed = 9600;
|
||||
dll = base_bauds / speed;
|
||||
|
||||
if (comport) {
|
||||
udbg_comport = (struct NS16550 __iomem *)comport;
|
||||
out_8(&udbg_comport->lcr, 0x00);
|
||||
out_8(&udbg_comport->ier, 0xff);
|
||||
out_8(&udbg_comport->ier, 0x00);
|
||||
out_8(&udbg_comport->lcr, 0x80); /* Access baud rate */
|
||||
out_8(&udbg_comport->dll, dll & 0xff); /* 1 = 115200, 2 = 57600,
|
||||
3 = 38400, 12 = 9600 baud */
|
||||
out_8(&udbg_comport->dlm, dll >> 8); /* dll >> 8 which should be zero
|
||||
for fast rates; */
|
||||
out_8(&udbg_comport->lcr, 0x03); /* 8 data, 1 stop, no parity */
|
||||
out_8(&udbg_comport->mcr, 0x03); /* RTS/DTR */
|
||||
out_8(&udbg_comport->fcr ,0x07); /* Clear & enable FIFOs */
|
||||
out_8(&udbg_comport->lcr, LCR_DLAB);
|
||||
out_8(&udbg_comport->dll, dll & 0xff);
|
||||
out_8(&udbg_comport->dlm, dll >> 8);
|
||||
/* 8 data, 1 stop, no parity */
|
||||
out_8(&udbg_comport->lcr, 0x03);
|
||||
/* RTS/DTR */
|
||||
out_8(&udbg_comport->mcr, 0x03);
|
||||
/* Clear & enable FIFOs */
|
||||
out_8(&udbg_comport->fcr ,0x07);
|
||||
udbg_putc = udbg_550_putc;
|
||||
udbg_getc = udbg_550_getc;
|
||||
udbg_getc_poll = udbg_550_getc_poll;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
|
||||
{
|
||||
unsigned int dll, dlm, divisor, prescaler, speed;
|
||||
u8 old_lcr;
|
||||
volatile struct NS16550 __iomem *port = comport;
|
||||
|
||||
old_lcr = in_8(&port->lcr);
|
||||
|
||||
/* select divisor latch registers. */
|
||||
out_8(&port->lcr, LCR_DLAB);
|
||||
|
||||
/* now, read the divisor */
|
||||
dll = in_8(&port->dll);
|
||||
dlm = in_8(&port->dlm);
|
||||
divisor = dlm << 8 | dll;
|
||||
|
||||
/* check prescaling */
|
||||
if (in_8(&port->mcr) & 0x80)
|
||||
prescaler = 4;
|
||||
else
|
||||
prescaler = 1;
|
||||
|
||||
/* restore the LCR */
|
||||
out_8(&port->lcr, old_lcr);
|
||||
|
||||
/* calculate speed */
|
||||
speed = (clock / prescaler) / (divisor * 16);
|
||||
|
||||
/* sanity check */
|
||||
if (speed < 0 || speed > (clock / 16))
|
||||
speed = 9600;
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_MAPLE
|
||||
void udbg_maple_real_putc(unsigned char c)
|
||||
{
|
||||
|
@ -81,7 +81,8 @@ static int store_updates_sp(struct pt_regs *regs)
|
||||
}
|
||||
|
||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
|
||||
static void do_dabr(struct pt_regs *regs, unsigned long error_code)
|
||||
static void do_dabr(struct pt_regs *regs, unsigned long address,
|
||||
unsigned long error_code)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
@ -99,7 +100,7 @@ static void do_dabr(struct pt_regs *regs, unsigned long error_code)
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_HWBKPT;
|
||||
info.si_addr = (void __user *)regs->nip;
|
||||
info.si_addr = (void __user *)address;
|
||||
force_sig_info(SIGTRAP, &info, current);
|
||||
}
|
||||
#endif /* !(CONFIG_4xx || CONFIG_BOOKE)*/
|
||||
@ -159,7 +160,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
|
||||
#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
|
||||
if (error_code & DSISR_DABRMATCH) {
|
||||
/* DABR match */
|
||||
do_dabr(regs, error_code);
|
||||
do_dabr(regs, address, error_code);
|
||||
return 0;
|
||||
}
|
||||
#endif /* !(CONFIG_4xx || CONFIG_BOOKE)*/
|
||||
|
@ -456,7 +456,7 @@ void __init htab_initialize(void)
|
||||
|
||||
/* create bolted the linear mapping in the hash table */
|
||||
for (i=0; i < lmb.memory.cnt; i++) {
|
||||
base = lmb.memory.region[i].base + KERNELBASE;
|
||||
base = (unsigned long)__va(lmb.memory.region[i].base);
|
||||
size = lmb.memory.region[i].size;
|
||||
|
||||
DBG("creating mapping for region: %lx : %lx\n", base, size);
|
||||
@ -498,8 +498,8 @@ void __init htab_initialize(void)
|
||||
* for either 4K or 16MB pages.
|
||||
*/
|
||||
if (tce_alloc_start) {
|
||||
tce_alloc_start += KERNELBASE;
|
||||
tce_alloc_end += KERNELBASE;
|
||||
tce_alloc_start = (unsigned long)__va(tce_alloc_start);
|
||||
tce_alloc_end = (unsigned long)__va(tce_alloc_end);
|
||||
|
||||
if (base + size >= tce_alloc_start)
|
||||
tce_alloc_start = base + size + 1;
|
||||
@ -644,6 +644,7 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
|
||||
DBG_LOW(" -> rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hash_page);
|
||||
|
||||
void hash_preload(struct mm_struct *mm, unsigned long ea,
|
||||
unsigned long access, unsigned long trap)
|
||||
|
@ -549,6 +549,17 @@ fail:
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int htlb_check_hinted_area(unsigned long addr, unsigned long len)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
vma = find_vma(current->mm, addr);
|
||||
if (!vma || ((addr + len) <= vma->vm_start))
|
||||
return 0;
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static unsigned long htlb_get_low_area(unsigned long len, u16 segmask)
|
||||
{
|
||||
unsigned long addr = 0;
|
||||
@ -618,15 +629,28 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
|
||||
if (!cpu_has_feature(CPU_FTR_16M_PAGE))
|
||||
return -EINVAL;
|
||||
|
||||
/* Paranoia, caller should have dealt with this */
|
||||
BUG_ON((addr + len) < addr);
|
||||
|
||||
if (test_thread_flag(TIF_32BIT)) {
|
||||
/* Paranoia, caller should have dealt with this */
|
||||
BUG_ON((addr + len) > 0x100000000UL);
|
||||
|
||||
curareas = current->mm->context.low_htlb_areas;
|
||||
|
||||
/* First see if we can do the mapping in the existing
|
||||
* low areas */
|
||||
/* First see if we can use the hint address */
|
||||
if (addr && (htlb_check_hinted_area(addr, len) == 0)) {
|
||||
areamask = LOW_ESID_MASK(addr, len);
|
||||
if (open_low_hpage_areas(current->mm, areamask) == 0)
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Next see if we can map in the existing low areas */
|
||||
addr = htlb_get_low_area(len, curareas);
|
||||
if (addr != -ENOMEM)
|
||||
return addr;
|
||||
|
||||
/* Finally go looking for areas to open */
|
||||
lastshift = 0;
|
||||
for (areamask = LOW_ESID_MASK(0x100000000UL-len, len);
|
||||
! lastshift; areamask >>=1) {
|
||||
@ -641,12 +665,22 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
|
||||
} else {
|
||||
curareas = current->mm->context.high_htlb_areas;
|
||||
|
||||
/* First see if we can do the mapping in the existing
|
||||
* high areas */
|
||||
/* First see if we can use the hint address */
|
||||
/* We discourage 64-bit processes from doing hugepage
|
||||
* mappings below 4GB (must use MAP_FIXED) */
|
||||
if ((addr >= 0x100000000UL)
|
||||
&& (htlb_check_hinted_area(addr, len) == 0)) {
|
||||
areamask = HTLB_AREA_MASK(addr, len);
|
||||
if (open_high_hpage_areas(current->mm, areamask) == 0)
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Next see if we can map in the existing high areas */
|
||||
addr = htlb_get_high_area(len, curareas);
|
||||
if (addr != -ENOMEM)
|
||||
return addr;
|
||||
|
||||
/* Finally go looking for areas to open */
|
||||
lastshift = 0;
|
||||
for (areamask = HTLB_AREA_MASK(TASK_SIZE_USER64-len, len);
|
||||
! lastshift; areamask >>=1) {
|
||||
|
@ -107,6 +107,7 @@ static int im_region_status(unsigned long v_addr, unsigned long size,
|
||||
if (v_addr < (unsigned long) tmp->addr + tmp->size)
|
||||
break;
|
||||
|
||||
*vm = NULL;
|
||||
if (tmp) {
|
||||
if (im_region_overlaps(v_addr, size, tmp))
|
||||
return IM_REGION_OVERLAP;
|
||||
@ -127,7 +128,6 @@ static int im_region_status(unsigned long v_addr, unsigned long size,
|
||||
}
|
||||
}
|
||||
|
||||
*vm = NULL;
|
||||
return IM_REGION_UNUSED;
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,11 @@ void __init MMU_init(void)
|
||||
|
||||
if (ppc_md.progress)
|
||||
ppc_md.progress("MMU:exit", 0x211);
|
||||
|
||||
/* From now on, btext is no longer BAT mapped if it was at all */
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
btext_unmap();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This is only called until mem_init is done. */
|
||||
|
@ -114,19 +114,18 @@ void online_page(struct page *page)
|
||||
num_physpages++;
|
||||
}
|
||||
|
||||
/*
|
||||
* This works only for the non-NUMA case. Later, we'll need a lookup
|
||||
* to convert from real physical addresses to nid, that doesn't use
|
||||
* pfn_to_nid().
|
||||
*/
|
||||
int __devinit add_memory(u64 start, u64 size)
|
||||
{
|
||||
struct pglist_data *pgdata = NODE_DATA(0);
|
||||
struct pglist_data *pgdata;
|
||||
struct zone *zone;
|
||||
int nid;
|
||||
unsigned long start_pfn = start >> PAGE_SHIFT;
|
||||
unsigned long nr_pages = size >> PAGE_SHIFT;
|
||||
|
||||
start += KERNELBASE;
|
||||
nid = hot_add_scn_to_nid(start);
|
||||
pgdata = NODE_DATA(nid);
|
||||
|
||||
start = __va(start);
|
||||
create_section_mapping(start, start + size);
|
||||
|
||||
/* this should work for most non-highmem platforms */
|
||||
|
@ -37,6 +37,7 @@ EXPORT_SYMBOL(node_data);
|
||||
|
||||
static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES];
|
||||
static int min_common_depth;
|
||||
static int n_mem_addr_cells, n_mem_size_cells;
|
||||
|
||||
/*
|
||||
* We need somewhere to store start/end/node for each region until we have
|
||||
@ -254,32 +255,20 @@ static int __init find_min_common_depth(void)
|
||||
return depth;
|
||||
}
|
||||
|
||||
static int __init get_mem_addr_cells(void)
|
||||
static void __init get_n_mem_cells(int *n_addr_cells, int *n_size_cells)
|
||||
{
|
||||
struct device_node *memory = NULL;
|
||||
int rc;
|
||||
|
||||
memory = of_find_node_by_type(memory, "memory");
|
||||
if (!memory)
|
||||
return 0; /* it won't matter */
|
||||
panic("numa.c: No memory nodes found!");
|
||||
|
||||
rc = prom_n_addr_cells(memory);
|
||||
return rc;
|
||||
*n_addr_cells = prom_n_addr_cells(memory);
|
||||
*n_size_cells = prom_n_size_cells(memory);
|
||||
of_node_put(memory);
|
||||
}
|
||||
|
||||
static int __init get_mem_size_cells(void)
|
||||
{
|
||||
struct device_node *memory = NULL;
|
||||
int rc;
|
||||
|
||||
memory = of_find_node_by_type(memory, "memory");
|
||||
if (!memory)
|
||||
return 0; /* it won't matter */
|
||||
rc = prom_n_size_cells(memory);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static unsigned long __init read_n_cells(int n, unsigned int **buf)
|
||||
static unsigned long __devinit read_n_cells(int n, unsigned int **buf)
|
||||
{
|
||||
unsigned long result = 0;
|
||||
|
||||
@ -386,7 +375,6 @@ static int __init parse_numa_properties(void)
|
||||
{
|
||||
struct device_node *cpu = NULL;
|
||||
struct device_node *memory = NULL;
|
||||
int addr_cells, size_cells;
|
||||
int max_domain;
|
||||
unsigned long i;
|
||||
|
||||
@ -425,8 +413,7 @@ static int __init parse_numa_properties(void)
|
||||
}
|
||||
}
|
||||
|
||||
addr_cells = get_mem_addr_cells();
|
||||
size_cells = get_mem_size_cells();
|
||||
get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells);
|
||||
memory = NULL;
|
||||
while ((memory = of_find_node_by_type(memory, "memory")) != NULL) {
|
||||
unsigned long start;
|
||||
@ -436,15 +423,21 @@ static int __init parse_numa_properties(void)
|
||||
unsigned int *memcell_buf;
|
||||
unsigned int len;
|
||||
|
||||
memcell_buf = (unsigned int *)get_property(memory, "reg", &len);
|
||||
memcell_buf = (unsigned int *)get_property(memory,
|
||||
"linux,usable-memory", &len);
|
||||
if (!memcell_buf || len <= 0)
|
||||
memcell_buf =
|
||||
(unsigned int *)get_property(memory, "reg",
|
||||
&len);
|
||||
if (!memcell_buf || len <= 0)
|
||||
continue;
|
||||
|
||||
ranges = memory->n_addrs;
|
||||
/* ranges in cell */
|
||||
ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells);
|
||||
new_range:
|
||||
/* these are order-sensitive, and modify the buffer pointer */
|
||||
start = read_n_cells(addr_cells, &memcell_buf);
|
||||
size = read_n_cells(size_cells, &memcell_buf);
|
||||
start = read_n_cells(n_mem_addr_cells, &memcell_buf);
|
||||
size = read_n_cells(n_mem_size_cells, &memcell_buf);
|
||||
|
||||
numa_domain = of_node_numa_domain(memory);
|
||||
|
||||
@ -497,7 +490,41 @@ static void __init setup_nonnuma(void)
|
||||
node_set_online(0);
|
||||
}
|
||||
|
||||
static void __init dump_numa_topology(void)
|
||||
void __init dump_numa_cpu_topology(void)
|
||||
{
|
||||
unsigned int node;
|
||||
unsigned int cpu, count;
|
||||
|
||||
if (min_common_depth == -1 || !numa_enabled)
|
||||
return;
|
||||
|
||||
for_each_online_node(node) {
|
||||
printk(KERN_INFO "Node %d CPUs:", node);
|
||||
|
||||
count = 0;
|
||||
/*
|
||||
* If we used a CPU iterator here we would miss printing
|
||||
* the holes in the cpumap.
|
||||
*/
|
||||
for (cpu = 0; cpu < NR_CPUS; cpu++) {
|
||||
if (cpu_isset(cpu, numa_cpumask_lookup_table[node])) {
|
||||
if (count == 0)
|
||||
printk(" %u", cpu);
|
||||
++count;
|
||||
} else {
|
||||
if (count > 1)
|
||||
printk("-%u", cpu - 1);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 1)
|
||||
printk("-%u", NR_CPUS - 1);
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void __init dump_numa_memory_topology(void)
|
||||
{
|
||||
unsigned int node;
|
||||
unsigned int count;
|
||||
@ -529,7 +556,6 @@ static void __init dump_numa_topology(void)
|
||||
printk("-0x%lx", i);
|
||||
printk("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -591,7 +617,7 @@ void __init do_init_bootmem(void)
|
||||
if (parse_numa_properties())
|
||||
setup_nonnuma();
|
||||
else
|
||||
dump_numa_topology();
|
||||
dump_numa_memory_topology();
|
||||
|
||||
register_cpu_notifier(&ppc64_numa_nb);
|
||||
|
||||
@ -730,3 +756,60 @@ static int __init early_numa(char *p)
|
||||
return 0;
|
||||
}
|
||||
early_param("numa", early_numa);
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
/*
|
||||
* Find the node associated with a hot added memory section. Section
|
||||
* corresponds to a SPARSEMEM section, not an LMB. It is assumed that
|
||||
* sections are fully contained within a single LMB.
|
||||
*/
|
||||
int hot_add_scn_to_nid(unsigned long scn_addr)
|
||||
{
|
||||
struct device_node *memory = NULL;
|
||||
nodemask_t nodes;
|
||||
int numa_domain = 0;
|
||||
|
||||
if (!numa_enabled || (min_common_depth < 0))
|
||||
return numa_domain;
|
||||
|
||||
while ((memory = of_find_node_by_type(memory, "memory")) != NULL) {
|
||||
unsigned long start, size;
|
||||
int ranges;
|
||||
unsigned int *memcell_buf;
|
||||
unsigned int len;
|
||||
|
||||
memcell_buf = (unsigned int *)get_property(memory, "reg", &len);
|
||||
if (!memcell_buf || len <= 0)
|
||||
continue;
|
||||
|
||||
/* ranges in cell */
|
||||
ranges = (len >> 2) / (n_mem_addr_cells + n_mem_size_cells);
|
||||
ha_new_range:
|
||||
start = read_n_cells(n_mem_addr_cells, &memcell_buf);
|
||||
size = read_n_cells(n_mem_size_cells, &memcell_buf);
|
||||
numa_domain = of_node_numa_domain(memory);
|
||||
|
||||
/* Domains not present at boot default to 0 */
|
||||
if (!node_online(numa_domain))
|
||||
numa_domain = any_online_node(NODE_MASK_ALL);
|
||||
|
||||
if ((scn_addr >= start) && (scn_addr < (start + size))) {
|
||||
of_node_put(memory);
|
||||
goto got_numa_domain;
|
||||
}
|
||||
|
||||
if (--ranges) /* process all ranges in cell */
|
||||
goto ha_new_range;
|
||||
}
|
||||
BUG(); /* section address should be found above */
|
||||
|
||||
/* Temporary code to ensure that returned node is not empty */
|
||||
got_numa_domain:
|
||||
nodes_setall(nodes);
|
||||
while (NODE_DATA(numa_domain)->node_spanned_pages == 0) {
|
||||
node_clear(numa_domain, nodes);
|
||||
numa_domain = any_online_node(nodes);
|
||||
}
|
||||
return numa_domain;
|
||||
}
|
||||
#endif /* CONFIG_MEMORY_HOTPLUG */
|
||||
|
@ -75,7 +75,7 @@ static void slb_flush_and_rebolt(void)
|
||||
vflags = SLB_VSID_KERNEL | virtual_llp;
|
||||
|
||||
ksp_esid_data = mk_esid_data(get_paca()->kstack, 2);
|
||||
if ((ksp_esid_data & ESID_MASK) == KERNELBASE)
|
||||
if ((ksp_esid_data & ESID_MASK) == PAGE_OFFSET)
|
||||
ksp_esid_data &= ~SLB_ESID_V;
|
||||
|
||||
/* We need to do this all in asm, so we're sure we don't touch
|
||||
@ -87,8 +87,8 @@ static void slb_flush_and_rebolt(void)
|
||||
/* Slot 2 - kernel stack */
|
||||
"slbmte %2,%3\n"
|
||||
"isync"
|
||||
:: "r"(mk_vsid_data(VMALLOCBASE, vflags)),
|
||||
"r"(mk_esid_data(VMALLOCBASE, 1)),
|
||||
:: "r"(mk_vsid_data(VMALLOC_START, vflags)),
|
||||
"r"(mk_esid_data(VMALLOC_START, 1)),
|
||||
"r"(mk_vsid_data(ksp_esid_data, lflags)),
|
||||
"r"(ksp_esid_data)
|
||||
: "memory");
|
||||
@ -134,14 +134,14 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
|
||||
else
|
||||
unmapped_base = TASK_UNMAPPED_BASE_USER64;
|
||||
|
||||
if (pc >= KERNELBASE)
|
||||
if (is_kernel_addr(pc))
|
||||
return;
|
||||
slb_allocate(pc);
|
||||
|
||||
if (GET_ESID(pc) == GET_ESID(stack))
|
||||
return;
|
||||
|
||||
if (stack >= KERNELBASE)
|
||||
if (is_kernel_addr(stack))
|
||||
return;
|
||||
slb_allocate(stack);
|
||||
|
||||
@ -149,7 +149,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
|
||||
|| (GET_ESID(stack) == GET_ESID(unmapped_base)))
|
||||
return;
|
||||
|
||||
if (unmapped_base >= KERNELBASE)
|
||||
if (is_kernel_addr(unmapped_base))
|
||||
return;
|
||||
slb_allocate(unmapped_base);
|
||||
}
|
||||
@ -213,10 +213,10 @@ void slb_initialize(void)
|
||||
asm volatile("isync":::"memory");
|
||||
asm volatile("slbmte %0,%0"::"r" (0) : "memory");
|
||||
asm volatile("isync; slbia; isync":::"memory");
|
||||
create_slbe(KERNELBASE, lflags, 0);
|
||||
create_slbe(PAGE_OFFSET, lflags, 0);
|
||||
|
||||
/* VMALLOC space has 4K pages always for now */
|
||||
create_slbe(VMALLOCBASE, vflags, 1);
|
||||
create_slbe(VMALLOC_START, vflags, 1);
|
||||
|
||||
/* We don't bolt the stack for the time being - we're in boot,
|
||||
* so the stack is in the bolted segment. By the time it goes
|
||||
|
@ -37,9 +37,9 @@ _GLOBAL(slb_allocate_realmode)
|
||||
|
||||
srdi r9,r3,60 /* get region */
|
||||
srdi r10,r3,28 /* get esid */
|
||||
cmpldi cr7,r9,0xc /* cmp KERNELBASE for later use */
|
||||
cmpldi cr7,r9,0xc /* cmp PAGE_OFFSET for later use */
|
||||
|
||||
/* r3 = address, r10 = esid, cr7 = <>KERNELBASE */
|
||||
/* r3 = address, r10 = esid, cr7 = <> PAGE_OFFSET */
|
||||
blt cr7,0f /* user or kernel? */
|
||||
|
||||
/* kernel address: proto-VSID = ESID */
|
||||
@ -166,7 +166,7 @@ _GLOBAL(slb_allocate_user)
|
||||
/*
|
||||
* Finish loading of an SLB entry and return
|
||||
*
|
||||
* r3 = EA, r10 = proto-VSID, r11 = flags, clobbers r9, cr7 = <>KERNELBASE
|
||||
* r3 = EA, r10 = proto-VSID, r11 = flags, clobbers r9, cr7 = <> PAGE_OFFSET
|
||||
*/
|
||||
slb_finish_load:
|
||||
ASM_VSID_SCRAMBLE(r10,r9)
|
||||
|
@ -40,7 +40,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid)
|
||||
unsigned long entry, group, old_esid, castout_entry, i;
|
||||
unsigned int global_entry;
|
||||
struct stab_entry *ste, *castout_ste;
|
||||
unsigned long kernel_segment = (esid << SID_SHIFT) >= KERNELBASE;
|
||||
unsigned long kernel_segment = (esid << SID_SHIFT) >= PAGE_OFFSET;
|
||||
|
||||
vsid_data = vsid << STE_VSID_SHIFT;
|
||||
esid_data = esid << SID_SHIFT | STE_ESID_KP | STE_ESID_V;
|
||||
@ -83,7 +83,7 @@ static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid)
|
||||
}
|
||||
|
||||
/* Dont cast out the first kernel segment */
|
||||
if ((castout_ste->esid_data & ESID_MASK) != KERNELBASE)
|
||||
if ((castout_ste->esid_data & ESID_MASK) != PAGE_OFFSET)
|
||||
break;
|
||||
|
||||
castout_entry = (castout_entry + 1) & 0xf;
|
||||
@ -122,7 +122,7 @@ static int __ste_allocate(unsigned long ea, struct mm_struct *mm)
|
||||
unsigned long offset;
|
||||
|
||||
/* Kernel or user address? */
|
||||
if (ea >= KERNELBASE) {
|
||||
if (is_kernel_addr(ea)) {
|
||||
vsid = get_kernel_vsid(ea);
|
||||
} else {
|
||||
if ((ea >= TASK_SIZE_USER64) || (! mm))
|
||||
@ -133,7 +133,7 @@ static int __ste_allocate(unsigned long ea, struct mm_struct *mm)
|
||||
|
||||
stab_entry = make_ste(get_paca()->stab_addr, GET_ESID(ea), vsid);
|
||||
|
||||
if (ea < KERNELBASE) {
|
||||
if (!is_kernel_addr(ea)) {
|
||||
offset = __get_cpu_var(stab_cache_ptr);
|
||||
if (offset < NR_STAB_CACHE_ENTRIES)
|
||||
__get_cpu_var(stab_cache[offset++]) = stab_entry;
|
||||
@ -190,7 +190,7 @@ void switch_stab(struct task_struct *tsk, struct mm_struct *mm)
|
||||
entry++, ste++) {
|
||||
unsigned long ea;
|
||||
ea = ste->esid_data & ESID_MASK;
|
||||
if (ea < KERNELBASE) {
|
||||
if (!is_kernel_addr(ea)) {
|
||||
ste->esid_data = 0;
|
||||
}
|
||||
}
|
||||
@ -251,7 +251,7 @@ void stabs_alloc(void)
|
||||
panic("Unable to allocate segment table for CPU %d.\n",
|
||||
cpu);
|
||||
|
||||
newstab += KERNELBASE;
|
||||
newstab = (unsigned long)__va(newstab);
|
||||
|
||||
memset((void *)newstab, 0, HW_PAGE_SIZE);
|
||||
|
||||
@ -270,11 +270,11 @@ void stabs_alloc(void)
|
||||
*/
|
||||
void stab_initialize(unsigned long stab)
|
||||
{
|
||||
unsigned long vsid = get_kernel_vsid(KERNELBASE);
|
||||
unsigned long vsid = get_kernel_vsid(PAGE_OFFSET);
|
||||
unsigned long stabreal;
|
||||
|
||||
asm volatile("isync; slbia; isync":::"memory");
|
||||
make_ste(stab, GET_ESID(KERNELBASE), vsid);
|
||||
make_ste(stab, GET_ESID(PAGE_OFFSET), vsid);
|
||||
|
||||
/* Order update */
|
||||
asm volatile("sync":::"memory");
|
||||
|
@ -168,7 +168,7 @@ void hpte_update(struct mm_struct *mm, unsigned long addr,
|
||||
batch->mm = mm;
|
||||
batch->psize = psize;
|
||||
}
|
||||
if (addr < KERNELBASE) {
|
||||
if (!is_kernel_addr(addr)) {
|
||||
vsid = get_vsid(mm->context.id, addr);
|
||||
WARN_ON(vsid == 0);
|
||||
} else
|
||||
|
@ -9,3 +9,4 @@ DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \
|
||||
oprofile-y := $(DRIVER_OBJS) common.o
|
||||
oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o
|
||||
oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o
|
||||
oprofile-$(CONFIG_PPC32) += op_model_7450.o
|
||||
|
@ -14,9 +14,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/oprofile.h>
|
||||
#ifndef __powerpc64__
|
||||
#include <linux/slab.h>
|
||||
#endif /* ! __powerpc64__ */
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/errno.h>
|
||||
@ -31,10 +28,6 @@ static struct op_powerpc_model *model;
|
||||
static struct op_counter_config ctr[OP_MAX_COUNTER];
|
||||
static struct op_system_config sys;
|
||||
|
||||
#ifndef __powerpc64__
|
||||
static char *cpu_type;
|
||||
#endif /* ! __powerpc64__ */
|
||||
|
||||
static void op_handle_interrupt(struct pt_regs *regs)
|
||||
{
|
||||
model->handle_interrupt(regs, ctr);
|
||||
@ -53,14 +46,7 @@ static int op_powerpc_setup(void)
|
||||
model->reg_setup(ctr, &sys, model->num_counters);
|
||||
|
||||
/* Configure the registers on all cpus. */
|
||||
#ifdef __powerpc64__
|
||||
on_each_cpu(model->cpu_setup, NULL, 0, 1);
|
||||
#else /* __powerpc64__ */
|
||||
#if 0
|
||||
/* FIXME: Make multi-cpu work */
|
||||
on_each_cpu(model->reg_setup, NULL, 0, 1);
|
||||
#endif
|
||||
#endif /* __powerpc64__ */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -95,7 +81,7 @@ static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
|
||||
{
|
||||
int i;
|
||||
|
||||
#ifdef __powerpc64__
|
||||
#ifdef CONFIG_PPC64
|
||||
/*
|
||||
* There is one mmcr0, mmcr1 and mmcra for setting the events for
|
||||
* all of the counters.
|
||||
@ -103,7 +89,7 @@ static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
|
||||
oprofilefs_create_ulong(sb, root, "mmcr0", &sys.mmcr0);
|
||||
oprofilefs_create_ulong(sb, root, "mmcr1", &sys.mmcr1);
|
||||
oprofilefs_create_ulong(sb, root, "mmcra", &sys.mmcra);
|
||||
#endif /* __powerpc64__ */
|
||||
#endif
|
||||
|
||||
for (i = 0; i < model->num_counters; ++i) {
|
||||
struct dentry *dir;
|
||||
@ -115,65 +101,68 @@ static int op_powerpc_create_files(struct super_block *sb, struct dentry *root)
|
||||
oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
|
||||
oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
|
||||
oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
|
||||
#ifdef __powerpc64__
|
||||
|
||||
/*
|
||||
* We dont support per counter user/kernel selection, but
|
||||
* we leave the entries because userspace expects them
|
||||
* Classic PowerPC doesn't support per-counter
|
||||
* control like this, but the options are
|
||||
* expected, so they remain. For Freescale
|
||||
* Book-E style performance monitors, we do
|
||||
* support them.
|
||||
*/
|
||||
#endif /* __powerpc64__ */
|
||||
oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
|
||||
oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
|
||||
|
||||
#ifndef __powerpc64__
|
||||
/* FIXME: Not sure if this is used */
|
||||
#endif /* ! __powerpc64__ */
|
||||
oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
|
||||
}
|
||||
|
||||
oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
|
||||
oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
|
||||
#ifdef __powerpc64__
|
||||
#ifdef CONFIG_PPC64
|
||||
oprofilefs_create_ulong(sb, root, "backtrace_spinlocks",
|
||||
&sys.backtrace_spinlocks);
|
||||
#endif /* __powerpc64__ */
|
||||
#endif
|
||||
|
||||
/* Default to tracing both kernel and user */
|
||||
sys.enable_kernel = 1;
|
||||
sys.enable_user = 1;
|
||||
#ifdef __powerpc64__
|
||||
#ifdef CONFIG_PPC64
|
||||
/* Turn on backtracing through spinlocks by default */
|
||||
sys.backtrace_spinlocks = 1;
|
||||
#endif /* __powerpc64__ */
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init oprofile_arch_init(struct oprofile_operations *ops)
|
||||
{
|
||||
#ifndef __powerpc64__
|
||||
#ifdef CONFIG_FSL_BOOKE
|
||||
model = &op_model_fsl_booke;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
cpu_type = kmalloc(32, GFP_KERNEL);
|
||||
if (NULL == cpu_type)
|
||||
return -ENOMEM;
|
||||
|
||||
sprintf(cpu_type, "ppc/%s", cur_cpu_spec->cpu_name);
|
||||
|
||||
model->num_counters = cur_cpu_spec->num_pmcs;
|
||||
|
||||
ops->cpu_type = cpu_type;
|
||||
#else /* __powerpc64__ */
|
||||
if (!cur_cpu_spec->oprofile_model || !cur_cpu_spec->oprofile_cpu_type)
|
||||
if (!cur_cpu_spec->oprofile_cpu_type)
|
||||
return -ENODEV;
|
||||
model = cur_cpu_spec->oprofile_model;
|
||||
|
||||
switch (cur_cpu_spec->oprofile_type) {
|
||||
#ifdef CONFIG_PPC64
|
||||
case RS64:
|
||||
model = &op_model_rs64;
|
||||
break;
|
||||
case POWER4:
|
||||
model = &op_model_power4;
|
||||
break;
|
||||
#else
|
||||
case G4:
|
||||
model = &op_model_7450;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_FSL_BOOKE
|
||||
case BOOKE:
|
||||
model = &op_model_fsl_booke;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
model->num_counters = cur_cpu_spec->num_pmcs;
|
||||
|
||||
ops->cpu_type = cur_cpu_spec->oprofile_cpu_type;
|
||||
#endif /* __powerpc64__ */
|
||||
ops->create_files = op_powerpc_create_files;
|
||||
ops->setup = op_powerpc_setup;
|
||||
ops->shutdown = op_powerpc_shutdown;
|
||||
@ -188,8 +177,4 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
|
||||
|
||||
void oprofile_arch_exit(void)
|
||||
{
|
||||
#ifndef __powerpc64__
|
||||
kfree(cpu_type);
|
||||
cpu_type = NULL;
|
||||
#endif /* ! __powerpc64__ */
|
||||
}
|
||||
|
206
arch/powerpc/oprofile/op_model_7450.c
Normal file
206
arch/powerpc/oprofile/op_model_7450.c
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* oprofile/op_model_7450.c
|
||||
*
|
||||
* Freescale 745x/744x oprofile support, based on fsl_booke support
|
||||
* Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
|
||||
*
|
||||
* Copyright (c) 2004 Freescale Semiconductor, Inc
|
||||
*
|
||||
* Author: Andy Fleming
|
||||
* Maintainer: Kumar Gala <galak@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.
|
||||
*/
|
||||
|
||||
#include <linux/oprofile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pmc.h>
|
||||
#include <asm/oprofile_impl.h>
|
||||
|
||||
static unsigned long reset_value[OP_MAX_COUNTER];
|
||||
|
||||
static int oprofile_running;
|
||||
static u32 mmcr0_val, mmcr1_val, mmcr2_val;
|
||||
|
||||
#define MMCR0_PMC1_SHIFT 6
|
||||
#define MMCR0_PMC2_SHIFT 0
|
||||
#define MMCR1_PMC3_SHIFT 27
|
||||
#define MMCR1_PMC4_SHIFT 22
|
||||
#define MMCR1_PMC5_SHIFT 17
|
||||
#define MMCR1_PMC6_SHIFT 11
|
||||
|
||||
#define mmcr0_event1(event) \
|
||||
((event << MMCR0_PMC1_SHIFT) & MMCR0_PMC1SEL)
|
||||
#define mmcr0_event2(event) \
|
||||
((event << MMCR0_PMC2_SHIFT) & MMCR0_PMC2SEL)
|
||||
|
||||
#define mmcr1_event3(event) \
|
||||
((event << MMCR1_PMC3_SHIFT) & MMCR1_PMC3SEL)
|
||||
#define mmcr1_event4(event) \
|
||||
((event << MMCR1_PMC4_SHIFT) & MMCR1_PMC4SEL)
|
||||
#define mmcr1_event5(event) \
|
||||
((event << MMCR1_PMC5_SHIFT) & MMCR1_PMC5SEL)
|
||||
#define mmcr1_event6(event) \
|
||||
((event << MMCR1_PMC6_SHIFT) & MMCR1_PMC6SEL)
|
||||
|
||||
#define MMCR0_INIT (MMCR0_FC | MMCR0_FCS | MMCR0_FCP | MMCR0_FCM1 | MMCR0_FCM0)
|
||||
|
||||
/* Unfreezes the counters on this CPU, enables the interrupt,
|
||||
* enables the counters to trigger the interrupt, and sets the
|
||||
* counters to only count when the mark bit is not set.
|
||||
*/
|
||||
static void pmc_start_ctrs(void)
|
||||
{
|
||||
u32 mmcr0 = mfspr(SPRN_MMCR0);
|
||||
|
||||
mmcr0 &= ~(MMCR0_FC | MMCR0_FCM0);
|
||||
mmcr0 |= (MMCR0_FCECE | MMCR0_PMC1CE | MMCR0_PMCnCE | MMCR0_PMXE);
|
||||
|
||||
mtspr(SPRN_MMCR0, mmcr0);
|
||||
}
|
||||
|
||||
/* Disables the counters on this CPU, and freezes them */
|
||||
static void pmc_stop_ctrs(void)
|
||||
{
|
||||
u32 mmcr0 = mfspr(SPRN_MMCR0);
|
||||
|
||||
mmcr0 |= MMCR0_FC;
|
||||
mmcr0 &= ~(MMCR0_FCECE | MMCR0_PMC1CE | MMCR0_PMCnCE | MMCR0_PMXE);
|
||||
|
||||
mtspr(SPRN_MMCR0, mmcr0);
|
||||
}
|
||||
|
||||
/* Configures the counters on this CPU based on the global
|
||||
* settings */
|
||||
static void fsl7450_cpu_setup(void *unused)
|
||||
{
|
||||
/* freeze all counters */
|
||||
pmc_stop_ctrs();
|
||||
|
||||
mtspr(SPRN_MMCR0, mmcr0_val);
|
||||
mtspr(SPRN_MMCR1, mmcr1_val);
|
||||
mtspr(SPRN_MMCR2, mmcr2_val);
|
||||
}
|
||||
|
||||
#define NUM_CTRS 6
|
||||
|
||||
/* Configures the global settings for the countes on all CPUs. */
|
||||
static void fsl7450_reg_setup(struct op_counter_config *ctr,
|
||||
struct op_system_config *sys,
|
||||
int num_ctrs)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Our counters count up, and "count" refers to
|
||||
* how much before the next interrupt, and we interrupt
|
||||
* on overflow. So we calculate the starting value
|
||||
* which will give us "count" until overflow.
|
||||
* Then we set the events on the enabled counters */
|
||||
for (i = 0; i < NUM_CTRS; ++i)
|
||||
reset_value[i] = 0x80000000UL - ctr[i].count;
|
||||
|
||||
/* Set events for Counters 1 & 2 */
|
||||
mmcr0_val = MMCR0_INIT | mmcr0_event1(ctr[0].event)
|
||||
| mmcr0_event2(ctr[1].event);
|
||||
|
||||
/* Setup user/kernel bits */
|
||||
if (sys->enable_kernel)
|
||||
mmcr0_val &= ~(MMCR0_FCS);
|
||||
|
||||
if (sys->enable_user)
|
||||
mmcr0_val &= ~(MMCR0_FCP);
|
||||
|
||||
/* Set events for Counters 3-6 */
|
||||
mmcr1_val = mmcr1_event3(ctr[2].event)
|
||||
| mmcr1_event4(ctr[3].event)
|
||||
| mmcr1_event5(ctr[4].event)
|
||||
| mmcr1_event6(ctr[5].event);
|
||||
|
||||
mmcr2_val = 0;
|
||||
}
|
||||
|
||||
/* Sets the counters on this CPU to the chosen values, and starts them */
|
||||
static void fsl7450_start(struct op_counter_config *ctr)
|
||||
{
|
||||
int i;
|
||||
|
||||
mtmsr(mfmsr() | MSR_PMM);
|
||||
|
||||
for (i = 0; i < NUM_CTRS; ++i) {
|
||||
if (ctr[i].enabled)
|
||||
ctr_write(i, reset_value[i]);
|
||||
else
|
||||
ctr_write(i, 0);
|
||||
}
|
||||
|
||||
/* Clear the freeze bit, and enable the interrupt.
|
||||
* The counters won't actually start until the rfi clears
|
||||
* the PMM bit */
|
||||
pmc_start_ctrs();
|
||||
|
||||
oprofile_running = 1;
|
||||
}
|
||||
|
||||
/* Stop the counters on this CPU */
|
||||
static void fsl7450_stop(void)
|
||||
{
|
||||
/* freeze counters */
|
||||
pmc_stop_ctrs();
|
||||
|
||||
oprofile_running = 0;
|
||||
|
||||
mb();
|
||||
}
|
||||
|
||||
|
||||
/* Handle the interrupt on this CPU, and log a sample for each
|
||||
* event that triggered the interrupt */
|
||||
static void fsl7450_handle_interrupt(struct pt_regs *regs,
|
||||
struct op_counter_config *ctr)
|
||||
{
|
||||
unsigned long pc;
|
||||
int is_kernel;
|
||||
int val;
|
||||
int i;
|
||||
|
||||
/* set the PMM bit (see comment below) */
|
||||
mtmsr(mfmsr() | MSR_PMM);
|
||||
|
||||
pc = mfspr(SPRN_SIAR);
|
||||
is_kernel = (pc >= KERNELBASE);
|
||||
|
||||
for (i = 0; i < NUM_CTRS; ++i) {
|
||||
val = ctr_read(i);
|
||||
if (val < 0) {
|
||||
if (oprofile_running && ctr[i].enabled) {
|
||||
oprofile_add_pc(pc, is_kernel, i);
|
||||
ctr_write(i, reset_value[i]);
|
||||
} else {
|
||||
ctr_write(i, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The freeze bit was set by the interrupt. */
|
||||
/* Clear the freeze bit, and reenable the interrupt.
|
||||
* The counters won't actually start until the rfi clears
|
||||
* the PMM bit */
|
||||
pmc_start_ctrs();
|
||||
}
|
||||
|
||||
struct op_powerpc_model op_model_7450= {
|
||||
.reg_setup = fsl7450_reg_setup,
|
||||
.cpu_setup = fsl7450_cpu_setup,
|
||||
.start = fsl7450_start,
|
||||
.stop = fsl7450_stop,
|
||||
.handle_interrupt = fsl7450_handle_interrupt,
|
||||
};
|
@ -252,7 +252,7 @@ static unsigned long get_pc(struct pt_regs *regs)
|
||||
return (unsigned long)__va(pc);
|
||||
|
||||
/* Not sure where we were */
|
||||
if (pc < KERNELBASE)
|
||||
if (!is_kernel_addr(pc))
|
||||
/* function descriptor madness */
|
||||
return *((unsigned long *)kernel_unknown_bucket);
|
||||
|
||||
@ -264,7 +264,7 @@ static int get_kernel(unsigned long pc)
|
||||
int is_kernel;
|
||||
|
||||
if (!mmcra_has_sihv) {
|
||||
is_kernel = (pc >= KERNELBASE);
|
||||
is_kernel = is_kernel_addr(pc);
|
||||
} else {
|
||||
unsigned long mmcra = mfspr(SPRN_MMCRA);
|
||||
is_kernel = ((mmcra & MMCRA_SIPR) == 0);
|
||||
|
@ -178,7 +178,6 @@ static void rs64_handle_interrupt(struct pt_regs *regs,
|
||||
int val;
|
||||
int i;
|
||||
unsigned long pc = mfspr(SPRN_SIAR);
|
||||
int is_kernel = (pc >= KERNELBASE);
|
||||
|
||||
/* set the PMM bit (see comment below) */
|
||||
mtmsrd(mfmsr() | MSR_PMM);
|
||||
@ -187,7 +186,7 @@ static void rs64_handle_interrupt(struct pt_regs *regs,
|
||||
val = ctr_read(i);
|
||||
if (val < 0) {
|
||||
if (ctr[i].enabled) {
|
||||
oprofile_add_pc(pc, is_kernel, i);
|
||||
oprofile_add_pc(pc, is_kernel_addr(pc), i);
|
||||
ctr_write(i, reset_value[i]);
|
||||
} else {
|
||||
ctr_write(i, 0);
|
||||
|
13
arch/powerpc/platforms/cell/Kconfig
Normal file
13
arch/powerpc/platforms/cell/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
menu "Cell Broadband Engine options"
|
||||
depends on PPC_CELL
|
||||
|
||||
config SPU_FS
|
||||
tristate "SPU file system"
|
||||
default m
|
||||
depends on PPC_CELL
|
||||
help
|
||||
The SPU file system is used to access Synergistic Processing
|
||||
Units on machines implementing the Broadband Processor
|
||||
Architecture.
|
||||
|
||||
endmenu
|
@ -1,2 +1,10 @@
|
||||
obj-y += interrupt.o iommu.o setup.o spider-pic.o
|
||||
obj-y += pervasive.o
|
||||
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_SPU_FS) += spufs/ spu-base.o
|
||||
|
||||
spu-base-y += spu_base.o spu_priv1.o
|
||||
|
||||
builtin-spufs-$(CONFIG_SPU_FS) += spu_syscalls.o
|
||||
obj-y += $(builtin-spufs-m)
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@ -55,6 +56,7 @@ struct iic_regs {
|
||||
|
||||
struct iic {
|
||||
struct iic_regs __iomem *regs;
|
||||
u8 target_id;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct iic, iic);
|
||||
@ -172,12 +174,11 @@ int iic_get_irq(struct pt_regs *regs)
|
||||
return irq;
|
||||
}
|
||||
|
||||
static struct iic_regs __iomem *find_iic(int cpu)
|
||||
static int setup_iic(int cpu, struct iic *iic)
|
||||
{
|
||||
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;
|
||||
@ -188,20 +189,23 @@ static struct iic_regs __iomem *find_iic(int cpu)
|
||||
|
||||
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);
|
||||
iic->regs = NULL;
|
||||
iic->target_id = 0xff;
|
||||
return -ENODEV;
|
||||
}
|
||||
return iic_regs;
|
||||
|
||||
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);
|
||||
iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@ -227,6 +231,12 @@ void iic_cause_IPI(int cpu, int mesg)
|
||||
out_be64(&per_cpu(iic, cpu).regs->generate, (IIC_NUM_IPIS - 1 - mesg) << 4);
|
||||
}
|
||||
|
||||
u8 iic_get_target_id(int cpu)
|
||||
{
|
||||
return per_cpu(iic, cpu).target_id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iic_get_target_id);
|
||||
|
||||
static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
smp_message_recv(iic_irq_to_ipi(irq), regs);
|
||||
@ -276,7 +286,7 @@ void iic_init_IRQ(void)
|
||||
irq_offset = 0;
|
||||
for_each_cpu(cpu) {
|
||||
iic = &per_cpu(iic, cpu);
|
||||
iic->regs = find_iic(cpu);
|
||||
setup_iic(cpu, iic);
|
||||
if (iic->regs)
|
||||
out_be64(&iic->regs->prio, 0xff);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ extern void iic_setup_cpu(void);
|
||||
extern void iic_local_enable(void);
|
||||
extern void iic_local_disable(void);
|
||||
|
||||
extern u8 iic_get_target_id(int cpu);
|
||||
|
||||
extern void spider_init_IRQ(void);
|
||||
extern int spider_get_irq(unsigned long int_pending);
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/iommu.h>
|
||||
@ -40,6 +42,7 @@
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#include "iommu.h"
|
||||
|
||||
@ -220,8 +223,6 @@ set_iopt_cache(void __iomem *base, unsigned long index,
|
||||
{
|
||||
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);
|
||||
@ -248,66 +249,175 @@ set_iocmd_config(void __iomem *base)
|
||||
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)
|
||||
static void enable_mapping(void __iomem *base, void __iomem *mmio_base)
|
||||
{
|
||||
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);
|
||||
set_iocmd_config(base);
|
||||
set_iost_origin(mmio_base);
|
||||
}
|
||||
|
||||
static void iommu_bus_setup_null(struct pci_bus *b) { }
|
||||
static void iommu_dev_setup_null(struct pci_dev *d) { }
|
||||
static void iommu_bus_setup_null(struct pci_bus *b) { }
|
||||
|
||||
struct cell_iommu {
|
||||
unsigned long base;
|
||||
unsigned long mmio_base;
|
||||
void __iomem *mapped_base;
|
||||
void __iomem *mapped_mmio_base;
|
||||
};
|
||||
|
||||
static struct cell_iommu cell_iommus[NR_CPUS];
|
||||
|
||||
/* 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 cell_map_iommu(void)
|
||||
static void cell_do_map_iommu(struct cell_iommu *iommu,
|
||||
unsigned int ioid,
|
||||
unsigned long map_start,
|
||||
unsigned long map_size)
|
||||
{
|
||||
unsigned long address;
|
||||
void __iomem *base;
|
||||
unsigned long io_address, real_address;
|
||||
void __iomem *ioc_base, *ioc_mmio_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);
|
||||
/* we pretend the io page table was at a very high address */
|
||||
const unsigned long fake_iopt = 0x10000000000ul;
|
||||
const unsigned long io_page_size = 0x1000000; /* use 16M pages */
|
||||
const unsigned long io_segment_size = 0x10000000; /* 256M */
|
||||
|
||||
base = __ioremap(ioc_mmio_base, 0x1000, _PAGE_NO_CACHE);
|
||||
pr_debug("%lx mapped to %p\n", ioc_mmio_base, base);
|
||||
ioc_base = iommu->mapped_base;
|
||||
ioc_mmio_base = iommu->mapped_mmio_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);
|
||||
for (real_address = 0, io_address = 0;
|
||||
io_address <= map_start + map_size;
|
||||
real_address += io_page_size, io_address += io_page_size) {
|
||||
ioste = get_iost_entry(fake_iopt, io_address, io_page_size);
|
||||
if ((real_address % io_segment_size) == 0) /* segment start */
|
||||
set_iost_cache(ioc_mmio_base,
|
||||
io_address >> 28, ioste);
|
||||
index = get_ioc_hash_1way(ioste, io_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));
|
||||
io_address, index, ioste.val);
|
||||
set_iopt_cache(ioc_mmio_base,
|
||||
get_ioc_hash_1way(ioste, io_address),
|
||||
get_ioc_tag(ioste, io_address),
|
||||
get_iopt_entry(real_address-map_start, ioid, IOPT_PROT_RW));
|
||||
}
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void iommu_devnode_setup(struct device_node *d)
|
||||
{
|
||||
unsigned int *ioid;
|
||||
unsigned long *dma_window, map_start, map_size, token;
|
||||
struct cell_iommu *iommu;
|
||||
|
||||
ioid = (unsigned int *)get_property(d, "ioid", NULL);
|
||||
if (!ioid)
|
||||
pr_debug("No ioid entry found !\n");
|
||||
|
||||
dma_window = (unsigned long *)get_property(d, "ibm,dma-window", NULL);
|
||||
if (!dma_window)
|
||||
pr_debug("No ibm,dma-window entry found !\n");
|
||||
|
||||
map_start = dma_window[1];
|
||||
map_size = dma_window[2];
|
||||
token = dma_window[0] >> 32;
|
||||
|
||||
iommu = &cell_iommus[token];
|
||||
|
||||
cell_do_map_iommu(iommu, *ioid, map_start, map_size);
|
||||
}
|
||||
|
||||
static void iommu_bus_setup(struct pci_bus *b)
|
||||
{
|
||||
struct device_node *d = (struct device_node *)b->sysdata;
|
||||
iommu_devnode_setup(d);
|
||||
}
|
||||
|
||||
|
||||
static int cell_map_iommu_hardcoded(int num_nodes)
|
||||
{
|
||||
struct cell_iommu *iommu = NULL;
|
||||
|
||||
pr_debug("%s(%d): Using hardcoded defaults\n", __FUNCTION__, __LINE__);
|
||||
|
||||
/* node 0 */
|
||||
iommu = &cell_iommus[0];
|
||||
iommu->mapped_base = __ioremap(0x20000511000, 0x1000, _PAGE_NO_CACHE);
|
||||
iommu->mapped_mmio_base = __ioremap(0x20000510000, 0x1000, _PAGE_NO_CACHE);
|
||||
|
||||
enable_mapping(iommu->mapped_base, iommu->mapped_mmio_base);
|
||||
|
||||
cell_do_map_iommu(iommu, 0x048a,
|
||||
0x20000000ul,0x20000000ul);
|
||||
|
||||
if (num_nodes < 2)
|
||||
return 0;
|
||||
|
||||
/* node 1 */
|
||||
iommu = &cell_iommus[1];
|
||||
iommu->mapped_base = __ioremap(0x30000511000, 0x1000, _PAGE_NO_CACHE);
|
||||
iommu->mapped_mmio_base = __ioremap(0x30000510000, 0x1000, _PAGE_NO_CACHE);
|
||||
|
||||
enable_mapping(iommu->mapped_base, iommu->mapped_mmio_base);
|
||||
|
||||
cell_do_map_iommu(iommu, 0x048a,
|
||||
0x20000000,0x20000000ul);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int cell_map_iommu(void)
|
||||
{
|
||||
unsigned int num_nodes = 0, *node_id;
|
||||
unsigned long *base, *mmio_base;
|
||||
struct device_node *dn;
|
||||
struct cell_iommu *iommu = NULL;
|
||||
|
||||
/* determine number of nodes (=iommus) */
|
||||
pr_debug("%s(%d): determining number of nodes...", __FUNCTION__, __LINE__);
|
||||
for(dn = of_find_node_by_type(NULL, "cpu");
|
||||
dn;
|
||||
dn = of_find_node_by_type(dn, "cpu")) {
|
||||
node_id = (unsigned int *)get_property(dn, "node-id", NULL);
|
||||
|
||||
if (num_nodes < *node_id)
|
||||
num_nodes = *node_id;
|
||||
}
|
||||
|
||||
num_nodes++;
|
||||
pr_debug("%i found.\n", num_nodes);
|
||||
|
||||
/* map the iommu registers for each node */
|
||||
pr_debug("%s(%d): Looping through nodes\n", __FUNCTION__, __LINE__);
|
||||
for(dn = of_find_node_by_type(NULL, "cpu");
|
||||
dn;
|
||||
dn = of_find_node_by_type(dn, "cpu")) {
|
||||
|
||||
node_id = (unsigned int *)get_property(dn, "node-id", NULL);
|
||||
base = (unsigned long *)get_property(dn, "ioc-cache", NULL);
|
||||
mmio_base = (unsigned long *)get_property(dn, "ioc-translation", NULL);
|
||||
|
||||
if (!base || !mmio_base || !node_id)
|
||||
return cell_map_iommu_hardcoded(num_nodes);
|
||||
|
||||
iommu = &cell_iommus[*node_id];
|
||||
iommu->base = *base;
|
||||
iommu->mmio_base = *mmio_base;
|
||||
|
||||
iommu->mapped_base = __ioremap(*base, 0x1000, _PAGE_NO_CACHE);
|
||||
iommu->mapped_mmio_base = __ioremap(*mmio_base, 0x1000, _PAGE_NO_CACHE);
|
||||
|
||||
enable_mapping(iommu->mapped_base,
|
||||
iommu->mapped_mmio_base);
|
||||
|
||||
/* everything else will be done in iommu_bus_setup */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *cell_alloc_coherent(struct device *hwdev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flag)
|
||||
@ -365,11 +475,28 @@ static int cell_dma_supported(struct device *dev, u64 mask)
|
||||
|
||||
void cell_init_iommu(void)
|
||||
{
|
||||
cell_map_iommu();
|
||||
int setup_bus = 0;
|
||||
|
||||
/* Direct I/O, IOMMU off */
|
||||
ppc_md.iommu_dev_setup = iommu_dev_setup_null;
|
||||
ppc_md.iommu_bus_setup = iommu_bus_setup_null;
|
||||
if (of_find_node_by_path("/mambo")) {
|
||||
pr_info("Not using iommu on systemsim\n");
|
||||
} else {
|
||||
|
||||
if (!(of_chosen &&
|
||||
get_property(of_chosen, "linux,iommu-off", NULL)))
|
||||
setup_bus = cell_map_iommu();
|
||||
|
||||
if (setup_bus) {
|
||||
pr_debug("%s: IOMMU mapping activated\n", __FUNCTION__);
|
||||
ppc_md.iommu_dev_setup = iommu_dev_setup_null;
|
||||
ppc_md.iommu_bus_setup = iommu_bus_setup;
|
||||
} else {
|
||||
pr_debug("%s: IOMMU mapping activated, "
|
||||
"no device action necessary\n", __FUNCTION__);
|
||||
/* 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 = cell_alloc_coherent;
|
||||
pci_dma_ops.free_coherent = cell_free_coherent;
|
||||
|
229
arch/powerpc/platforms/cell/pervasive.c
Normal file
229
arch/powerpc/platforms/cell/pervasive.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* CBE Pervasive Monitor and Debug
|
||||
*
|
||||
* (C) Copyright IBM Corporation 2005
|
||||
*
|
||||
* Authors: Maximino Aguilar (maguilar@us.ibm.com)
|
||||
* Michael N. Day (mnday@us.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.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/reg.h>
|
||||
|
||||
#include "pervasive.h"
|
||||
|
||||
static DEFINE_SPINLOCK(cbe_pervasive_lock);
|
||||
struct cbe_pervasive {
|
||||
struct pmd_regs __iomem *regs;
|
||||
unsigned int thread;
|
||||
};
|
||||
|
||||
/* can't use per_cpu from setup_arch */
|
||||
static struct cbe_pervasive cbe_pervasive[NR_CPUS];
|
||||
|
||||
static void __init cbe_enable_pause_zero(void)
|
||||
{
|
||||
unsigned long thread_switch_control;
|
||||
unsigned long temp_register;
|
||||
struct cbe_pervasive *p;
|
||||
int thread;
|
||||
|
||||
spin_lock_irq(&cbe_pervasive_lock);
|
||||
p = &cbe_pervasive[smp_processor_id()];
|
||||
|
||||
if (!cbe_pervasive->regs)
|
||||
goto out;
|
||||
|
||||
pr_debug("Power Management: CPU %d\n", smp_processor_id());
|
||||
|
||||
/* Enable Pause(0) control bit */
|
||||
temp_register = in_be64(&p->regs->pm_control);
|
||||
|
||||
out_be64(&p->regs->pm_control,
|
||||
temp_register|PMD_PAUSE_ZERO_CONTROL);
|
||||
|
||||
/* Enable DEC and EE interrupt request */
|
||||
thread_switch_control = mfspr(SPRN_TSC_CELL);
|
||||
thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST;
|
||||
|
||||
switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) {
|
||||
case CTRL_CT0:
|
||||
thread_switch_control |= TSC_CELL_DEC_ENABLE_0;
|
||||
thread = 0;
|
||||
break;
|
||||
case CTRL_CT1:
|
||||
thread_switch_control |= TSC_CELL_DEC_ENABLE_1;
|
||||
thread = 1;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "%s: unknown configuration\n",
|
||||
__FUNCTION__);
|
||||
thread = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p->thread != thread)
|
||||
printk(KERN_WARNING "%s: device tree inconsistant, "
|
||||
"cpu %i: %d/%d\n", __FUNCTION__,
|
||||
smp_processor_id(),
|
||||
p->thread, thread);
|
||||
|
||||
mtspr(SPRN_TSC_CELL, thread_switch_control);
|
||||
|
||||
out:
|
||||
spin_unlock_irq(&cbe_pervasive_lock);
|
||||
}
|
||||
|
||||
static void cbe_idle(void)
|
||||
{
|
||||
unsigned long ctrl;
|
||||
|
||||
cbe_enable_pause_zero();
|
||||
|
||||
while (1) {
|
||||
if (!need_resched()) {
|
||||
local_irq_disable();
|
||||
while (!need_resched()) {
|
||||
/* go into low thread priority */
|
||||
HMT_low();
|
||||
|
||||
/*
|
||||
* atomically disable thread execution
|
||||
* and runlatch.
|
||||
* External and Decrementer exceptions
|
||||
* are still handled when the thread
|
||||
* is disabled but now enter in
|
||||
* cbe_system_reset_exception()
|
||||
*/
|
||||
ctrl = mfspr(SPRN_CTRLF);
|
||||
ctrl &= ~(CTRL_RUNLATCH | CTRL_TE);
|
||||
mtspr(SPRN_CTRLT, ctrl);
|
||||
}
|
||||
/* restore thread prio */
|
||||
HMT_medium();
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* turn runlatch on again before scheduling the
|
||||
* process we just woke up
|
||||
*/
|
||||
ppc64_runlatch_on();
|
||||
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
preempt_disable();
|
||||
}
|
||||
}
|
||||
|
||||
int cbe_system_reset_exception(struct pt_regs *regs)
|
||||
{
|
||||
switch (regs->msr & SRR1_WAKEMASK) {
|
||||
case SRR1_WAKEEE:
|
||||
do_IRQ(regs);
|
||||
break;
|
||||
case SRR1_WAKEDEC:
|
||||
timer_interrupt(regs);
|
||||
break;
|
||||
case SRR1_WAKEMT:
|
||||
/* no action required */
|
||||
break;
|
||||
default:
|
||||
/* do system reset */
|
||||
return 0;
|
||||
}
|
||||
/* everything handled */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __init cbe_find_pmd_mmio(int cpu, struct cbe_pervasive *p)
|
||||
{
|
||||
struct device_node *node;
|
||||
unsigned int *int_servers;
|
||||
char *addr;
|
||||
unsigned long real_address;
|
||||
unsigned int size;
|
||||
|
||||
struct pmd_regs __iomem *pmd_mmio_area;
|
||||
int hardid, thread;
|
||||
int proplen;
|
||||
|
||||
pmd_mmio_area = NULL;
|
||||
hardid = get_hard_smp_processor_id(cpu);
|
||||
for (node = NULL; (node = of_find_node_by_type(node, "cpu"));) {
|
||||
int_servers = (void *) get_property(node,
|
||||
"ibm,ppc-interrupt-server#s", &proplen);
|
||||
if (!int_servers) {
|
||||
printk(KERN_WARNING "%s misses "
|
||||
"ibm,ppc-interrupt-server#s property",
|
||||
node->full_name);
|
||||
continue;
|
||||
}
|
||||
for (thread = 0; thread < proplen / sizeof (int); thread++) {
|
||||
if (hardid == int_servers[thread]) {
|
||||
addr = get_property(node, "pervasive", NULL);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "%s: CPU %d not found\n", __FUNCTION__, cpu);
|
||||
return -EINVAL;
|
||||
|
||||
found:
|
||||
real_address = *(unsigned long*) addr;
|
||||
addr += sizeof (unsigned long);
|
||||
size = *(unsigned int*) addr;
|
||||
|
||||
pr_debug("pervasive area for CPU %d at %lx, size %x\n",
|
||||
cpu, real_address, size);
|
||||
p->regs = __ioremap(real_address, size, _PAGE_NO_CACHE);
|
||||
p->thread = thread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init cell_pervasive_init(void)
|
||||
{
|
||||
struct cbe_pervasive *p;
|
||||
int cpu;
|
||||
int ret;
|
||||
|
||||
if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
|
||||
return;
|
||||
|
||||
for_each_cpu(cpu) {
|
||||
p = &cbe_pervasive[cpu];
|
||||
ret = cbe_find_pmd_mmio(cpu, p);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
|
||||
ppc_md.idle_loop = cbe_idle;
|
||||
ppc_md.system_reset_exception = cbe_system_reset_exception;
|
||||
}
|
62
arch/powerpc/platforms/cell/pervasive.h
Normal file
62
arch/powerpc/platforms/cell/pervasive.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Cell Pervasive Monitor and Debug interface and HW structures
|
||||
*
|
||||
* (C) Copyright IBM Corporation 2005
|
||||
*
|
||||
* Authors: Maximino Aguilar (maguilar@us.ibm.com)
|
||||
* David J. Erb (djerb@us.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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PERVASIVE_H
|
||||
#define PERVASIVE_H
|
||||
|
||||
struct pmd_regs {
|
||||
u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */
|
||||
|
||||
/* Thermal Sensor Registers */
|
||||
u64 ts_ctsr1; /* 0x0800 */
|
||||
u64 ts_ctsr2; /* 0x0808 */
|
||||
u64 ts_mtsr1; /* 0x0810 */
|
||||
u64 ts_mtsr2; /* 0x0818 */
|
||||
u64 ts_itr1; /* 0x0820 */
|
||||
u64 ts_itr2; /* 0x0828 */
|
||||
u64 ts_gitr; /* 0x0830 */
|
||||
u64 ts_isr; /* 0x0838 */
|
||||
u64 ts_imr; /* 0x0840 */
|
||||
u64 tm_cr1; /* 0x0848 */
|
||||
u64 tm_cr2; /* 0x0850 */
|
||||
u64 tm_simr; /* 0x0858 */
|
||||
u64 tm_tpr; /* 0x0860 */
|
||||
u64 tm_str1; /* 0x0868 */
|
||||
u64 tm_str2; /* 0x0870 */
|
||||
u64 tm_tsr; /* 0x0878 */
|
||||
|
||||
/* Power Management */
|
||||
u64 pm_control; /* 0x0880 */
|
||||
#define PMD_PAUSE_ZERO_CONTROL 0x10000
|
||||
u64 pm_status; /* 0x0888 */
|
||||
|
||||
/* Time Base Register */
|
||||
u64 tbr; /* 0x0890 */
|
||||
|
||||
u8 pad_0x0898_0x1000 [0x1000 - 0x0898]; /* 0x0898 */
|
||||
};
|
||||
|
||||
void __init cell_pervasive_init(void);
|
||||
|
||||
#endif
|
@ -33,6 +33,7 @@
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
@ -48,6 +49,7 @@
|
||||
|
||||
#include "interrupt.h"
|
||||
#include "iommu.h"
|
||||
#include "pervasive.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
@ -67,6 +69,77 @@ void cell_show_cpuinfo(struct seq_file *m)
|
||||
of_node_put(root);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSEMEM
|
||||
static int __init find_spu_node_id(struct device_node *spe)
|
||||
{
|
||||
unsigned int *id;
|
||||
#ifdef CONFIG_NUMA
|
||||
struct device_node *cpu;
|
||||
cpu = spe->parent->parent;
|
||||
id = (unsigned int *)get_property(cpu, "node-id", NULL);
|
||||
#else
|
||||
id = NULL;
|
||||
#endif
|
||||
return id ? *id : 0;
|
||||
}
|
||||
|
||||
static void __init cell_spuprop_present(struct device_node *spe,
|
||||
const char *prop, int early)
|
||||
{
|
||||
struct address_prop {
|
||||
unsigned long address;
|
||||
unsigned int len;
|
||||
} __attribute__((packed)) *p;
|
||||
int proplen;
|
||||
|
||||
unsigned long start_pfn, end_pfn, pfn;
|
||||
int node_id;
|
||||
|
||||
p = (void*)get_property(spe, prop, &proplen);
|
||||
WARN_ON(proplen != sizeof (*p));
|
||||
|
||||
node_id = find_spu_node_id(spe);
|
||||
|
||||
start_pfn = p->address >> PAGE_SHIFT;
|
||||
end_pfn = (p->address + p->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
|
||||
/* We need to call memory_present *before* the call to sparse_init,
|
||||
but we can initialize the page structs only *after* that call.
|
||||
Thus, we're being called twice. */
|
||||
if (early)
|
||||
memory_present(node_id, start_pfn, end_pfn);
|
||||
else {
|
||||
/* As the pages backing SPU LS and I/O are outside the range
|
||||
of regular memory, their page structs were not initialized
|
||||
by free_area_init. Do it here instead. */
|
||||
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
set_page_links(page, ZONE_DMA, node_id, pfn);
|
||||
set_page_count(page, 1);
|
||||
reset_page_mapcount(page);
|
||||
SetPageReserved(page);
|
||||
INIT_LIST_HEAD(&page->lru);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __init cell_spumem_init(int early)
|
||||
{
|
||||
struct device_node *node;
|
||||
for (node = of_find_node_by_type(NULL, "spe");
|
||||
node; node = of_find_node_by_type(node, "spe")) {
|
||||
cell_spuprop_present(node, "local-store", early);
|
||||
cell_spuprop_present(node, "problem", early);
|
||||
cell_spuprop_present(node, "priv1", early);
|
||||
cell_spuprop_present(node, "priv2", early);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void __init cell_spumem_init(int early)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cell_progress(char *s, unsigned short hex)
|
||||
{
|
||||
printk("*** %04x : %s\n", hex, s ? s : "");
|
||||
@ -93,11 +166,14 @@ static void __init cell_setup_arch(void)
|
||||
init_pci_config_tokens();
|
||||
find_and_init_phbs();
|
||||
spider_init_IRQ();
|
||||
cell_pervasive_init();
|
||||
#ifdef CONFIG_DUMMY_CONSOLE
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
|
||||
mmio_nvram_init();
|
||||
|
||||
cell_spumem_init(0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -113,6 +189,8 @@ static void __init cell_init_early(void)
|
||||
|
||||
ppc64_interrupt_controller = IC_CELL_PIC;
|
||||
|
||||
cell_spumem_init(1);
|
||||
|
||||
DBG(" <- cell_init_early()\n");
|
||||
}
|
||||
|
||||
@ -125,6 +203,15 @@ static int __init cell_probe(int platform)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cell has no legacy IO; anything calling this function has to
|
||||
* fail or bad things will happen
|
||||
*/
|
||||
static int cell_check_legacy_ioport(unsigned int baseport)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
struct machdep_calls __initdata cell_md = {
|
||||
.probe = cell_probe,
|
||||
.setup_arch = cell_setup_arch,
|
||||
@ -137,5 +224,11 @@ struct machdep_calls __initdata cell_md = {
|
||||
.get_rtc_time = rtas_get_rtc_time,
|
||||
.set_rtc_time = rtas_set_rtc_time,
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.check_legacy_ioport = cell_check_legacy_ioport,
|
||||
.progress = cell_progress,
|
||||
#ifdef CONFIG_KEXEC
|
||||
.machine_kexec = default_machine_kexec,
|
||||
.machine_kexec_prepare = default_machine_kexec_prepare,
|
||||
.machine_crash_shutdown = default_machine_crash_shutdown,
|
||||
#endif
|
||||
};
|
||||
|
711
arch/powerpc/platforms/cell/spu_base.c
Normal file
711
arch/powerpc/platforms/cell/spu_base.c
Normal file
@ -0,0 +1,711 @@
|
||||
/*
|
||||
* Low-level SPU handling
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
#include "interrupt.h"
|
||||
|
||||
static int __spu_trap_invalid_dma(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
force_sig(SIGBUS, /* info, */ current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_dma_align(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
force_sig(SIGBUS, /* info, */ current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_error(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
force_sig(SIGILL, /* info, */ current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spu_restart_dma(struct spu *spu)
|
||||
{
|
||||
struct spu_priv2 __iomem *priv2 = spu->priv2;
|
||||
|
||||
if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags))
|
||||
out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
|
||||
}
|
||||
|
||||
static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
|
||||
{
|
||||
struct spu_priv2 __iomem *priv2 = spu->priv2;
|
||||
struct mm_struct *mm = spu->mm;
|
||||
u64 esid, vsid;
|
||||
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
|
||||
if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE, &spu->flags)) {
|
||||
/* SLBs are pre-loaded for context switch, so
|
||||
* we should never get here!
|
||||
*/
|
||||
printk("%s: invalid access during switch!\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
if (!mm || (REGION_ID(ea) != USER_REGION_ID)) {
|
||||
/* Future: support kernel segments so that drivers
|
||||
* can use SPUs.
|
||||
*/
|
||||
pr_debug("invalid region access at %016lx\n", ea);
|
||||
return 1;
|
||||
}
|
||||
|
||||
esid = (ea & ESID_MASK) | SLB_ESID_V;
|
||||
vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | SLB_VSID_USER;
|
||||
if (in_hugepage_area(mm->context, ea))
|
||||
vsid |= SLB_VSID_L;
|
||||
|
||||
out_be64(&priv2->slb_index_W, spu->slb_replace);
|
||||
out_be64(&priv2->slb_vsid_RW, vsid);
|
||||
out_be64(&priv2->slb_esid_RW, esid);
|
||||
|
||||
spu->slb_replace++;
|
||||
if (spu->slb_replace >= 8)
|
||||
spu->slb_replace = 0;
|
||||
|
||||
spu_restart_dma(spu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX
|
||||
static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
|
||||
/* Handle kernel space hash faults immediately.
|
||||
User hash faults need to be deferred to process context. */
|
||||
if ((dsisr & MFC_DSISR_PTE_NOT_FOUND)
|
||||
&& REGION_ID(ea) != USER_REGION_ID
|
||||
&& hash_page(ea, _PAGE_PRESENT, 0x300) == 0) {
|
||||
spu_restart_dma(spu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE, &spu->flags)) {
|
||||
printk("%s: invalid access during switch!\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spu->dar = ea;
|
||||
spu->dsisr = dsisr;
|
||||
mb();
|
||||
if (spu->stop_callback)
|
||||
spu->stop_callback(spu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_mailbox(struct spu *spu)
|
||||
{
|
||||
if (spu->ibox_callback)
|
||||
spu->ibox_callback(spu);
|
||||
|
||||
/* atomically disable SPU mailbox interrupts */
|
||||
spin_lock(&spu->register_lock);
|
||||
spu_int_mask_and(spu, 2, ~0x1);
|
||||
spin_unlock(&spu->register_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_stop(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
spu->stop_code = in_be32(&spu->problem->spu_status_R);
|
||||
if (spu->stop_callback)
|
||||
spu->stop_callback(spu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_halt(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
spu->stop_code = in_be32(&spu->problem->spu_status_R);
|
||||
if (spu->stop_callback)
|
||||
spu->stop_callback(spu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_tag_group(struct spu *spu)
|
||||
{
|
||||
pr_debug("%s\n", __FUNCTION__);
|
||||
/* wake_up(&spu->dma_wq); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spu_trap_spubox(struct spu *spu)
|
||||
{
|
||||
if (spu->wbox_callback)
|
||||
spu->wbox_callback(spu);
|
||||
|
||||
/* atomically disable SPU mailbox interrupts */
|
||||
spin_lock(&spu->register_lock);
|
||||
spu_int_mask_and(spu, 2, ~0x10);
|
||||
spin_unlock(&spu->register_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
spu_irq_class_0(int irq, void *data, struct pt_regs *regs)
|
||||
{
|
||||
struct spu *spu;
|
||||
|
||||
spu = data;
|
||||
spu->class_0_pending = 1;
|
||||
if (spu->stop_callback)
|
||||
spu->stop_callback(spu);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int
|
||||
spu_irq_class_0_bottom(struct spu *spu)
|
||||
{
|
||||
unsigned long stat, mask;
|
||||
|
||||
spu->class_0_pending = 0;
|
||||
|
||||
mask = spu_int_mask_get(spu, 0);
|
||||
stat = spu_int_stat_get(spu, 0);
|
||||
|
||||
stat &= mask;
|
||||
|
||||
if (stat & 1) /* invalid MFC DMA */
|
||||
__spu_trap_invalid_dma(spu);
|
||||
|
||||
if (stat & 2) /* invalid DMA alignment */
|
||||
__spu_trap_dma_align(spu);
|
||||
|
||||
if (stat & 4) /* error on SPU */
|
||||
__spu_trap_error(spu);
|
||||
|
||||
spu_int_stat_clear(spu, 0, stat);
|
||||
|
||||
return (stat & 0x7) ? -EIO : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_irq_class_0_bottom);
|
||||
|
||||
static irqreturn_t
|
||||
spu_irq_class_1(int irq, void *data, struct pt_regs *regs)
|
||||
{
|
||||
struct spu *spu;
|
||||
unsigned long stat, mask, dar, dsisr;
|
||||
|
||||
spu = data;
|
||||
|
||||
/* atomically read & clear class1 status. */
|
||||
spin_lock(&spu->register_lock);
|
||||
mask = spu_int_mask_get(spu, 1);
|
||||
stat = spu_int_stat_get(spu, 1) & mask;
|
||||
dar = spu_mfc_dar_get(spu);
|
||||
dsisr = spu_mfc_dsisr_get(spu);
|
||||
if (stat & 2) /* mapping fault */
|
||||
spu_mfc_dsisr_set(spu, 0ul);
|
||||
spu_int_stat_clear(spu, 1, stat);
|
||||
spin_unlock(&spu->register_lock);
|
||||
|
||||
if (stat & 1) /* segment fault */
|
||||
__spu_trap_data_seg(spu, dar);
|
||||
|
||||
if (stat & 2) { /* mapping fault */
|
||||
__spu_trap_data_map(spu, dar, dsisr);
|
||||
}
|
||||
|
||||
if (stat & 4) /* ls compare & suspend on get */
|
||||
;
|
||||
|
||||
if (stat & 8) /* ls compare & suspend on put */
|
||||
;
|
||||
|
||||
return stat ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_irq_class_1_bottom);
|
||||
|
||||
static irqreturn_t
|
||||
spu_irq_class_2(int irq, void *data, struct pt_regs *regs)
|
||||
{
|
||||
struct spu *spu;
|
||||
unsigned long stat;
|
||||
unsigned long mask;
|
||||
|
||||
spu = data;
|
||||
stat = spu_int_stat_get(spu, 2);
|
||||
mask = spu_int_mask_get(spu, 2);
|
||||
|
||||
pr_debug("class 2 interrupt %d, %lx, %lx\n", irq, stat, mask);
|
||||
|
||||
stat &= mask;
|
||||
|
||||
if (stat & 1) /* PPC core mailbox */
|
||||
__spu_trap_mailbox(spu);
|
||||
|
||||
if (stat & 2) /* SPU stop-and-signal */
|
||||
__spu_trap_stop(spu);
|
||||
|
||||
if (stat & 4) /* SPU halted */
|
||||
__spu_trap_halt(spu);
|
||||
|
||||
if (stat & 8) /* DMA tag group complete */
|
||||
__spu_trap_tag_group(spu);
|
||||
|
||||
if (stat & 0x10) /* SPU mailbox threshold */
|
||||
__spu_trap_spubox(spu);
|
||||
|
||||
spu_int_stat_clear(spu, 2, stat);
|
||||
return stat ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
spu_request_irqs(struct spu *spu)
|
||||
{
|
||||
int ret;
|
||||
int irq_base;
|
||||
|
||||
irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET;
|
||||
|
||||
snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0", spu->number);
|
||||
ret = request_irq(irq_base + spu->isrc,
|
||||
spu_irq_class_0, 0, spu->irq_c0, spu);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1", spu->number);
|
||||
ret = request_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc,
|
||||
spu_irq_class_1, 0, spu->irq_c1, spu);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2", spu->number);
|
||||
ret = request_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc,
|
||||
spu_irq_class_2, 0, spu->irq_c2, spu);
|
||||
if (ret)
|
||||
goto out2;
|
||||
goto out;
|
||||
|
||||
out2:
|
||||
free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu);
|
||||
out1:
|
||||
free_irq(irq_base + spu->isrc, spu);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
spu_free_irqs(struct spu *spu)
|
||||
{
|
||||
int irq_base;
|
||||
|
||||
irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET;
|
||||
|
||||
free_irq(irq_base + spu->isrc, spu);
|
||||
free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu);
|
||||
free_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc, spu);
|
||||
}
|
||||
|
||||
static LIST_HEAD(spu_list);
|
||||
static DECLARE_MUTEX(spu_mutex);
|
||||
|
||||
static void spu_init_channels(struct spu *spu)
|
||||
{
|
||||
static const struct {
|
||||
unsigned channel;
|
||||
unsigned count;
|
||||
} zero_list[] = {
|
||||
{ 0x00, 1, }, { 0x01, 1, }, { 0x03, 1, }, { 0x04, 1, },
|
||||
{ 0x18, 1, }, { 0x19, 1, }, { 0x1b, 1, }, { 0x1d, 1, },
|
||||
}, count_list[] = {
|
||||
{ 0x00, 0, }, { 0x03, 0, }, { 0x04, 0, }, { 0x15, 16, },
|
||||
{ 0x17, 1, }, { 0x18, 0, }, { 0x19, 0, }, { 0x1b, 0, },
|
||||
{ 0x1c, 1, }, { 0x1d, 0, }, { 0x1e, 1, },
|
||||
};
|
||||
struct spu_priv2 __iomem *priv2;
|
||||
int i;
|
||||
|
||||
priv2 = spu->priv2;
|
||||
|
||||
/* initialize all channel data to zero */
|
||||
for (i = 0; i < ARRAY_SIZE(zero_list); i++) {
|
||||
int count;
|
||||
|
||||
out_be64(&priv2->spu_chnlcntptr_RW, zero_list[i].channel);
|
||||
for (count = 0; count < zero_list[i].count; count++)
|
||||
out_be64(&priv2->spu_chnldata_RW, 0);
|
||||
}
|
||||
|
||||
/* initialize channel counts to meaningful values */
|
||||
for (i = 0; i < ARRAY_SIZE(count_list); i++) {
|
||||
out_be64(&priv2->spu_chnlcntptr_RW, count_list[i].channel);
|
||||
out_be64(&priv2->spu_chnlcnt_RW, count_list[i].count);
|
||||
}
|
||||
}
|
||||
|
||||
struct spu *spu_alloc(void)
|
||||
{
|
||||
struct spu *spu;
|
||||
|
||||
down(&spu_mutex);
|
||||
if (!list_empty(&spu_list)) {
|
||||
spu = list_entry(spu_list.next, struct spu, list);
|
||||
list_del_init(&spu->list);
|
||||
pr_debug("Got SPU %x %d\n", spu->isrc, spu->number);
|
||||
} else {
|
||||
pr_debug("No SPU left\n");
|
||||
spu = NULL;
|
||||
}
|
||||
up(&spu_mutex);
|
||||
|
||||
if (spu)
|
||||
spu_init_channels(spu);
|
||||
|
||||
return spu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_alloc);
|
||||
|
||||
void spu_free(struct spu *spu)
|
||||
{
|
||||
down(&spu_mutex);
|
||||
list_add_tail(&spu->list, &spu_list);
|
||||
up(&spu_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_free);
|
||||
|
||||
static int spu_handle_mm_fault(struct spu *spu)
|
||||
{
|
||||
struct mm_struct *mm = spu->mm;
|
||||
struct vm_area_struct *vma;
|
||||
u64 ea, dsisr, is_write;
|
||||
int ret;
|
||||
|
||||
ea = spu->dar;
|
||||
dsisr = spu->dsisr;
|
||||
#if 0
|
||||
if (!IS_VALID_EA(ea)) {
|
||||
return -EFAULT;
|
||||
}
|
||||
#endif /* XXX */
|
||||
if (mm == NULL) {
|
||||
return -EFAULT;
|
||||
}
|
||||
if (mm->pgd == NULL) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, ea);
|
||||
if (!vma)
|
||||
goto bad_area;
|
||||
if (vma->vm_start <= ea)
|
||||
goto good_area;
|
||||
if (!(vma->vm_flags & VM_GROWSDOWN))
|
||||
goto bad_area;
|
||||
#if 0
|
||||
if (expand_stack(vma, ea))
|
||||
goto bad_area;
|
||||
#endif /* XXX */
|
||||
good_area:
|
||||
is_write = dsisr & MFC_DSISR_ACCESS_PUT;
|
||||
if (is_write) {
|
||||
if (!(vma->vm_flags & VM_WRITE))
|
||||
goto bad_area;
|
||||
} else {
|
||||
if (dsisr & MFC_DSISR_ACCESS_DENIED)
|
||||
goto bad_area;
|
||||
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
|
||||
goto bad_area;
|
||||
}
|
||||
ret = 0;
|
||||
switch (handle_mm_fault(mm, vma, ea, is_write)) {
|
||||
case VM_FAULT_MINOR:
|
||||
current->min_flt++;
|
||||
break;
|
||||
case VM_FAULT_MAJOR:
|
||||
current->maj_flt++;
|
||||
break;
|
||||
case VM_FAULT_SIGBUS:
|
||||
ret = -EFAULT;
|
||||
goto bad_area;
|
||||
case VM_FAULT_OOM:
|
||||
ret = -ENOMEM;
|
||||
goto bad_area;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
up_read(&mm->mmap_sem);
|
||||
return ret;
|
||||
|
||||
bad_area:
|
||||
up_read(&mm->mmap_sem);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
int spu_irq_class_1_bottom(struct spu *spu)
|
||||
{
|
||||
u64 ea, dsisr, access, error = 0UL;
|
||||
int ret = 0;
|
||||
|
||||
ea = spu->dar;
|
||||
dsisr = spu->dsisr;
|
||||
if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
|
||||
access = (_PAGE_PRESENT | _PAGE_USER);
|
||||
access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL;
|
||||
if (hash_page(ea, access, 0x300) != 0)
|
||||
error |= CLASS1_ENABLE_STORAGE_FAULT_INTR;
|
||||
}
|
||||
if ((error & CLASS1_ENABLE_STORAGE_FAULT_INTR) ||
|
||||
(dsisr & MFC_DSISR_ACCESS_DENIED)) {
|
||||
if ((ret = spu_handle_mm_fault(spu)) != 0)
|
||||
error |= CLASS1_ENABLE_STORAGE_FAULT_INTR;
|
||||
else
|
||||
error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR;
|
||||
}
|
||||
spu->dar = 0UL;
|
||||
spu->dsisr = 0UL;
|
||||
if (!error) {
|
||||
spu_restart_dma(spu);
|
||||
} else {
|
||||
__spu_trap_invalid_dma(spu);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spu_irq_setaffinity(struct spu *spu, int cpu)
|
||||
{
|
||||
u64 target = iic_get_target_id(cpu);
|
||||
u64 route = target << 48 | target << 32 | target << 16;
|
||||
spu_int_route_set(spu, route);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_irq_setaffinity);
|
||||
|
||||
static void __iomem * __init map_spe_prop(struct device_node *n,
|
||||
const char *name)
|
||||
{
|
||||
struct address_prop {
|
||||
unsigned long address;
|
||||
unsigned int len;
|
||||
} __attribute__((packed)) *prop;
|
||||
|
||||
void *p;
|
||||
int proplen;
|
||||
|
||||
p = get_property(n, name, &proplen);
|
||||
if (proplen != sizeof (struct address_prop))
|
||||
return NULL;
|
||||
|
||||
prop = p;
|
||||
|
||||
return ioremap(prop->address, prop->len);
|
||||
}
|
||||
|
||||
static void spu_unmap(struct spu *spu)
|
||||
{
|
||||
iounmap(spu->priv2);
|
||||
iounmap(spu->priv1);
|
||||
iounmap(spu->problem);
|
||||
iounmap((u8 __iomem *)spu->local_store);
|
||||
}
|
||||
|
||||
static int __init spu_map_device(struct spu *spu, struct device_node *spe)
|
||||
{
|
||||
char *prop;
|
||||
int ret;
|
||||
|
||||
ret = -ENODEV;
|
||||
prop = get_property(spe, "isrc", NULL);
|
||||
if (!prop)
|
||||
goto out;
|
||||
spu->isrc = *(unsigned int *)prop;
|
||||
|
||||
spu->name = get_property(spe, "name", NULL);
|
||||
if (!spu->name)
|
||||
goto out;
|
||||
|
||||
prop = get_property(spe, "local-store", NULL);
|
||||
if (!prop)
|
||||
goto out;
|
||||
spu->local_store_phys = *(unsigned long *)prop;
|
||||
|
||||
/* we use local store as ram, not io memory */
|
||||
spu->local_store = (void __force *)map_spe_prop(spe, "local-store");
|
||||
if (!spu->local_store)
|
||||
goto out;
|
||||
|
||||
spu->problem= map_spe_prop(spe, "problem");
|
||||
if (!spu->problem)
|
||||
goto out_unmap;
|
||||
|
||||
spu->priv1= map_spe_prop(spe, "priv1");
|
||||
/* priv1 is not available on a hypervisor */
|
||||
|
||||
spu->priv2= map_spe_prop(spe, "priv2");
|
||||
if (!spu->priv2)
|
||||
goto out_unmap;
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
out_unmap:
|
||||
spu_unmap(spu);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init find_spu_node_id(struct device_node *spe)
|
||||
{
|
||||
unsigned int *id;
|
||||
struct device_node *cpu;
|
||||
|
||||
cpu = spe->parent->parent;
|
||||
id = (unsigned int *)get_property(cpu, "node-id", NULL);
|
||||
|
||||
return id ? *id : 0;
|
||||
}
|
||||
|
||||
static int __init create_spu(struct device_node *spe)
|
||||
{
|
||||
struct spu *spu;
|
||||
int ret;
|
||||
static int number;
|
||||
|
||||
ret = -ENOMEM;
|
||||
spu = kmalloc(sizeof (*spu), GFP_KERNEL);
|
||||
if (!spu)
|
||||
goto out;
|
||||
|
||||
ret = spu_map_device(spu, spe);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
spu->node = find_spu_node_id(spe);
|
||||
spu->stop_code = 0;
|
||||
spu->slb_replace = 0;
|
||||
spu->mm = NULL;
|
||||
spu->ctx = NULL;
|
||||
spu->rq = NULL;
|
||||
spu->pid = 0;
|
||||
spu->class_0_pending = 0;
|
||||
spu->flags = 0UL;
|
||||
spu->dar = 0UL;
|
||||
spu->dsisr = 0UL;
|
||||
spin_lock_init(&spu->register_lock);
|
||||
|
||||
spu_mfc_sdr_set(spu, mfspr(SPRN_SDR1));
|
||||
spu_mfc_sr1_set(spu, 0x33);
|
||||
|
||||
spu->ibox_callback = NULL;
|
||||
spu->wbox_callback = NULL;
|
||||
spu->stop_callback = NULL;
|
||||
|
||||
down(&spu_mutex);
|
||||
spu->number = number++;
|
||||
ret = spu_request_irqs(spu);
|
||||
if (ret)
|
||||
goto out_unmap;
|
||||
|
||||
list_add(&spu->list, &spu_list);
|
||||
up(&spu_mutex);
|
||||
|
||||
pr_debug(KERN_DEBUG "Using SPE %s %02x %p %p %p %p %d\n",
|
||||
spu->name, spu->isrc, spu->local_store,
|
||||
spu->problem, spu->priv1, spu->priv2, spu->number);
|
||||
goto out;
|
||||
|
||||
out_unmap:
|
||||
up(&spu_mutex);
|
||||
spu_unmap(spu);
|
||||
out_free:
|
||||
kfree(spu);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void destroy_spu(struct spu *spu)
|
||||
{
|
||||
list_del_init(&spu->list);
|
||||
|
||||
spu_free_irqs(spu);
|
||||
spu_unmap(spu);
|
||||
kfree(spu);
|
||||
}
|
||||
|
||||
static void cleanup_spu_base(void)
|
||||
{
|
||||
struct spu *spu, *tmp;
|
||||
down(&spu_mutex);
|
||||
list_for_each_entry_safe(spu, tmp, &spu_list, list)
|
||||
destroy_spu(spu);
|
||||
up(&spu_mutex);
|
||||
}
|
||||
module_exit(cleanup_spu_base);
|
||||
|
||||
static int __init init_spu_base(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
int ret;
|
||||
|
||||
ret = -ENODEV;
|
||||
for (node = of_find_node_by_type(NULL, "spe");
|
||||
node; node = of_find_node_by_type(node, "spe")) {
|
||||
ret = create_spu(node);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "%s: Error initializing %s\n",
|
||||
__FUNCTION__, node->name);
|
||||
cleanup_spu_base();
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* in some old firmware versions, the spe is called 'spc', so we
|
||||
look for that as well */
|
||||
for (node = of_find_node_by_type(NULL, "spc");
|
||||
node; node = of_find_node_by_type(node, "spc")) {
|
||||
ret = create_spu(node);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "%s: Error initializing %s\n",
|
||||
__FUNCTION__, node->name);
|
||||
cleanup_spu_base();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
module_init(init_spu_base);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
|
133
arch/powerpc/platforms/cell/spu_priv1.c
Normal file
133
arch/powerpc/platforms/cell/spu_priv1.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* access to SPU privileged registers
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/spu.h>
|
||||
|
||||
void spu_int_mask_and(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
u64 old_mask;
|
||||
|
||||
old_mask = in_be64(&spu->priv1->int_mask_RW[class]);
|
||||
out_be64(&spu->priv1->int_mask_RW[class], old_mask & mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_mask_and);
|
||||
|
||||
void spu_int_mask_or(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
u64 old_mask;
|
||||
|
||||
old_mask = in_be64(&spu->priv1->int_mask_RW[class]);
|
||||
out_be64(&spu->priv1->int_mask_RW[class], old_mask | mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_mask_or);
|
||||
|
||||
void spu_int_mask_set(struct spu *spu, int class, u64 mask)
|
||||
{
|
||||
out_be64(&spu->priv1->int_mask_RW[class], mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_mask_set);
|
||||
|
||||
u64 spu_int_mask_get(struct spu *spu, int class)
|
||||
{
|
||||
return in_be64(&spu->priv1->int_mask_RW[class]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_mask_get);
|
||||
|
||||
void spu_int_stat_clear(struct spu *spu, int class, u64 stat)
|
||||
{
|
||||
out_be64(&spu->priv1->int_stat_RW[class], stat);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_stat_clear);
|
||||
|
||||
u64 spu_int_stat_get(struct spu *spu, int class)
|
||||
{
|
||||
return in_be64(&spu->priv1->int_stat_RW[class]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_stat_get);
|
||||
|
||||
void spu_int_route_set(struct spu *spu, u64 route)
|
||||
{
|
||||
out_be64(&spu->priv1->int_route_RW, route);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_int_route_set);
|
||||
|
||||
u64 spu_mfc_dar_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->mfc_dar_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_dar_get);
|
||||
|
||||
u64 spu_mfc_dsisr_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->mfc_dsisr_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_dsisr_get);
|
||||
|
||||
void spu_mfc_dsisr_set(struct spu *spu, u64 dsisr)
|
||||
{
|
||||
out_be64(&spu->priv1->mfc_dsisr_RW, dsisr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_dsisr_set);
|
||||
|
||||
void spu_mfc_sdr_set(struct spu *spu, u64 sdr)
|
||||
{
|
||||
out_be64(&spu->priv1->mfc_sdr_RW, sdr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_sdr_set);
|
||||
|
||||
void spu_mfc_sr1_set(struct spu *spu, u64 sr1)
|
||||
{
|
||||
out_be64(&spu->priv1->mfc_sr1_RW, sr1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_sr1_set);
|
||||
|
||||
u64 spu_mfc_sr1_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->mfc_sr1_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_sr1_get);
|
||||
|
||||
void spu_mfc_tclass_id_set(struct spu *spu, u64 tclass_id)
|
||||
{
|
||||
out_be64(&spu->priv1->mfc_tclass_id_RW, tclass_id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_tclass_id_set);
|
||||
|
||||
u64 spu_mfc_tclass_id_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->mfc_tclass_id_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_mfc_tclass_id_get);
|
||||
|
||||
void spu_tlb_invalidate(struct spu *spu)
|
||||
{
|
||||
out_be64(&spu->priv1->tlb_invalidate_entry_W, 0ul);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_tlb_invalidate);
|
||||
|
||||
void spu_resource_allocation_groupID_set(struct spu *spu, u64 id)
|
||||
{
|
||||
out_be64(&spu->priv1->resource_allocation_groupID_RW, id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_resource_allocation_groupID_set);
|
||||
|
||||
u64 spu_resource_allocation_groupID_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->resource_allocation_groupID_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_resource_allocation_groupID_get);
|
||||
|
||||
void spu_resource_allocation_enable_set(struct spu *spu, u64 enable)
|
||||
{
|
||||
out_be64(&spu->priv1->resource_allocation_enable_RW, enable);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_resource_allocation_enable_set);
|
||||
|
||||
u64 spu_resource_allocation_enable_get(struct spu *spu)
|
||||
{
|
||||
return in_be64(&spu->priv1->resource_allocation_enable_RW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spu_resource_allocation_enable_get);
|
88
arch/powerpc/platforms/cell/spu_syscalls.c
Normal file
88
arch/powerpc/platforms/cell/spu_syscalls.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPU file system -- system call stubs
|
||||
*
|
||||
* (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/file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include <asm/spu.h>
|
||||
|
||||
struct spufs_calls spufs_calls = {
|
||||
.owner = NULL,
|
||||
};
|
||||
|
||||
/* These stub syscalls are needed to have the actual implementation
|
||||
* within a loadable module. When spufs is built into the kernel,
|
||||
* this file is not used and the syscalls directly enter the fs code */
|
||||
|
||||
asmlinkage long sys_spu_create(const char __user *name,
|
||||
unsigned int flags, mode_t mode)
|
||||
{
|
||||
long ret;
|
||||
struct module *owner = spufs_calls.owner;
|
||||
|
||||
ret = -ENOSYS;
|
||||
if (owner && try_module_get(owner)) {
|
||||
ret = spufs_calls.create_thread(name, flags, mode);
|
||||
module_put(owner);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
|
||||
{
|
||||
long ret;
|
||||
struct file *filp;
|
||||
int fput_needed;
|
||||
struct module *owner = spufs_calls.owner;
|
||||
|
||||
ret = -ENOSYS;
|
||||
if (owner && try_module_get(owner)) {
|
||||
ret = -EBADF;
|
||||
filp = fget_light(fd, &fput_needed);
|
||||
if (filp) {
|
||||
ret = spufs_calls.spu_run(filp, unpc, ustatus);
|
||||
fput_light(filp, fput_needed);
|
||||
}
|
||||
module_put(owner);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int register_spu_syscalls(struct spufs_calls *calls)
|
||||
{
|
||||
if (spufs_calls.owner)
|
||||
return -EBUSY;
|
||||
|
||||
spufs_calls.create_thread = calls->create_thread;
|
||||
spufs_calls.spu_run = calls->spu_run;
|
||||
smp_mb();
|
||||
spufs_calls.owner = calls->owner;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_spu_syscalls);
|
||||
|
||||
void unregister_spu_syscalls(struct spufs_calls *calls)
|
||||
{
|
||||
BUG_ON(spufs_calls.owner != calls->owner);
|
||||
spufs_calls.owner = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_spu_syscalls);
|
54
arch/powerpc/platforms/cell/spufs/Makefile
Normal file
54
arch/powerpc/platforms/cell/spufs/Makefile
Normal file
@ -0,0 +1,54 @@
|
||||
obj-$(CONFIG_SPU_FS) += spufs.o
|
||||
spufs-y += inode.o file.o context.o switch.o syscalls.o
|
||||
spufs-y += sched.o backing_ops.o hw_ops.o run.o
|
||||
|
||||
# Rules to build switch.o with the help of SPU tool chain
|
||||
SPU_CROSS := spu-
|
||||
SPU_CC := $(SPU_CROSS)gcc
|
||||
SPU_AS := $(SPU_CROSS)gcc
|
||||
SPU_LD := $(SPU_CROSS)ld
|
||||
SPU_OBJCOPY := $(SPU_CROSS)objcopy
|
||||
SPU_CFLAGS := -O2 -Wall -I$(srctree)/include -I$(objtree)/include2
|
||||
SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include -I$(objtree)/include2
|
||||
SPU_LDFLAGS := -N -Ttext=0x0
|
||||
|
||||
$(obj)/switch.o: $(obj)/spu_save_dump.h $(obj)/spu_restore_dump.h
|
||||
|
||||
# Compile SPU files
|
||||
cmd_spu_cc = $(SPU_CC) $(SPU_CFLAGS) -c -o $@ $<
|
||||
quiet_cmd_spu_cc = SPU_CC $@
|
||||
$(obj)/spu_%.o: $(src)/spu_%.c
|
||||
$(call if_changed,spu_cc)
|
||||
|
||||
# Assemble SPU files
|
||||
cmd_spu_as = $(SPU_AS) $(SPU_AFLAGS) -o $@ $<
|
||||
quiet_cmd_spu_as = SPU_AS $@
|
||||
$(obj)/spu_%.o: $(src)/spu_%.S
|
||||
$(call if_changed,spu_as)
|
||||
|
||||
# Link SPU Executables
|
||||
cmd_spu_ld = $(SPU_LD) $(SPU_LDFLAGS) -o $@ $^
|
||||
quiet_cmd_spu_ld = SPU_LD $@
|
||||
$(obj)/spu_%: $(obj)/spu_%_crt0.o $(obj)/spu_%.o
|
||||
$(call if_changed,spu_ld)
|
||||
|
||||
# Copy into binary format
|
||||
cmd_spu_objcopy = $(SPU_OBJCOPY) -O binary $< $@
|
||||
quiet_cmd_spu_objcopy = OBJCOPY $@
|
||||
$(obj)/spu_%.bin: $(src)/spu_%
|
||||
$(call if_changed,spu_objcopy)
|
||||
|
||||
# create C code from ELF executable
|
||||
cmd_hexdump = ( \
|
||||
echo "/*" ; \
|
||||
echo " * $*_dump.h: Copyright (C) 2005 IBM." ; \
|
||||
echo " * Hex-dump auto generated from $*.c." ; \
|
||||
echo " * Do not edit!" ; \
|
||||
echo " */" ; \
|
||||
echo "static unsigned int $*_code[] __page_aligned = {" ; \
|
||||
hexdump -v -e '"0x" 4/1 "%02x" "," "\n"' $< ; \
|
||||
echo "};" ; \
|
||||
) > $@
|
||||
quiet_cmd_hexdump = HEXDUMP $@
|
||||
$(obj)/%_dump.h: $(obj)/%.bin
|
||||
$(call if_changed,hexdump)
|
308
arch/powerpc/platforms/cell/spufs/backing_ops.c
Normal file
308
arch/powerpc/platforms/cell/spufs/backing_ops.c
Normal file
@ -0,0 +1,308 @@
|
||||
/* backing_ops.c - query/set operations on saved SPU context.
|
||||
*
|
||||
* Copyright (C) IBM 2005
|
||||
* Author: Mark Nutter <mnutter@us.ibm.com>
|
||||
*
|
||||
* These register operations allow SPUFS to operate on saved
|
||||
* SPU contexts rather than hardware.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include "spufs.h"
|
||||
|
||||
/*
|
||||
* Reads/writes to various problem and priv2 registers require
|
||||
* state changes, i.e. generate SPU events, modify channel
|
||||
* counts, etc.
|
||||
*/
|
||||
|
||||
static void gen_spu_event(struct spu_context *ctx, u32 event)
|
||||
{
|
||||
u64 ch0_cnt;
|
||||
u64 ch0_data;
|
||||
u64 ch1_data;
|
||||
|
||||
ch0_cnt = ctx->csa.spu_chnlcnt_RW[0];
|
||||
ch0_data = ctx->csa.spu_chnldata_RW[0];
|
||||
ch1_data = ctx->csa.spu_chnldata_RW[1];
|
||||
ctx->csa.spu_chnldata_RW[0] |= event;
|
||||
if ((ch0_cnt == 0) && !(ch0_data & event) && (ch1_data & event)) {
|
||||
ctx->csa.spu_chnlcnt_RW[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int spu_backing_mbox_read(struct spu_context *ctx, u32 * data)
|
||||
{
|
||||
u32 mbox_stat;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
mbox_stat = ctx->csa.prob.mb_stat_R;
|
||||
if (mbox_stat & 0x0000ff) {
|
||||
/* Read the first available word.
|
||||
* Implementation note: the depth
|
||||
* of pu_mb_R is currently 1.
|
||||
*/
|
||||
*data = ctx->csa.prob.pu_mb_R;
|
||||
ctx->csa.prob.mb_stat_R &= ~(0x0000ff);
|
||||
ctx->csa.spu_chnlcnt_RW[28] = 1;
|
||||
gen_spu_event(ctx, MFC_PU_MAILBOX_AVAILABLE_EVENT);
|
||||
ret = 4;
|
||||
}
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 spu_backing_mbox_stat_read(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.prob.mb_stat_R;
|
||||
}
|
||||
|
||||
static unsigned int spu_backing_mbox_stat_poll(struct spu_context *ctx,
|
||||
unsigned int events)
|
||||
{
|
||||
int ret;
|
||||
u32 stat;
|
||||
|
||||
ret = 0;
|
||||
spin_lock_irq(&ctx->csa.register_lock);
|
||||
stat = ctx->csa.prob.mb_stat_R;
|
||||
|
||||
/* if the requested event is there, return the poll
|
||||
mask, otherwise enable the interrupt to get notified,
|
||||
but first mark any pending interrupts as done so
|
||||
we don't get woken up unnecessarily */
|
||||
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (stat & 0xff0000)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
else {
|
||||
ctx->csa.priv1.int_stat_class0_RW &= ~0x1;
|
||||
ctx->csa.priv1.int_mask_class2_RW |= 0x1;
|
||||
}
|
||||
}
|
||||
if (events & (POLLOUT | POLLWRNORM)) {
|
||||
if (stat & 0x00ff00)
|
||||
ret = POLLOUT | POLLWRNORM;
|
||||
else {
|
||||
ctx->csa.priv1.int_stat_class0_RW &= ~0x10;
|
||||
ctx->csa.priv1.int_mask_class2_RW |= 0x10;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ctx->csa.register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spu_backing_ibox_read(struct spu_context *ctx, u32 * data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
if (ctx->csa.prob.mb_stat_R & 0xff0000) {
|
||||
/* Read the first available word.
|
||||
* Implementation note: the depth
|
||||
* of puint_mb_R is currently 1.
|
||||
*/
|
||||
*data = ctx->csa.priv2.puint_mb_R;
|
||||
ctx->csa.prob.mb_stat_R &= ~(0xff0000);
|
||||
ctx->csa.spu_chnlcnt_RW[30] = 1;
|
||||
gen_spu_event(ctx, MFC_PU_INT_MAILBOX_AVAILABLE_EVENT);
|
||||
ret = 4;
|
||||
} else {
|
||||
/* make sure we get woken up by the interrupt */
|
||||
ctx->csa.priv1.int_mask_class2_RW |= 0x1UL;
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spu_backing_wbox_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
if ((ctx->csa.prob.mb_stat_R) & 0x00ff00) {
|
||||
int slot = ctx->csa.spu_chnlcnt_RW[29];
|
||||
int avail = (ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8;
|
||||
|
||||
/* We have space to write wbox_data.
|
||||
* Implementation note: the depth
|
||||
* of spu_mb_W is currently 4.
|
||||
*/
|
||||
BUG_ON(avail != (4 - slot));
|
||||
ctx->csa.spu_mailbox_data[slot] = data;
|
||||
ctx->csa.spu_chnlcnt_RW[29] = ++slot;
|
||||
ctx->csa.prob.mb_stat_R = (((4 - slot) & 0xff) << 8);
|
||||
gen_spu_event(ctx, MFC_SPU_MAILBOX_WRITTEN_EVENT);
|
||||
ret = 4;
|
||||
} else {
|
||||
/* make sure we get woken up by the interrupt when space
|
||||
becomes available */
|
||||
ctx->csa.priv1.int_mask_class2_RW |= 0x10;
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 spu_backing_signal1_read(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.spu_chnldata_RW[3];
|
||||
}
|
||||
|
||||
static void spu_backing_signal1_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
if (ctx->csa.priv2.spu_cfg_RW & 0x1)
|
||||
ctx->csa.spu_chnldata_RW[3] |= data;
|
||||
else
|
||||
ctx->csa.spu_chnldata_RW[3] = data;
|
||||
ctx->csa.spu_chnlcnt_RW[3] = 1;
|
||||
gen_spu_event(ctx, MFC_SIGNAL_1_EVENT);
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
}
|
||||
|
||||
static u32 spu_backing_signal2_read(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.spu_chnldata_RW[4];
|
||||
}
|
||||
|
||||
static void spu_backing_signal2_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
if (ctx->csa.priv2.spu_cfg_RW & 0x2)
|
||||
ctx->csa.spu_chnldata_RW[4] |= data;
|
||||
else
|
||||
ctx->csa.spu_chnldata_RW[4] = data;
|
||||
ctx->csa.spu_chnlcnt_RW[4] = 1;
|
||||
gen_spu_event(ctx, MFC_SIGNAL_2_EVENT);
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
}
|
||||
|
||||
static void spu_backing_signal1_type_set(struct spu_context *ctx, u64 val)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
tmp = ctx->csa.priv2.spu_cfg_RW;
|
||||
if (val)
|
||||
tmp |= 1;
|
||||
else
|
||||
tmp &= ~1;
|
||||
ctx->csa.priv2.spu_cfg_RW = tmp;
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
}
|
||||
|
||||
static u64 spu_backing_signal1_type_get(struct spu_context *ctx)
|
||||
{
|
||||
return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0);
|
||||
}
|
||||
|
||||
static void spu_backing_signal2_type_set(struct spu_context *ctx, u64 val)
|
||||
{
|
||||
u64 tmp;
|
||||
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
tmp = ctx->csa.priv2.spu_cfg_RW;
|
||||
if (val)
|
||||
tmp |= 2;
|
||||
else
|
||||
tmp &= ~2;
|
||||
ctx->csa.priv2.spu_cfg_RW = tmp;
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
}
|
||||
|
||||
static u64 spu_backing_signal2_type_get(struct spu_context *ctx)
|
||||
{
|
||||
return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0);
|
||||
}
|
||||
|
||||
static u32 spu_backing_npc_read(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.prob.spu_npc_RW;
|
||||
}
|
||||
|
||||
static void spu_backing_npc_write(struct spu_context *ctx, u32 val)
|
||||
{
|
||||
ctx->csa.prob.spu_npc_RW = val;
|
||||
}
|
||||
|
||||
static u32 spu_backing_status_read(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.prob.spu_status_R;
|
||||
}
|
||||
|
||||
static char *spu_backing_get_ls(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->csa.lscsa->ls;
|
||||
}
|
||||
|
||||
static void spu_backing_runcntl_write(struct spu_context *ctx, u32 val)
|
||||
{
|
||||
spin_lock(&ctx->csa.register_lock);
|
||||
ctx->csa.prob.spu_runcntl_RW = val;
|
||||
if (val & SPU_RUNCNTL_RUNNABLE) {
|
||||
ctx->csa.prob.spu_status_R |= SPU_STATUS_RUNNING;
|
||||
} else {
|
||||
ctx->csa.prob.spu_status_R &= ~SPU_STATUS_RUNNING;
|
||||
}
|
||||
spin_unlock(&ctx->csa.register_lock);
|
||||
}
|
||||
|
||||
static void spu_backing_runcntl_stop(struct spu_context *ctx)
|
||||
{
|
||||
spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP);
|
||||
}
|
||||
|
||||
struct spu_context_ops spu_backing_ops = {
|
||||
.mbox_read = spu_backing_mbox_read,
|
||||
.mbox_stat_read = spu_backing_mbox_stat_read,
|
||||
.mbox_stat_poll = spu_backing_mbox_stat_poll,
|
||||
.ibox_read = spu_backing_ibox_read,
|
||||
.wbox_write = spu_backing_wbox_write,
|
||||
.signal1_read = spu_backing_signal1_read,
|
||||
.signal1_write = spu_backing_signal1_write,
|
||||
.signal2_read = spu_backing_signal2_read,
|
||||
.signal2_write = spu_backing_signal2_write,
|
||||
.signal1_type_set = spu_backing_signal1_type_set,
|
||||
.signal1_type_get = spu_backing_signal1_type_get,
|
||||
.signal2_type_set = spu_backing_signal2_type_set,
|
||||
.signal2_type_get = spu_backing_signal2_type_get,
|
||||
.npc_read = spu_backing_npc_read,
|
||||
.npc_write = spu_backing_npc_write,
|
||||
.status_read = spu_backing_status_read,
|
||||
.get_ls = spu_backing_get_ls,
|
||||
.runcntl_write = spu_backing_runcntl_write,
|
||||
.runcntl_stop = spu_backing_runcntl_stop,
|
||||
};
|
167
arch/powerpc/platforms/cell/spufs/context.c
Normal file
167
arch/powerpc/platforms/cell/spufs/context.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPU file system -- SPU context management
|
||||
*
|
||||
* (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/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include "spufs.h"
|
||||
|
||||
struct spu_context *alloc_spu_context(struct address_space *local_store)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
ctx = kmalloc(sizeof *ctx, GFP_KERNEL);
|
||||
if (!ctx)
|
||||
goto out;
|
||||
/* Binding to physical processor deferred
|
||||
* until spu_activate().
|
||||
*/
|
||||
spu_init_csa(&ctx->csa);
|
||||
if (!ctx->csa.lscsa) {
|
||||
goto out_free;
|
||||
}
|
||||
spin_lock_init(&ctx->mmio_lock);
|
||||
kref_init(&ctx->kref);
|
||||
init_rwsem(&ctx->state_sema);
|
||||
init_MUTEX(&ctx->run_sema);
|
||||
init_waitqueue_head(&ctx->ibox_wq);
|
||||
init_waitqueue_head(&ctx->wbox_wq);
|
||||
init_waitqueue_head(&ctx->stop_wq);
|
||||
ctx->ibox_fasync = NULL;
|
||||
ctx->wbox_fasync = NULL;
|
||||
ctx->state = SPU_STATE_SAVED;
|
||||
ctx->local_store = local_store;
|
||||
ctx->spu = NULL;
|
||||
ctx->ops = &spu_backing_ops;
|
||||
ctx->owner = get_task_mm(current);
|
||||
goto out;
|
||||
out_free:
|
||||
kfree(ctx);
|
||||
ctx = NULL;
|
||||
out:
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void destroy_spu_context(struct kref *kref)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
ctx = container_of(kref, struct spu_context, kref);
|
||||
down_write(&ctx->state_sema);
|
||||
spu_deactivate(ctx);
|
||||
ctx->ibox_fasync = NULL;
|
||||
ctx->wbox_fasync = NULL;
|
||||
up_write(&ctx->state_sema);
|
||||
spu_fini_csa(&ctx->csa);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
struct spu_context * get_spu_context(struct spu_context *ctx)
|
||||
{
|
||||
kref_get(&ctx->kref);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int put_spu_context(struct spu_context *ctx)
|
||||
{
|
||||
return kref_put(&ctx->kref, &destroy_spu_context);
|
||||
}
|
||||
|
||||
/* give up the mm reference when the context is about to be destroyed */
|
||||
void spu_forget(struct spu_context *ctx)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
spu_acquire_saved(ctx);
|
||||
mm = ctx->owner;
|
||||
ctx->owner = NULL;
|
||||
mmput(mm);
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
void spu_acquire(struct spu_context *ctx)
|
||||
{
|
||||
down_read(&ctx->state_sema);
|
||||
}
|
||||
|
||||
void spu_release(struct spu_context *ctx)
|
||||
{
|
||||
up_read(&ctx->state_sema);
|
||||
}
|
||||
|
||||
void spu_unmap_mappings(struct spu_context *ctx)
|
||||
{
|
||||
unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
|
||||
}
|
||||
|
||||
int spu_acquire_runnable(struct spu_context *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
down_read(&ctx->state_sema);
|
||||
if (ctx->state == SPU_STATE_RUNNABLE) {
|
||||
ctx->spu->prio = current->prio;
|
||||
return 0;
|
||||
}
|
||||
up_read(&ctx->state_sema);
|
||||
|
||||
down_write(&ctx->state_sema);
|
||||
/* ctx is about to be freed, can't acquire any more */
|
||||
if (!ctx->owner) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->state == SPU_STATE_SAVED) {
|
||||
ret = spu_activate(ctx, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
ctx->state = SPU_STATE_RUNNABLE;
|
||||
}
|
||||
|
||||
downgrade_write(&ctx->state_sema);
|
||||
/* On success, we return holding the lock */
|
||||
|
||||
return ret;
|
||||
out:
|
||||
/* Release here, to simplify calling code. */
|
||||
up_write(&ctx->state_sema);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void spu_acquire_saved(struct spu_context *ctx)
|
||||
{
|
||||
down_read(&ctx->state_sema);
|
||||
|
||||
if (ctx->state == SPU_STATE_SAVED)
|
||||
return;
|
||||
|
||||
up_read(&ctx->state_sema);
|
||||
down_write(&ctx->state_sema);
|
||||
|
||||
if (ctx->state == SPU_STATE_RUNNABLE) {
|
||||
spu_deactivate(ctx);
|
||||
ctx->state = SPU_STATE_SAVED;
|
||||
}
|
||||
|
||||
downgrade_write(&ctx->state_sema);
|
||||
}
|
794
arch/powerpc/platforms/cell/spufs/file.c
Normal file
794
arch/powerpc/platforms/cell/spufs/file.c
Normal file
@ -0,0 +1,794 @@
|
||||
/*
|
||||
* SPU file system -- file contents
|
||||
*
|
||||
* (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/fs.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "spufs.h"
|
||||
|
||||
|
||||
static int
|
||||
spufs_mem_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct spufs_inode_info *i = SPUFS_I(inode);
|
||||
file->private_data = i->i_ctx;
|
||||
file->f_mapping = i->i_ctx->local_store;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
spufs_mem_read(struct file *file, char __user *buffer,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
char *local_store;
|
||||
int ret;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
local_store = ctx->ops->get_ls(ctx);
|
||||
ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE);
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
spufs_mem_write(struct file *file, const char __user *buffer,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
char *local_store;
|
||||
int ret;
|
||||
|
||||
size = min_t(ssize_t, LS_SIZE - *pos, size);
|
||||
if (size <= 0)
|
||||
return -EFBIG;
|
||||
*pos += size;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
local_store = ctx->ops->get_ls(ctx);
|
||||
ret = copy_from_user(local_store + *pos - size,
|
||||
buffer, size) ? -EFAULT : size;
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPARSEMEM
|
||||
static struct page *
|
||||
spufs_mem_mmap_nopage(struct vm_area_struct *vma,
|
||||
unsigned long address, int *type)
|
||||
{
|
||||
struct page *page = NOPAGE_SIGBUS;
|
||||
|
||||
struct spu_context *ctx = vma->vm_file->private_data;
|
||||
unsigned long offset = address - vma->vm_start;
|
||||
offset += vma->vm_pgoff << PAGE_SHIFT;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
if (ctx->state == SPU_STATE_SAVED)
|
||||
page = vmalloc_to_page(ctx->csa.lscsa->ls + offset);
|
||||
else
|
||||
page = pfn_to_page((ctx->spu->local_store_phys + offset)
|
||||
>> PAGE_SHIFT);
|
||||
|
||||
spu_release(ctx);
|
||||
|
||||
if (type)
|
||||
*type = VM_FAULT_MINOR;
|
||||
|
||||
page_cache_get(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct spufs_mem_mmap_vmops = {
|
||||
.nopage = spufs_mem_mmap_nopage,
|
||||
};
|
||||
|
||||
static int
|
||||
spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
if (!(vma->vm_flags & VM_SHARED))
|
||||
return -EINVAL;
|
||||
|
||||
/* FIXME: */
|
||||
vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
|
||||
| _PAGE_NO_CACHE);
|
||||
|
||||
vma->vm_ops = &spufs_mem_mmap_vmops;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct file_operations spufs_mem_fops = {
|
||||
.open = spufs_mem_open,
|
||||
.read = spufs_mem_read,
|
||||
.write = spufs_mem_write,
|
||||
.llseek = generic_file_llseek,
|
||||
#ifdef CONFIG_SPARSEMEM
|
||||
.mmap = spufs_mem_mmap,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int
|
||||
spufs_regs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct spufs_inode_info *i = SPUFS_I(inode);
|
||||
file->private_data = i->i_ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
spufs_regs_read(struct file *file, char __user *buffer,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
int ret;
|
||||
|
||||
spu_acquire_saved(ctx);
|
||||
|
||||
ret = simple_read_from_buffer(buffer, size, pos,
|
||||
lscsa->gprs, sizeof lscsa->gprs);
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
spufs_regs_write(struct file *file, const char __user *buffer,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
int ret;
|
||||
|
||||
size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
|
||||
if (size <= 0)
|
||||
return -EFBIG;
|
||||
*pos += size;
|
||||
|
||||
spu_acquire_saved(ctx);
|
||||
|
||||
ret = copy_from_user(lscsa->gprs + *pos - size,
|
||||
buffer, size) ? -EFAULT : size;
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_regs_fops = {
|
||||
.open = spufs_regs_open,
|
||||
.read = spufs_regs_read,
|
||||
.write = spufs_regs_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
spufs_fpcr_read(struct file *file, char __user * buffer,
|
||||
size_t size, loff_t * pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
int ret;
|
||||
|
||||
spu_acquire_saved(ctx);
|
||||
|
||||
ret = simple_read_from_buffer(buffer, size, pos,
|
||||
&lscsa->fpcr, sizeof(lscsa->fpcr));
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
spufs_fpcr_write(struct file *file, const char __user * buffer,
|
||||
size_t size, loff_t * pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
int ret;
|
||||
|
||||
size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
|
||||
if (size <= 0)
|
||||
return -EFBIG;
|
||||
*pos += size;
|
||||
|
||||
spu_acquire_saved(ctx);
|
||||
|
||||
ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
|
||||
buffer, size) ? -EFAULT : size;
|
||||
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_fpcr_fops = {
|
||||
.open = spufs_regs_open,
|
||||
.read = spufs_fpcr_read,
|
||||
.write = spufs_fpcr_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/* generic open function for all pipe-like files */
|
||||
static int spufs_pipe_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct spufs_inode_info *i = SPUFS_I(inode);
|
||||
file->private_data = i->i_ctx;
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 mbox_data;
|
||||
int ret;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ret = ctx->ops->mbox_read(ctx, &mbox_data);
|
||||
spu_release(ctx);
|
||||
|
||||
if (!ret)
|
||||
return -EAGAIN;
|
||||
|
||||
if (copy_to_user(buf, &mbox_data, sizeof mbox_data))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_mbox_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_mbox_read,
|
||||
};
|
||||
|
||||
static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 mbox_stat;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
|
||||
|
||||
spu_release(ctx);
|
||||
|
||||
if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_mbox_stat_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_mbox_stat_read,
|
||||
};
|
||||
|
||||
/* low-level ibox access function */
|
||||
size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
|
||||
{
|
||||
return ctx->ops->ibox_read(ctx, data);
|
||||
}
|
||||
|
||||
static int spufs_ibox_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
|
||||
return fasync_helper(fd, file, on, &ctx->ibox_fasync);
|
||||
}
|
||||
|
||||
/* interrupt-level ibox callback function. */
|
||||
void spufs_ibox_callback(struct spu *spu)
|
||||
{
|
||||
struct spu_context *ctx = spu->ctx;
|
||||
|
||||
wake_up_all(&ctx->ibox_wq);
|
||||
kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
|
||||
}
|
||||
|
||||
static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 ibox_data;
|
||||
ssize_t ret;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
ret = 0;
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
if (!spu_ibox_read(ctx, &ibox_data))
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
ret = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
|
||||
}
|
||||
|
||||
spu_release(ctx);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = 4;
|
||||
if (copy_to_user(buf, &ibox_data, sizeof ibox_data))
|
||||
ret = -EFAULT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
unsigned int mask;
|
||||
|
||||
poll_wait(file, &ctx->ibox_wq, wait);
|
||||
|
||||
spu_acquire(ctx);
|
||||
mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM);
|
||||
spu_release(ctx);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_ibox_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_ibox_read,
|
||||
.poll = spufs_ibox_poll,
|
||||
.fasync = spufs_ibox_fasync,
|
||||
};
|
||||
|
||||
static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 ibox_stat;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
|
||||
spu_release(ctx);
|
||||
|
||||
if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_ibox_stat_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_ibox_stat_read,
|
||||
};
|
||||
|
||||
/* low-level mailbox write */
|
||||
size_t spu_wbox_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
return ctx->ops->wbox_write(ctx, data);
|
||||
}
|
||||
|
||||
static int spufs_wbox_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
int ret;
|
||||
|
||||
ret = fasync_helper(fd, file, on, &ctx->wbox_fasync);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* interrupt-level wbox callback function. */
|
||||
void spufs_wbox_callback(struct spu *spu)
|
||||
{
|
||||
struct spu_context *ctx = spu->ctx;
|
||||
|
||||
wake_up_all(&ctx->wbox_wq);
|
||||
kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
|
||||
}
|
||||
|
||||
static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 wbox_data;
|
||||
int ret;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&wbox_data, buf, sizeof wbox_data))
|
||||
return -EFAULT;
|
||||
|
||||
spu_acquire(ctx);
|
||||
|
||||
ret = 0;
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
if (!spu_wbox_write(ctx, wbox_data))
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
ret = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
|
||||
}
|
||||
|
||||
spu_release(ctx);
|
||||
|
||||
return ret ? ret : sizeof wbox_data;
|
||||
}
|
||||
|
||||
static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
unsigned int mask;
|
||||
|
||||
poll_wait(file, &ctx->wbox_wq, wait);
|
||||
|
||||
spu_acquire(ctx);
|
||||
mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM);
|
||||
spu_release(ctx);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_wbox_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.write = spufs_wbox_write,
|
||||
.poll = spufs_wbox_poll,
|
||||
.fasync = spufs_wbox_fasync,
|
||||
};
|
||||
|
||||
static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 wbox_stat;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
|
||||
spu_release(ctx);
|
||||
|
||||
if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_wbox_stat_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_wbox_stat_read,
|
||||
};
|
||||
|
||||
static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx = file->private_data;
|
||||
u32 data;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
data = ctx->ops->signal1_read(ctx);
|
||||
spu_release(ctx);
|
||||
|
||||
if (copy_to_user(buf, &data, 4))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
u32 data;
|
||||
|
||||
ctx = file->private_data;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&data, buf, 4))
|
||||
return -EFAULT;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ctx->ops->signal1_write(ctx, data);
|
||||
spu_release(ctx);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_signal1_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_signal1_read,
|
||||
.write = spufs_signal1_write,
|
||||
};
|
||||
|
||||
static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
u32 data;
|
||||
|
||||
ctx = file->private_data;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
spu_acquire(ctx);
|
||||
data = ctx->ops->signal2_read(ctx);
|
||||
spu_release(ctx);
|
||||
|
||||
if (copy_to_user(buf, &data, 4))
|
||||
return -EFAULT;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *pos)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
u32 data;
|
||||
|
||||
ctx = file->private_data;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&data, buf, 4))
|
||||
return -EFAULT;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ctx->ops->signal2_write(ctx, data);
|
||||
spu_release(ctx);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
static struct file_operations spufs_signal2_fops = {
|
||||
.open = spufs_pipe_open,
|
||||
.read = spufs_signal2_read,
|
||||
.write = spufs_signal2_write,
|
||||
};
|
||||
|
||||
static void spufs_signal1_type_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ctx->ops->signal1_type_set(ctx, val);
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_signal1_type_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
u64 ret;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ret = ctx->ops->signal1_type_get(ctx);
|
||||
spu_release(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
|
||||
spufs_signal1_type_set, "%llu");
|
||||
|
||||
static void spufs_signal2_type_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ctx->ops->signal2_type_set(ctx, val);
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_signal2_type_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
u64 ret;
|
||||
|
||||
spu_acquire(ctx);
|
||||
ret = ctx->ops->signal2_type_get(ctx);
|
||||
spu_release(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
|
||||
spufs_signal2_type_set, "%llu");
|
||||
|
||||
static void spufs_npc_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
spu_acquire(ctx);
|
||||
ctx->ops->npc_write(ctx, val);
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_npc_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
u64 ret;
|
||||
spu_acquire(ctx);
|
||||
ret = ctx->ops->npc_read(ctx);
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n")
|
||||
|
||||
static void spufs_decr_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
spu_acquire_saved(ctx);
|
||||
lscsa->decr.slot[0] = (u32) val;
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_decr_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
u64 ret;
|
||||
spu_acquire_saved(ctx);
|
||||
ret = lscsa->decr.slot[0];
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
|
||||
"%llx\n")
|
||||
|
||||
static void spufs_decr_status_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
spu_acquire_saved(ctx);
|
||||
lscsa->decr_status.slot[0] = (u32) val;
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_decr_status_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
u64 ret;
|
||||
spu_acquire_saved(ctx);
|
||||
ret = lscsa->decr_status.slot[0];
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
|
||||
spufs_decr_status_set, "%llx\n")
|
||||
|
||||
static void spufs_spu_tag_mask_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
spu_acquire_saved(ctx);
|
||||
lscsa->tag_mask.slot[0] = (u32) val;
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_spu_tag_mask_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
u64 ret;
|
||||
spu_acquire_saved(ctx);
|
||||
ret = lscsa->tag_mask.slot[0];
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_spu_tag_mask_ops, spufs_spu_tag_mask_get,
|
||||
spufs_spu_tag_mask_set, "%llx\n")
|
||||
|
||||
static void spufs_event_mask_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
spu_acquire_saved(ctx);
|
||||
lscsa->event_mask.slot[0] = (u32) val;
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_event_mask_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
u64 ret;
|
||||
spu_acquire_saved(ctx);
|
||||
ret = lscsa->event_mask.slot[0];
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
|
||||
spufs_event_mask_set, "%llx\n")
|
||||
|
||||
static void spufs_srr0_set(void *data, u64 val)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
spu_acquire_saved(ctx);
|
||||
lscsa->srr0.slot[0] = (u32) val;
|
||||
spu_release(ctx);
|
||||
}
|
||||
|
||||
static u64 spufs_srr0_get(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu_lscsa *lscsa = ctx->csa.lscsa;
|
||||
u64 ret;
|
||||
spu_acquire_saved(ctx);
|
||||
ret = lscsa->srr0.slot[0];
|
||||
spu_release(ctx);
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
|
||||
"%llx\n")
|
||||
|
||||
struct tree_descr spufs_dir_contents[] = {
|
||||
{ "mem", &spufs_mem_fops, 0666, },
|
||||
{ "regs", &spufs_regs_fops, 0666, },
|
||||
{ "mbox", &spufs_mbox_fops, 0444, },
|
||||
{ "ibox", &spufs_ibox_fops, 0444, },
|
||||
{ "wbox", &spufs_wbox_fops, 0222, },
|
||||
{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
|
||||
{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
|
||||
{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
|
||||
{ "signal1", &spufs_signal1_fops, 0666, },
|
||||
{ "signal2", &spufs_signal2_fops, 0666, },
|
||||
{ "signal1_type", &spufs_signal1_type, 0666, },
|
||||
{ "signal2_type", &spufs_signal2_type, 0666, },
|
||||
{ "npc", &spufs_npc_ops, 0666, },
|
||||
{ "fpcr", &spufs_fpcr_fops, 0666, },
|
||||
{ "decr", &spufs_decr_ops, 0666, },
|
||||
{ "decr_status", &spufs_decr_status_ops, 0666, },
|
||||
{ "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, },
|
||||
{ "event_mask", &spufs_event_mask_ops, 0666, },
|
||||
{ "srr0", &spufs_srr0_ops, 0666, },
|
||||
{},
|
||||
};
|
255
arch/powerpc/platforms/cell/spufs/hw_ops.c
Normal file
255
arch/powerpc/platforms/cell/spufs/hw_ops.c
Normal file
@ -0,0 +1,255 @@
|
||||
/* hw_ops.c - query/set operations on active SPU context.
|
||||
*
|
||||
* Copyright (C) IBM 2005
|
||||
* Author: Mark Nutter <mnutter@us.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/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include "spufs.h"
|
||||
|
||||
static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
struct spu_problem __iomem *prob = spu->problem;
|
||||
u32 mbox_stat;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
mbox_stat = in_be32(&prob->mb_stat_R);
|
||||
if (mbox_stat & 0x0000ff) {
|
||||
*data = in_be32(&prob->pu_mb_R);
|
||||
ret = 4;
|
||||
}
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 spu_hw_mbox_stat_read(struct spu_context *ctx)
|
||||
{
|
||||
return in_be32(&ctx->spu->problem->mb_stat_R);
|
||||
}
|
||||
|
||||
static unsigned int spu_hw_mbox_stat_poll(struct spu_context *ctx,
|
||||
unsigned int events)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
int ret = 0;
|
||||
u32 stat;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
stat = in_be32(&spu->problem->mb_stat_R);
|
||||
|
||||
/* if the requested event is there, return the poll
|
||||
mask, otherwise enable the interrupt to get notified,
|
||||
but first mark any pending interrupts as done so
|
||||
we don't get woken up unnecessarily */
|
||||
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (stat & 0xff0000)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
else {
|
||||
spu_int_stat_clear(spu, 2, 0x1);
|
||||
spu_int_mask_or(spu, 2, 0x1);
|
||||
}
|
||||
}
|
||||
if (events & (POLLOUT | POLLWRNORM)) {
|
||||
if (stat & 0x00ff00)
|
||||
ret = POLLOUT | POLLWRNORM;
|
||||
else {
|
||||
spu_int_stat_clear(spu, 2, 0x10);
|
||||
spu_int_mask_or(spu, 2, 0x10);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
struct spu_problem __iomem *prob = spu->problem;
|
||||
struct spu_priv2 __iomem *priv2 = spu->priv2;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
if (in_be32(&prob->mb_stat_R) & 0xff0000) {
|
||||
/* read the first available word */
|
||||
*data = in_be64(&priv2->puint_mb_R);
|
||||
ret = 4;
|
||||
} else {
|
||||
/* make sure we get woken up by the interrupt */
|
||||
spu_int_mask_or(spu, 2, 0x1);
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spu_hw_wbox_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
struct spu_problem __iomem *prob = spu->problem;
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
if (in_be32(&prob->mb_stat_R) & 0x00ff00) {
|
||||
/* we have space to write wbox_data to */
|
||||
out_be32(&prob->spu_mb_W, data);
|
||||
ret = 4;
|
||||
} else {
|
||||
/* make sure we get woken up by the interrupt when space
|
||||
becomes available */
|
||||
spu_int_mask_or(spu, 2, 0x10);
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 spu_hw_signal1_read(struct spu_context *ctx)
|
||||
{
|
||||
return in_be32(&ctx->spu->problem->signal_notify1);
|
||||
}
|
||||
|
||||
static void spu_hw_signal1_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
out_be32(&ctx->spu->problem->signal_notify1, data);
|
||||
}
|
||||
|
||||
static u32 spu_hw_signal2_read(struct spu_context *ctx)
|
||||
{
|
||||
return in_be32(&ctx->spu->problem->signal_notify1);
|
||||
}
|
||||
|
||||
static void spu_hw_signal2_write(struct spu_context *ctx, u32 data)
|
||||
{
|
||||
out_be32(&ctx->spu->problem->signal_notify2, data);
|
||||
}
|
||||
|
||||
static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
struct spu_priv2 __iomem *priv2 = spu->priv2;
|
||||
u64 tmp;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
tmp = in_be64(&priv2->spu_cfg_RW);
|
||||
if (val)
|
||||
tmp |= 1;
|
||||
else
|
||||
tmp &= ~1;
|
||||
out_be64(&priv2->spu_cfg_RW, tmp);
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
}
|
||||
|
||||
static u64 spu_hw_signal1_type_get(struct spu_context *ctx)
|
||||
{
|
||||
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0);
|
||||
}
|
||||
|
||||
static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
struct spu_priv2 __iomem *priv2 = spu->priv2;
|
||||
u64 tmp;
|
||||
|
||||
spin_lock_irq(&spu->register_lock);
|
||||
tmp = in_be64(&priv2->spu_cfg_RW);
|
||||
if (val)
|
||||
tmp |= 2;
|
||||
else
|
||||
tmp &= ~2;
|
||||
out_be64(&priv2->spu_cfg_RW, tmp);
|
||||
spin_unlock_irq(&spu->register_lock);
|
||||
}
|
||||
|
||||
static u64 spu_hw_signal2_type_get(struct spu_context *ctx)
|
||||
{
|
||||
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0);
|
||||
}
|
||||
|
||||
static u32 spu_hw_npc_read(struct spu_context *ctx)
|
||||
{
|
||||
return in_be32(&ctx->spu->problem->spu_npc_RW);
|
||||
}
|
||||
|
||||
static void spu_hw_npc_write(struct spu_context *ctx, u32 val)
|
||||
{
|
||||
out_be32(&ctx->spu->problem->spu_npc_RW, val);
|
||||
}
|
||||
|
||||
static u32 spu_hw_status_read(struct spu_context *ctx)
|
||||
{
|
||||
return in_be32(&ctx->spu->problem->spu_status_R);
|
||||
}
|
||||
|
||||
static char *spu_hw_get_ls(struct spu_context *ctx)
|
||||
{
|
||||
return ctx->spu->local_store;
|
||||
}
|
||||
|
||||
static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val)
|
||||
{
|
||||
eieio();
|
||||
out_be32(&ctx->spu->problem->spu_runcntl_RW, val);
|
||||
}
|
||||
|
||||
static void spu_hw_runcntl_stop(struct spu_context *ctx)
|
||||
{
|
||||
spin_lock_irq(&ctx->spu->register_lock);
|
||||
out_be32(&ctx->spu->problem->spu_runcntl_RW, SPU_RUNCNTL_STOP);
|
||||
while (in_be32(&ctx->spu->problem->spu_status_R) & SPU_STATUS_RUNNING)
|
||||
cpu_relax();
|
||||
spin_unlock_irq(&ctx->spu->register_lock);
|
||||
}
|
||||
|
||||
struct spu_context_ops spu_hw_ops = {
|
||||
.mbox_read = spu_hw_mbox_read,
|
||||
.mbox_stat_read = spu_hw_mbox_stat_read,
|
||||
.mbox_stat_poll = spu_hw_mbox_stat_poll,
|
||||
.ibox_read = spu_hw_ibox_read,
|
||||
.wbox_write = spu_hw_wbox_write,
|
||||
.signal1_read = spu_hw_signal1_read,
|
||||
.signal1_write = spu_hw_signal1_write,
|
||||
.signal2_read = spu_hw_signal2_read,
|
||||
.signal2_write = spu_hw_signal2_write,
|
||||
.signal1_type_set = spu_hw_signal1_type_set,
|
||||
.signal1_type_get = spu_hw_signal1_type_get,
|
||||
.signal2_type_set = spu_hw_signal2_type_set,
|
||||
.signal2_type_get = spu_hw_signal2_type_get,
|
||||
.npc_read = spu_hw_npc_read,
|
||||
.npc_write = spu_hw_npc_write,
|
||||
.status_read = spu_hw_status_read,
|
||||
.get_ls = spu_hw_get_ls,
|
||||
.runcntl_write = spu_hw_runcntl_write,
|
||||
.runcntl_stop = spu_hw_runcntl_stop,
|
||||
};
|
486
arch/powerpc/platforms/cell/spufs/inode.c
Normal file
486
arch/powerpc/platforms/cell/spufs/inode.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* SPU file system
|
||||
*
|
||||
* (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/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/parser.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "spufs.h"
|
||||
|
||||
static kmem_cache_t *spufs_inode_cache;
|
||||
|
||||
static struct inode *
|
||||
spufs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct spufs_inode_info *ei;
|
||||
|
||||
ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void
|
||||
spufs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
|
||||
}
|
||||
|
||||
static void
|
||||
spufs_init_once(void *p, kmem_cache_t * cachep, unsigned long flags)
|
||||
{
|
||||
struct spufs_inode_info *ei = p;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR) {
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
}
|
||||
|
||||
static struct inode *
|
||||
spufs_new_inode(struct super_block *sb, int mode)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current->fsuid;
|
||||
inode->i_gid = current->fsgid;
|
||||
inode->i_blksize = PAGE_CACHE_SIZE;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
out:
|
||||
return inode;
|
||||
}
|
||||
|
||||
static int
|
||||
spufs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if ((attr->ia_valid & ATTR_SIZE) &&
|
||||
(attr->ia_size != inode->i_size))
|
||||
return -EINVAL;
|
||||
return inode_setattr(inode, attr);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
spufs_new_file(struct super_block *sb, struct dentry *dentry,
|
||||
struct file_operations *fops, int mode,
|
||||
struct spu_context *ctx)
|
||||
{
|
||||
static struct inode_operations spufs_file_iops = {
|
||||
.setattr = spufs_setattr,
|
||||
};
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
ret = -ENOSPC;
|
||||
inode = spufs_new_inode(sb, S_IFREG | mode);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
inode->i_op = &spufs_file_iops;
|
||||
inode->i_fop = fops;
|
||||
inode->u.generic_ip = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
|
||||
d_add(dentry, inode);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
spufs_delete_inode(struct inode *inode)
|
||||
{
|
||||
if (SPUFS_I(inode)->i_ctx)
|
||||
put_spu_context(SPUFS_I(inode)->i_ctx);
|
||||
clear_inode(inode);
|
||||
}
|
||||
|
||||
static void spufs_prune_dir(struct dentry *dir)
|
||||
{
|
||||
struct dentry *dentry, *tmp;
|
||||
down(&dir->d_inode->i_sem);
|
||||
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) {
|
||||
spin_lock(&dcache_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!(d_unhashed(dentry)) && dentry->d_inode) {
|
||||
dget_locked(dentry);
|
||||
__d_drop(dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
simple_unlink(dir->d_inode, dentry);
|
||||
spin_unlock(&dcache_lock);
|
||||
dput(dentry);
|
||||
} else {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
}
|
||||
shrink_dcache_parent(dir);
|
||||
up(&dir->d_inode->i_sem);
|
||||
}
|
||||
|
||||
static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry)
|
||||
{
|
||||
struct spu_context *ctx;
|
||||
|
||||
/* remove all entries */
|
||||
down(&root->i_sem);
|
||||
spufs_prune_dir(dir_dentry);
|
||||
up(&root->i_sem);
|
||||
|
||||
/* We have to give up the mm_struct */
|
||||
ctx = SPUFS_I(dir_dentry->d_inode)->i_ctx;
|
||||
spu_forget(ctx);
|
||||
|
||||
/* XXX Do we need to hold i_sem here ? */
|
||||
return simple_rmdir(root, dir_dentry);
|
||||
}
|
||||
|
||||
static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
|
||||
int mode, struct spu_context *ctx)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int ret;
|
||||
|
||||
while (files->name && files->name[0]) {
|
||||
ret = -ENOMEM;
|
||||
dentry = d_alloc_name(dir, files->name);
|
||||
if (!dentry)
|
||||
goto out;
|
||||
ret = spufs_new_file(dir->d_sb, dentry, files->ops,
|
||||
files->mode & mode, ctx);
|
||||
if (ret)
|
||||
goto out;
|
||||
files++;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
spufs_prune_dir(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spufs_dir_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct inode *dir;
|
||||
struct dentry *dentry;
|
||||
int ret;
|
||||
|
||||
dentry = file->f_dentry;
|
||||
dir = dentry->d_parent->d_inode;
|
||||
|
||||
ret = spufs_rmdir(dir, dentry);
|
||||
WARN_ON(ret);
|
||||
|
||||
return dcache_dir_close(inode, file);
|
||||
}
|
||||
|
||||
struct inode_operations spufs_dir_inode_operations = {
|
||||
.lookup = simple_lookup,
|
||||
};
|
||||
|
||||
struct file_operations spufs_context_fops = {
|
||||
.open = dcache_dir_open,
|
||||
.release = spufs_dir_close,
|
||||
.llseek = dcache_dir_lseek,
|
||||
.read = generic_read_dir,
|
||||
.readdir = dcache_readdir,
|
||||
.fsync = simple_sync_file,
|
||||
};
|
||||
|
||||
static int
|
||||
spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
int ret;
|
||||
struct inode *inode;
|
||||
struct spu_context *ctx;
|
||||
|
||||
ret = -ENOSPC;
|
||||
inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
if (dir->i_mode & S_ISGID) {
|
||||
inode->i_gid = dir->i_gid;
|
||||
inode->i_mode &= S_ISGID;
|
||||
}
|
||||
ctx = alloc_spu_context(inode->i_mapping);
|
||||
SPUFS_I(inode)->i_ctx = ctx;
|
||||
if (!ctx)
|
||||
goto out_iput;
|
||||
|
||||
inode->i_op = &spufs_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
|
||||
if (ret)
|
||||
goto out_free_ctx;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
dir->i_nlink++;
|
||||
dentry->d_inode->i_nlink++;
|
||||
goto out;
|
||||
|
||||
out_free_ctx:
|
||||
put_spu_context(ctx);
|
||||
out_iput:
|
||||
iput(inode);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
|
||||
{
|
||||
int ret;
|
||||
struct file *filp;
|
||||
|
||||
ret = get_unused_fd();
|
||||
if (ret < 0) {
|
||||
dput(dentry);
|
||||
mntput(mnt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
filp = dentry_open(dentry, mnt, O_RDONLY);
|
||||
if (IS_ERR(filp)) {
|
||||
put_unused_fd(ret);
|
||||
ret = PTR_ERR(filp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
filp->f_op = &spufs_context_fops;
|
||||
fd_install(ret, filp);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_system_type spufs_type;
|
||||
|
||||
long spufs_create_thread(struct nameidata *nd,
|
||||
unsigned int flags, mode_t mode)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int ret;
|
||||
|
||||
/* need to be at the root of spufs */
|
||||
ret = -EINVAL;
|
||||
if (nd->dentry->d_sb->s_type != &spufs_type ||
|
||||
nd->dentry != nd->dentry->d_sb->s_root)
|
||||
goto out;
|
||||
|
||||
dentry = lookup_create(nd, 1);
|
||||
ret = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry))
|
||||
goto out_dir;
|
||||
|
||||
ret = -EEXIST;
|
||||
if (dentry->d_inode)
|
||||
goto out_dput;
|
||||
|
||||
mode &= ~current->fs->umask;
|
||||
ret = spufs_mkdir(nd->dentry->d_inode, dentry, mode & S_IRWXUGO);
|
||||
if (ret)
|
||||
goto out_dput;
|
||||
|
||||
/*
|
||||
* get references for dget and mntget, will be released
|
||||
* in error path of *_open().
|
||||
*/
|
||||
ret = spufs_context_open(dget(dentry), mntget(nd->mnt));
|
||||
if (ret < 0)
|
||||
spufs_rmdir(nd->dentry->d_inode, dentry);
|
||||
|
||||
out_dput:
|
||||
dput(dentry);
|
||||
out_dir:
|
||||
up(&nd->dentry->d_inode->i_sem);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* File system initialization */
|
||||
enum {
|
||||
Opt_uid, Opt_gid, Opt_err,
|
||||
};
|
||||
|
||||
static match_table_t spufs_tokens = {
|
||||
{ Opt_uid, "uid=%d" },
|
||||
{ Opt_gid, "gid=%d" },
|
||||
{ Opt_err, NULL },
|
||||
};
|
||||
|
||||
static int
|
||||
spufs_parse_options(char *options, struct inode *root)
|
||||
{
|
||||
char *p;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token, option;
|
||||
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
token = match_token(p, spufs_tokens, args);
|
||||
switch (token) {
|
||||
case Opt_uid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
root->i_uid = option;
|
||||
break;
|
||||
case Opt_gid:
|
||||
if (match_int(&args[0], &option))
|
||||
return 0;
|
||||
root->i_gid = option;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
spufs_create_root(struct super_block *sb, void *data)
|
||||
{
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
inode = spufs_new_inode(sb, S_IFDIR | 0775);
|
||||
if (!inode)
|
||||
goto out;
|
||||
|
||||
inode->i_op = &spufs_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
SPUFS_I(inode)->i_ctx = NULL;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (!spufs_parse_options(data, inode))
|
||||
goto out_iput;
|
||||
|
||||
ret = -ENOMEM;
|
||||
sb->s_root = d_alloc_root(inode);
|
||||
if (!sb->s_root)
|
||||
goto out_iput;
|
||||
|
||||
return 0;
|
||||
out_iput:
|
||||
iput(inode);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
spufs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
static struct super_operations s_ops = {
|
||||
.alloc_inode = spufs_alloc_inode,
|
||||
.destroy_inode = spufs_destroy_inode,
|
||||
.statfs = simple_statfs,
|
||||
.delete_inode = spufs_delete_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
};
|
||||
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = SPUFS_MAGIC;
|
||||
sb->s_op = &s_ops;
|
||||
|
||||
return spufs_create_root(sb, data);
|
||||
}
|
||||
|
||||
static struct super_block *
|
||||
spufs_get_sb(struct file_system_type *fstype, int flags,
|
||||
const char *name, void *data)
|
||||
{
|
||||
return get_sb_single(fstype, flags, data, spufs_fill_super);
|
||||
}
|
||||
|
||||
static struct file_system_type spufs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "spufs",
|
||||
.get_sb = spufs_get_sb,
|
||||
.kill_sb = kill_litter_super,
|
||||
};
|
||||
|
||||
static int spufs_init(void)
|
||||
{
|
||||
int ret;
|
||||
ret = -ENOMEM;
|
||||
spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
|
||||
sizeof(struct spufs_inode_info), 0,
|
||||
SLAB_HWCACHE_ALIGN, spufs_init_once, NULL);
|
||||
|
||||
if (!spufs_inode_cache)
|
||||
goto out;
|
||||
if (spu_sched_init() != 0) {
|
||||
kmem_cache_destroy(spufs_inode_cache);
|
||||
goto out;
|
||||
}
|
||||
ret = register_filesystem(&spufs_type);
|
||||
if (ret)
|
||||
goto out_cache;
|
||||
ret = register_spu_syscalls(&spufs_calls);
|
||||
if (ret)
|
||||
goto out_fs;
|
||||
return 0;
|
||||
out_fs:
|
||||
unregister_filesystem(&spufs_type);
|
||||
out_cache:
|
||||
kmem_cache_destroy(spufs_inode_cache);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
module_init(spufs_init);
|
||||
|
||||
static void spufs_exit(void)
|
||||
{
|
||||
spu_sched_exit();
|
||||
unregister_spu_syscalls(&spufs_calls);
|
||||
unregister_filesystem(&spufs_type);
|
||||
kmem_cache_destroy(spufs_inode_cache);
|
||||
}
|
||||
module_exit(spufs_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
|
||||
|
131
arch/powerpc/platforms/cell/spufs/run.c
Normal file
131
arch/powerpc/platforms/cell/spufs/run.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/spu.h>
|
||||
|
||||
#include "spufs.h"
|
||||
|
||||
/* interrupt-level stop callback function. */
|
||||
void spufs_stop_callback(struct spu *spu)
|
||||
{
|
||||
struct spu_context *ctx = spu->ctx;
|
||||
|
||||
wake_up_all(&ctx->stop_wq);
|
||||
}
|
||||
|
||||
static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
|
||||
{
|
||||
struct spu *spu;
|
||||
u64 pte_fault;
|
||||
|
||||
*stat = ctx->ops->status_read(ctx);
|
||||
if (ctx->state != SPU_STATE_RUNNABLE)
|
||||
return 1;
|
||||
spu = ctx->spu;
|
||||
pte_fault = spu->dsisr &
|
||||
(MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
|
||||
return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline int spu_run_init(struct spu_context *ctx, u32 * npc,
|
||||
u32 * status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = spu_acquire_runnable(ctx)) != 0)
|
||||
return ret;
|
||||
ctx->ops->npc_write(ctx, *npc);
|
||||
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
|
||||
u32 * status)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
*status = ctx->ops->status_read(ctx);
|
||||
*npc = ctx->ops->npc_read(ctx);
|
||||
spu_release(ctx);
|
||||
|
||||
if (signal_pending(current))
|
||||
ret = -ERESTARTSYS;
|
||||
if (unlikely(current->ptrace & PT_PTRACED)) {
|
||||
if ((*status & SPU_STATUS_STOPPED_BY_STOP)
|
||||
&& (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
|
||||
force_sig(SIGTRAP, current);
|
||||
ret = -ERESTARTSYS;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
|
||||
u32 *status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = spu_run_fini(ctx, npc, status)) != 0)
|
||||
return ret;
|
||||
if (*status & (SPU_STATUS_STOPPED_BY_STOP |
|
||||
SPU_STATUS_STOPPED_BY_HALT)) {
|
||||
return *status;
|
||||
}
|
||||
if ((ret = spu_run_init(ctx, npc, status)) != 0)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int spu_process_events(struct spu_context *ctx)
|
||||
{
|
||||
struct spu *spu = ctx->spu;
|
||||
u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
|
||||
int ret = 0;
|
||||
|
||||
if (spu->dsisr & pte_fault)
|
||||
ret = spu_irq_class_1_bottom(spu);
|
||||
if (spu->class_0_pending)
|
||||
ret = spu_irq_class_0_bottom(spu);
|
||||
if (!ret && signal_pending(current))
|
||||
ret = -ERESTARTSYS;
|
||||
return ret;
|
||||
}
|
||||
|
||||
long spufs_run_spu(struct file *file, struct spu_context *ctx,
|
||||
u32 * npc, u32 * status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (down_interruptible(&ctx->run_sema))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = spu_run_init(ctx, npc, status);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status));
|
||||
if (unlikely(ret))
|
||||
break;
|
||||
if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
|
||||
ret = spu_reacquire_runnable(ctx, npc, status);
|
||||
if (ret)
|
||||
goto out;
|
||||
continue;
|
||||
}
|
||||
ret = spu_process_events(ctx);
|
||||
|
||||
} while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP |
|
||||
SPU_STATUS_STOPPED_BY_HALT)));
|
||||
|
||||
ctx->ops->runcntl_stop(ctx);
|
||||
ret = spu_run_fini(ctx, npc, status);
|
||||
if (!ret)
|
||||
ret = *status;
|
||||
spu_yield(ctx);
|
||||
|
||||
out:
|
||||
up(&ctx->run_sema);
|
||||
return ret;
|
||||
}
|
||||
|
461
arch/powerpc/platforms/cell/spufs/sched.c
Normal file
461
arch/powerpc/platforms/cell/spufs/sched.c
Normal file
@ -0,0 +1,461 @@
|
||||
/* sched.c - SPU scheduler.
|
||||
*
|
||||
* Copyright (C) IBM 2005
|
||||
* Author: Mark Nutter <mnutter@us.ibm.com>
|
||||
*
|
||||
* SPU scheduler, based on Linux thread priority. For now use
|
||||
* a simple "cooperative" yield model with no preemption. SPU
|
||||
* scheduling will eventually be preemptive: When a thread with
|
||||
* a higher static priority gets ready to run, then an active SPU
|
||||
* context will be preempted and returned to the waitq.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/unistd.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include "spufs.h"
|
||||
|
||||
#define SPU_MIN_TIMESLICE (100 * HZ / 1000)
|
||||
|
||||
#define SPU_BITMAP_SIZE (((MAX_PRIO+BITS_PER_LONG)/BITS_PER_LONG)+1)
|
||||
struct spu_prio_array {
|
||||
atomic_t nr_blocked;
|
||||
unsigned long bitmap[SPU_BITMAP_SIZE];
|
||||
wait_queue_head_t waitq[MAX_PRIO];
|
||||
};
|
||||
|
||||
/* spu_runqueue - This is the main runqueue data structure for SPUs. */
|
||||
struct spu_runqueue {
|
||||
struct semaphore sem;
|
||||
unsigned long nr_active;
|
||||
unsigned long nr_idle;
|
||||
unsigned long nr_switches;
|
||||
struct list_head active_list;
|
||||
struct list_head idle_list;
|
||||
struct spu_prio_array prio;
|
||||
};
|
||||
|
||||
static struct spu_runqueue *spu_runqueues = NULL;
|
||||
|
||||
static inline struct spu_runqueue *spu_rq(void)
|
||||
{
|
||||
/* Future: make this a per-NODE array,
|
||||
* and use cpu_to_node(smp_processor_id())
|
||||
*/
|
||||
return spu_runqueues;
|
||||
}
|
||||
|
||||
static inline struct spu *del_idle(struct spu_runqueue *rq)
|
||||
{
|
||||
struct spu *spu;
|
||||
|
||||
BUG_ON(rq->nr_idle <= 0);
|
||||
BUG_ON(list_empty(&rq->idle_list));
|
||||
/* Future: Move SPU out of low-power SRI state. */
|
||||
spu = list_entry(rq->idle_list.next, struct spu, sched_list);
|
||||
list_del_init(&spu->sched_list);
|
||||
rq->nr_idle--;
|
||||
return spu;
|
||||
}
|
||||
|
||||
static inline void del_active(struct spu_runqueue *rq, struct spu *spu)
|
||||
{
|
||||
BUG_ON(rq->nr_active <= 0);
|
||||
BUG_ON(list_empty(&rq->active_list));
|
||||
list_del_init(&spu->sched_list);
|
||||
rq->nr_active--;
|
||||
}
|
||||
|
||||
static inline void add_idle(struct spu_runqueue *rq, struct spu *spu)
|
||||
{
|
||||
/* Future: Put SPU into low-power SRI state. */
|
||||
list_add_tail(&spu->sched_list, &rq->idle_list);
|
||||
rq->nr_idle++;
|
||||
}
|
||||
|
||||
static inline void add_active(struct spu_runqueue *rq, struct spu *spu)
|
||||
{
|
||||
rq->nr_active++;
|
||||
rq->nr_switches++;
|
||||
list_add_tail(&spu->sched_list, &rq->active_list);
|
||||
}
|
||||
|
||||
static void prio_wakeup(struct spu_runqueue *rq)
|
||||
{
|
||||
if (atomic_read(&rq->prio.nr_blocked) && rq->nr_idle) {
|
||||
int best = sched_find_first_bit(rq->prio.bitmap);
|
||||
if (best < MAX_PRIO) {
|
||||
wait_queue_head_t *wq = &rq->prio.waitq[best];
|
||||
wake_up_interruptible_nr(wq, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prio_wait(struct spu_runqueue *rq, struct spu_context *ctx,
|
||||
u64 flags)
|
||||
{
|
||||
int prio = current->prio;
|
||||
wait_queue_head_t *wq = &rq->prio.waitq[prio];
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
__set_bit(prio, rq->prio.bitmap);
|
||||
atomic_inc(&rq->prio.nr_blocked);
|
||||
prepare_to_wait_exclusive(wq, &wait, TASK_INTERRUPTIBLE);
|
||||
if (!signal_pending(current)) {
|
||||
up(&rq->sem);
|
||||
up_write(&ctx->state_sema);
|
||||
pr_debug("%s: pid=%d prio=%d\n", __FUNCTION__,
|
||||
current->pid, current->prio);
|
||||
schedule();
|
||||
down_write(&ctx->state_sema);
|
||||
down(&rq->sem);
|
||||
}
|
||||
finish_wait(wq, &wait);
|
||||
atomic_dec(&rq->prio.nr_blocked);
|
||||
if (!waitqueue_active(wq))
|
||||
__clear_bit(prio, rq->prio.bitmap);
|
||||
}
|
||||
|
||||
static inline int is_best_prio(struct spu_runqueue *rq)
|
||||
{
|
||||
int best_prio;
|
||||
|
||||
best_prio = sched_find_first_bit(rq->prio.bitmap);
|
||||
return (current->prio < best_prio) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline void mm_needs_global_tlbie(struct mm_struct *mm)
|
||||
{
|
||||
/* Global TLBIE broadcast required with SPEs. */
|
||||
#if (NR_CPUS > 1)
|
||||
__cpus_setall(&mm->cpu_vm_mask, NR_CPUS);
|
||||
#else
|
||||
__cpus_setall(&mm->cpu_vm_mask, NR_CPUS+1); /* is this ok? */
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void bind_context(struct spu *spu, struct spu_context *ctx)
|
||||
{
|
||||
pr_debug("%s: pid=%d SPU=%d\n", __FUNCTION__, current->pid,
|
||||
spu->number);
|
||||
spu->ctx = ctx;
|
||||
spu->flags = 0;
|
||||
ctx->flags = 0;
|
||||
ctx->spu = spu;
|
||||
ctx->ops = &spu_hw_ops;
|
||||
spu->pid = current->pid;
|
||||
spu->prio = current->prio;
|
||||
spu->mm = ctx->owner;
|
||||
mm_needs_global_tlbie(spu->mm);
|
||||
spu->ibox_callback = spufs_ibox_callback;
|
||||
spu->wbox_callback = spufs_wbox_callback;
|
||||
spu->stop_callback = spufs_stop_callback;
|
||||
mb();
|
||||
spu_unmap_mappings(ctx);
|
||||
spu_restore(&ctx->csa, spu);
|
||||
spu->timestamp = jiffies;
|
||||
}
|
||||
|
||||
static inline void unbind_context(struct spu *spu, struct spu_context *ctx)
|
||||
{
|
||||
pr_debug("%s: unbind pid=%d SPU=%d\n", __FUNCTION__,
|
||||
spu->pid, spu->number);
|
||||
spu_unmap_mappings(ctx);
|
||||
spu_save(&ctx->csa, spu);
|
||||
spu->timestamp = jiffies;
|
||||
ctx->state = SPU_STATE_SAVED;
|
||||
spu->ibox_callback = NULL;
|
||||
spu->wbox_callback = NULL;
|
||||
spu->stop_callback = NULL;
|
||||
spu->mm = NULL;
|
||||
spu->pid = 0;
|
||||
spu->prio = MAX_PRIO;
|
||||
ctx->ops = &spu_backing_ops;
|
||||
ctx->spu = NULL;
|
||||
ctx->flags = 0;
|
||||
spu->flags = 0;
|
||||
spu->ctx = NULL;
|
||||
}
|
||||
|
||||
static void spu_reaper(void *data)
|
||||
{
|
||||
struct spu_context *ctx = data;
|
||||
struct spu *spu;
|
||||
|
||||
down_write(&ctx->state_sema);
|
||||
spu = ctx->spu;
|
||||
if (spu && test_bit(SPU_CONTEXT_PREEMPT, &ctx->flags)) {
|
||||
if (atomic_read(&spu->rq->prio.nr_blocked)) {
|
||||
pr_debug("%s: spu=%d\n", __func__, spu->number);
|
||||
ctx->ops->runcntl_stop(ctx);
|
||||
spu_deactivate(ctx);
|
||||
wake_up_all(&ctx->stop_wq);
|
||||
} else {
|
||||
clear_bit(SPU_CONTEXT_PREEMPT, &ctx->flags);
|
||||
}
|
||||
}
|
||||
up_write(&ctx->state_sema);
|
||||
put_spu_context(ctx);
|
||||
}
|
||||
|
||||
static void schedule_spu_reaper(struct spu_runqueue *rq, struct spu *spu)
|
||||
{
|
||||
struct spu_context *ctx = get_spu_context(spu->ctx);
|
||||
unsigned long now = jiffies;
|
||||
unsigned long expire = spu->timestamp + SPU_MIN_TIMESLICE;
|
||||
|
||||
set_bit(SPU_CONTEXT_PREEMPT, &ctx->flags);
|
||||
INIT_WORK(&ctx->reap_work, spu_reaper, ctx);
|
||||
if (time_after(now, expire))
|
||||
schedule_work(&ctx->reap_work);
|
||||
else
|
||||
schedule_delayed_work(&ctx->reap_work, expire - now);
|
||||
}
|
||||
|
||||
static void check_preempt_active(struct spu_runqueue *rq)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct spu *worst = NULL;
|
||||
|
||||
list_for_each(p, &rq->active_list) {
|
||||
struct spu *spu = list_entry(p, struct spu, sched_list);
|
||||
struct spu_context *ctx = spu->ctx;
|
||||
if (!test_bit(SPU_CONTEXT_PREEMPT, &ctx->flags)) {
|
||||
if (!worst || (spu->prio > worst->prio)) {
|
||||
worst = spu;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (worst && (current->prio < worst->prio))
|
||||
schedule_spu_reaper(rq, worst);
|
||||
}
|
||||
|
||||
static struct spu *get_idle_spu(struct spu_context *ctx, u64 flags)
|
||||
{
|
||||
struct spu_runqueue *rq;
|
||||
struct spu *spu = NULL;
|
||||
|
||||
rq = spu_rq();
|
||||
down(&rq->sem);
|
||||
for (;;) {
|
||||
if (rq->nr_idle > 0) {
|
||||
if (is_best_prio(rq)) {
|
||||
/* Fall through. */
|
||||
spu = del_idle(rq);
|
||||
break;
|
||||
} else {
|
||||
prio_wakeup(rq);
|
||||
up(&rq->sem);
|
||||
yield();
|
||||
if (signal_pending(current)) {
|
||||
return NULL;
|
||||
}
|
||||
rq = spu_rq();
|
||||
down(&rq->sem);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
check_preempt_active(rq);
|
||||
prio_wait(rq, ctx, flags);
|
||||
if (signal_pending(current)) {
|
||||
prio_wakeup(rq);
|
||||
spu = NULL;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
up(&rq->sem);
|
||||
return spu;
|
||||
}
|
||||
|
||||
static void put_idle_spu(struct spu *spu)
|
||||
{
|
||||
struct spu_runqueue *rq = spu->rq;
|
||||
|
||||
down(&rq->sem);
|
||||
add_idle(rq, spu);
|
||||
prio_wakeup(rq);
|
||||
up(&rq->sem);
|
||||
}
|
||||
|
||||
static int get_active_spu(struct spu *spu)
|
||||
{
|
||||
struct spu_runqueue *rq = spu->rq;
|
||||
struct list_head *p;
|
||||
struct spu *tmp;
|
||||
int rc = 0;
|
||||
|
||||
down(&rq->sem);
|
||||
list_for_each(p, &rq->active_list) {
|
||||
tmp = list_entry(p, struct spu, sched_list);
|
||||
if (tmp == spu) {
|
||||
del_active(rq, spu);
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up(&rq->sem);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void put_active_spu(struct spu *spu)
|
||||
{
|
||||
struct spu_runqueue *rq = spu->rq;
|
||||
|
||||
down(&rq->sem);
|
||||
add_active(rq, spu);
|
||||
up(&rq->sem);
|
||||
}
|
||||
|
||||
/* Lock order:
|
||||
* spu_activate() & spu_deactivate() require the
|
||||
* caller to have down_write(&ctx->state_sema).
|
||||
*
|
||||
* The rq->sem is breifly held (inside or outside a
|
||||
* given ctx lock) for list management, but is never
|
||||
* held during save/restore.
|
||||
*/
|
||||
|
||||
int spu_activate(struct spu_context *ctx, u64 flags)
|
||||
{
|
||||
struct spu *spu;
|
||||
|
||||
if (ctx->spu)
|
||||
return 0;
|
||||
spu = get_idle_spu(ctx, flags);
|
||||
if (!spu)
|
||||
return (signal_pending(current)) ? -ERESTARTSYS : -EAGAIN;
|
||||
bind_context(spu, ctx);
|
||||
/*
|
||||
* We're likely to wait for interrupts on the same
|
||||
* CPU that we are now on, so send them here.
|
||||
*/
|
||||
spu_irq_setaffinity(spu, raw_smp_processor_id());
|
||||
put_active_spu(spu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spu_deactivate(struct spu_context *ctx)
|
||||
{
|
||||
struct spu *spu;
|
||||
int needs_idle;
|
||||
|
||||
spu = ctx->spu;
|
||||
if (!spu)
|
||||
return;
|
||||
needs_idle = get_active_spu(spu);
|
||||
unbind_context(spu, ctx);
|
||||
if (needs_idle)
|
||||
put_idle_spu(spu);
|
||||
}
|
||||
|
||||
void spu_yield(struct spu_context *ctx)
|
||||
{
|
||||
struct spu *spu;
|
||||
int need_yield = 0;
|
||||
|
||||
down_write(&ctx->state_sema);
|
||||
spu = ctx->spu;
|
||||
if (spu && (sched_find_first_bit(spu->rq->prio.bitmap) < MAX_PRIO)) {
|
||||
pr_debug("%s: yielding SPU %d\n", __FUNCTION__, spu->number);
|
||||
spu_deactivate(ctx);
|
||||
ctx->state = SPU_STATE_SAVED;
|
||||
need_yield = 1;
|
||||
} else if (spu) {
|
||||
spu->prio = MAX_PRIO;
|
||||
}
|
||||
up_write(&ctx->state_sema);
|
||||
if (unlikely(need_yield))
|
||||
yield();
|
||||
}
|
||||
|
||||
int __init spu_sched_init(void)
|
||||
{
|
||||
struct spu_runqueue *rq;
|
||||
struct spu *spu;
|
||||
int i;
|
||||
|
||||
rq = spu_runqueues = kmalloc(sizeof(struct spu_runqueue), GFP_KERNEL);
|
||||
if (!rq) {
|
||||
printk(KERN_WARNING "%s: Unable to allocate runqueues.\n",
|
||||
__FUNCTION__);
|
||||
return 1;
|
||||
}
|
||||
memset(rq, 0, sizeof(struct spu_runqueue));
|
||||
init_MUTEX(&rq->sem);
|
||||
INIT_LIST_HEAD(&rq->active_list);
|
||||
INIT_LIST_HEAD(&rq->idle_list);
|
||||
rq->nr_active = 0;
|
||||
rq->nr_idle = 0;
|
||||
rq->nr_switches = 0;
|
||||
atomic_set(&rq->prio.nr_blocked, 0);
|
||||
for (i = 0; i < MAX_PRIO; i++) {
|
||||
init_waitqueue_head(&rq->prio.waitq[i]);
|
||||
__clear_bit(i, rq->prio.bitmap);
|
||||
}
|
||||
__set_bit(MAX_PRIO, rq->prio.bitmap);
|
||||
for (;;) {
|
||||
spu = spu_alloc();
|
||||
if (!spu)
|
||||
break;
|
||||
pr_debug("%s: adding SPU[%d]\n", __FUNCTION__, spu->number);
|
||||
add_idle(rq, spu);
|
||||
spu->rq = rq;
|
||||
spu->timestamp = jiffies;
|
||||
}
|
||||
if (!rq->nr_idle) {
|
||||
printk(KERN_WARNING "%s: No available SPUs.\n", __FUNCTION__);
|
||||
kfree(rq);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __exit spu_sched_exit(void)
|
||||
{
|
||||
struct spu_runqueue *rq = spu_rq();
|
||||
struct spu *spu;
|
||||
|
||||
if (!rq) {
|
||||
printk(KERN_WARNING "%s: no runqueues!\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
while (rq->nr_idle > 0) {
|
||||
spu = del_idle(rq);
|
||||
if (!spu)
|
||||
break;
|
||||
spu_free(spu);
|
||||
}
|
||||
kfree(rq);
|
||||
}
|
336
arch/powerpc/platforms/cell/spufs/spu_restore.c
Normal file
336
arch/powerpc/platforms/cell/spufs/spu_restore.c
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* spu_restore.c
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005
|
||||
*
|
||||
* SPU-side context restore sequence outlined in
|
||||
* Synergistic Processor Element Book IV
|
||||
*
|
||||
* Author: Mark Nutter <mnutter@us.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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LS_SIZE
|
||||
#define LS_SIZE 0x40000 /* 256K (in bytes) */
|
||||
#endif
|
||||
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#include <spu_intrinsics.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include "spu_utils.h"
|
||||
|
||||
#define BR_INSTR 0x327fff80 /* br -4 */
|
||||
#define NOP_INSTR 0x40200000 /* nop */
|
||||
#define HEQ_INSTR 0x7b000000 /* heq $0, $0 */
|
||||
#define STOP_INSTR 0x00000000 /* stop 0x0 */
|
||||
#define ILLEGAL_INSTR 0x00800000 /* illegal instr */
|
||||
#define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */
|
||||
|
||||
static inline void fetch_regs_from_mem(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ls = (unsigned int)®s_spill[0];
|
||||
unsigned int size = sizeof(regs_spill);
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0x40; /* GET */
|
||||
|
||||
spu_writech(MFC_LSA, ls);
|
||||
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
|
||||
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
|
||||
spu_writech(MFC_Size, size);
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void restore_upper_240kb(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ls = 16384;
|
||||
unsigned int list = (unsigned int)&dma_list[0];
|
||||
unsigned int size = sizeof(dma_list);
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0x44; /* GETL */
|
||||
|
||||
/* Restore, Step 4:
|
||||
* Enqueue the GETL command (tag 0) to the MFC SPU command
|
||||
* queue to transfer the upper 240 kb of LS from CSA.
|
||||
*/
|
||||
spu_writech(MFC_LSA, ls);
|
||||
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
|
||||
spu_writech(MFC_EAL, list);
|
||||
spu_writech(MFC_Size, size);
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void restore_decr(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int decr_running;
|
||||
unsigned int decr;
|
||||
|
||||
/* Restore, Step 6:
|
||||
* If the LSCSA "decrementer running" flag is set
|
||||
* then write the SPU_WrDec channel with the
|
||||
* decrementer value from LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(decr_status);
|
||||
decr_running = regs_spill[offset].slot[0];
|
||||
if (decr_running) {
|
||||
offset = LSCSA_QW_OFFSET(decr);
|
||||
decr = regs_spill[offset].slot[0];
|
||||
spu_writech(SPU_WrDec, decr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void write_ppu_mb(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int data;
|
||||
|
||||
/* Restore, Step 11:
|
||||
* Write the MFC_WrOut_MB channel with the PPU_MB
|
||||
* data from LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(ppu_mb);
|
||||
data = regs_spill[offset].slot[0];
|
||||
spu_writech(SPU_WrOutMbox, data);
|
||||
}
|
||||
|
||||
static inline void write_ppuint_mb(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int data;
|
||||
|
||||
/* Restore, Step 12:
|
||||
* Write the MFC_WrInt_MB channel with the PPUINT_MB
|
||||
* data from LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(ppuint_mb);
|
||||
data = regs_spill[offset].slot[0];
|
||||
spu_writech(SPU_WrOutIntrMbox, data);
|
||||
}
|
||||
|
||||
static inline void restore_fpcr(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
vector unsigned int fpcr;
|
||||
|
||||
/* Restore, Step 13:
|
||||
* Restore the floating-point status and control
|
||||
* register from the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(fpcr);
|
||||
fpcr = regs_spill[offset].v;
|
||||
spu_mtfpscr(fpcr);
|
||||
}
|
||||
|
||||
static inline void restore_srr0(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int srr0;
|
||||
|
||||
/* Restore, Step 14:
|
||||
* Restore the SPU SRR0 data from the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(srr0);
|
||||
srr0 = regs_spill[offset].slot[0];
|
||||
spu_writech(SPU_WrSRR0, srr0);
|
||||
}
|
||||
|
||||
static inline void restore_event_mask(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int event_mask;
|
||||
|
||||
/* Restore, Step 15:
|
||||
* Restore the SPU_RdEventMsk data from the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(event_mask);
|
||||
event_mask = regs_spill[offset].slot[0];
|
||||
spu_writech(SPU_WrEventMask, event_mask);
|
||||
}
|
||||
|
||||
static inline void restore_tag_mask(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
unsigned int tag_mask;
|
||||
|
||||
/* Restore, Step 16:
|
||||
* Restore the SPU_RdTagMsk data from the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(tag_mask);
|
||||
tag_mask = regs_spill[offset].slot[0];
|
||||
spu_writech(MFC_WrTagMask, tag_mask);
|
||||
}
|
||||
|
||||
static inline void restore_complete(void)
|
||||
{
|
||||
extern void exit_fini(void);
|
||||
unsigned int *exit_instrs = (unsigned int *)exit_fini;
|
||||
unsigned int offset;
|
||||
unsigned int stopped_status;
|
||||
unsigned int stopped_code;
|
||||
|
||||
/* Restore, Step 18:
|
||||
* Issue a stop-and-signal instruction with
|
||||
* "good context restore" signal value.
|
||||
*
|
||||
* Restore, Step 19:
|
||||
* There may be additional instructions placed
|
||||
* here by the PPE Sequence for SPU Context
|
||||
* Restore in order to restore the correct
|
||||
* "stopped state".
|
||||
*
|
||||
* This step is handled here by analyzing the
|
||||
* LSCSA.stopped_status and then modifying the
|
||||
* exit() function to behave appropriately.
|
||||
*/
|
||||
|
||||
offset = LSCSA_QW_OFFSET(stopped_status);
|
||||
stopped_status = regs_spill[offset].slot[0];
|
||||
stopped_code = regs_spill[offset].slot[1];
|
||||
|
||||
switch (stopped_status) {
|
||||
case SPU_STOPPED_STATUS_P_I:
|
||||
/* SPU_Status[P,I]=1. Add illegal instruction
|
||||
* followed by stop-and-signal instruction after
|
||||
* end of restore code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = ILLEGAL_INSTR;
|
||||
exit_instrs[2] = STOP_INSTR | stopped_code;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_P_H:
|
||||
/* SPU_Status[P,H]=1. Add 'heq $0, $0' followed
|
||||
* by stop-and-signal instruction after end of
|
||||
* restore code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = HEQ_INSTR;
|
||||
exit_instrs[2] = STOP_INSTR | stopped_code;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_S_P:
|
||||
/* SPU_Status[S,P]=1. Add nop instruction
|
||||
* followed by 'br -4' after end of restore
|
||||
* code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = STOP_INSTR | stopped_code;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_S_I:
|
||||
/* SPU_Status[S,I]=1. Add illegal instruction
|
||||
* followed by 'br -4' after end of restore code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = ILLEGAL_INSTR;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_I:
|
||||
/* SPU_Status[I]=1. Add illegal instruction followed
|
||||
* by infinite loop after end of restore sequence.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = ILLEGAL_INSTR;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_S:
|
||||
/* SPU_Status[S]=1. Add two 'nop' instructions. */
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = NOP_INSTR;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_H:
|
||||
/* SPU_Status[H]=1. Add 'heq $0, $0' instruction
|
||||
* after end of restore code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = HEQ_INSTR;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_P:
|
||||
/* SPU_Status[P]=1. Add stop-and-signal instruction
|
||||
* after end of restore code.
|
||||
*/
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = STOP_INSTR | stopped_code;
|
||||
break;
|
||||
case SPU_STOPPED_STATUS_R:
|
||||
/* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */
|
||||
exit_instrs[0] = RESTORE_COMPLETE;
|
||||
exit_instrs[1] = NOP_INSTR;
|
||||
exit_instrs[2] = NOP_INSTR;
|
||||
exit_instrs[3] = BR_INSTR;
|
||||
break;
|
||||
default:
|
||||
/* SPU_Status[R]=1. No additonal instructions. */
|
||||
break;
|
||||
}
|
||||
spu_sync();
|
||||
}
|
||||
|
||||
/**
|
||||
* main - entry point for SPU-side context restore.
|
||||
*
|
||||
* This code deviates from the documented sequence in the
|
||||
* following aspects:
|
||||
*
|
||||
* 1. The EA for LSCSA is passed from PPE in the
|
||||
* signal notification channels.
|
||||
* 2. The register spill area is pulled by SPU
|
||||
* into LS, rather than pushed by PPE.
|
||||
* 3. All 128 registers are restored by exit().
|
||||
* 4. The exit() function is modified at run
|
||||
* time in order to properly restore the
|
||||
* SPU_Status register.
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
addr64 lscsa_ea;
|
||||
|
||||
lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
|
||||
lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
|
||||
fetch_regs_from_mem(lscsa_ea);
|
||||
|
||||
set_event_mask(); /* Step 1. */
|
||||
set_tag_mask(); /* Step 2. */
|
||||
build_dma_list(lscsa_ea); /* Step 3. */
|
||||
restore_upper_240kb(lscsa_ea); /* Step 4. */
|
||||
/* Step 5: done by 'exit'. */
|
||||
restore_decr(); /* Step 6. */
|
||||
enqueue_putllc(lscsa_ea); /* Step 7. */
|
||||
set_tag_update(); /* Step 8. */
|
||||
read_tag_status(); /* Step 9. */
|
||||
read_llar_status(); /* Step 10. */
|
||||
write_ppu_mb(); /* Step 11. */
|
||||
write_ppuint_mb(); /* Step 12. */
|
||||
restore_fpcr(); /* Step 13. */
|
||||
restore_srr0(); /* Step 14. */
|
||||
restore_event_mask(); /* Step 15. */
|
||||
restore_tag_mask(); /* Step 16. */
|
||||
/* Step 17. done by 'exit'. */
|
||||
restore_complete(); /* Step 18. */
|
||||
|
||||
return 0;
|
||||
}
|
116
arch/powerpc/platforms/cell/spufs/spu_restore_crt0.S
Normal file
116
arch/powerpc/platforms/cell/spufs/spu_restore_crt0.S
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* crt0_r.S: Entry function for SPU-side context restore.
|
||||
*
|
||||
* Copyright (C) 2005 IBM
|
||||
*
|
||||
* Entry and exit function for SPU-side of the context restore
|
||||
* sequence. Sets up an initial stack frame, then branches to
|
||||
* 'main'. On return, restores all 128 registers from the LSCSA
|
||||
* and exits.
|
||||
*
|
||||
*
|
||||
* 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 <asm/spu_csa.h>
|
||||
|
||||
.data
|
||||
.align 7
|
||||
.globl regs_spill
|
||||
regs_spill:
|
||||
.space SIZEOF_SPU_SPILL_REGS, 0x0
|
||||
|
||||
.text
|
||||
.global _start
|
||||
_start:
|
||||
/* Initialize the stack pointer to point to 16368
|
||||
* (16kb-16). The back chain pointer is initialized
|
||||
* to NULL.
|
||||
*/
|
||||
il $0, 0
|
||||
il $SP, 16368
|
||||
stqd $0, 0($SP)
|
||||
|
||||
/* Allocate a minimum stack frame for the called main.
|
||||
* This is needed so that main has a place to save the
|
||||
* link register when it calls another function.
|
||||
*/
|
||||
stqd $SP, -160($SP)
|
||||
ai $SP, $SP, -160
|
||||
|
||||
/* Call the program's main function. */
|
||||
brsl $0, main
|
||||
|
||||
.global exit
|
||||
.global _exit
|
||||
exit:
|
||||
_exit:
|
||||
/* SPU Context Restore, Step 5: Restore the remaining 112 GPRs. */
|
||||
ila $3, regs_spill + 256
|
||||
restore_regs:
|
||||
lqr $4, restore_reg_insts
|
||||
restore_reg_loop:
|
||||
ai $4, $4, 4
|
||||
.balignl 16, 0x40200000
|
||||
restore_reg_insts: /* must be quad-word aligned. */
|
||||
lqd $16, 0($3)
|
||||
lqd $17, 16($3)
|
||||
lqd $18, 32($3)
|
||||
lqd $19, 48($3)
|
||||
andi $5, $4, 0x7F
|
||||
stqr $4, restore_reg_insts
|
||||
ai $3, $3, 64
|
||||
brnz $5, restore_reg_loop
|
||||
|
||||
/* SPU Context Restore Step 17: Restore the first 16 GPRs. */
|
||||
lqa $0, regs_spill + 0
|
||||
lqa $1, regs_spill + 16
|
||||
lqa $2, regs_spill + 32
|
||||
lqa $3, regs_spill + 48
|
||||
lqa $4, regs_spill + 64
|
||||
lqa $5, regs_spill + 80
|
||||
lqa $6, regs_spill + 96
|
||||
lqa $7, regs_spill + 112
|
||||
lqa $8, regs_spill + 128
|
||||
lqa $9, regs_spill + 144
|
||||
lqa $10, regs_spill + 160
|
||||
lqa $11, regs_spill + 176
|
||||
lqa $12, regs_spill + 192
|
||||
lqa $13, regs_spill + 208
|
||||
lqa $14, regs_spill + 224
|
||||
lqa $15, regs_spill + 240
|
||||
|
||||
/* Under normal circumstances, the 'exit' function
|
||||
* terminates with 'stop SPU_RESTORE_COMPLETE',
|
||||
* indicating that the SPU-side restore code has
|
||||
* completed.
|
||||
*
|
||||
* However it is possible that instructions immediately
|
||||
* following the 'stop 0x3ffc' have been modified at run
|
||||
* time so as to recreate the exact SPU_Status settings
|
||||
* from the application, e.g. illegal instruciton, halt,
|
||||
* etc.
|
||||
*/
|
||||
.global exit_fini
|
||||
.global _exit_fini
|
||||
exit_fini:
|
||||
_exit_fini:
|
||||
stop SPU_RESTORE_COMPLETE
|
||||
stop 0
|
||||
stop 0
|
||||
stop 0
|
||||
|
||||
/* Pad the size of this crt0.o to be multiple of 16 bytes. */
|
||||
.balignl 16, 0x0
|
231
arch/powerpc/platforms/cell/spufs/spu_restore_dump.h_shipped
Normal file
231
arch/powerpc/platforms/cell/spufs/spu_restore_dump.h_shipped
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* spu_restore_dump.h: Copyright (C) 2005 IBM.
|
||||
* Hex-dump auto generated from spu_restore.c.
|
||||
* Do not edit!
|
||||
*/
|
||||
static unsigned int spu_restore_code[] __page_aligned = {
|
||||
0x40800000, 0x409ff801, 0x24000080, 0x24fd8081,
|
||||
0x1cd80081, 0x33001180, 0x42030003, 0x33800284,
|
||||
0x1c010204, 0x40200000, 0x40200000, 0x40200000,
|
||||
0x34000190, 0x34004191, 0x34008192, 0x3400c193,
|
||||
0x141fc205, 0x23fffd84, 0x1c100183, 0x217ffa85,
|
||||
0x3080a000, 0x3080a201, 0x3080a402, 0x3080a603,
|
||||
0x3080a804, 0x3080aa05, 0x3080ac06, 0x3080ae07,
|
||||
0x3080b008, 0x3080b209, 0x3080b40a, 0x3080b60b,
|
||||
0x3080b80c, 0x3080ba0d, 0x3080bc0e, 0x3080be0f,
|
||||
0x00003ffc, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x01a00182, 0x3ec00083, 0xb0a14103, 0x01a00204,
|
||||
0x3ec10082, 0x4202800e, 0x04000703, 0xb0a14202,
|
||||
0x21a00803, 0x3fbf028d, 0x3f20068d, 0x3fbe0682,
|
||||
0x3fe30102, 0x21a00882, 0x3f82028f, 0x3fe3078f,
|
||||
0x3fbf0784, 0x3f200204, 0x3fbe0204, 0x3fe30204,
|
||||
0x04000203, 0x21a00903, 0x40848002, 0x21a00982,
|
||||
0x40800003, 0x21a00a03, 0x40802002, 0x21a00a82,
|
||||
0x21a00083, 0x40800082, 0x21a00b02, 0x10002818,
|
||||
0x40a80002, 0x32800007, 0x4207000c, 0x18008208,
|
||||
0x40a0000b, 0x4080020a, 0x40800709, 0x00200000,
|
||||
0x42070002, 0x3ac30384, 0x1cffc489, 0x00200000,
|
||||
0x18008383, 0x38830382, 0x4cffc486, 0x3ac28185,
|
||||
0xb0408584, 0x28830382, 0x1c020387, 0x38828182,
|
||||
0xb0408405, 0x1802c408, 0x28828182, 0x217ff886,
|
||||
0x04000583, 0x21a00803, 0x3fbe0682, 0x3fe30102,
|
||||
0x04000106, 0x21a00886, 0x04000603, 0x21a00903,
|
||||
0x40803c02, 0x21a00982, 0x40800003, 0x04000184,
|
||||
0x21a00a04, 0x40802202, 0x21a00a82, 0x42028005,
|
||||
0x34208702, 0x21002282, 0x21a00804, 0x21a00886,
|
||||
0x3fbf0782, 0x3f200102, 0x3fbe0102, 0x3fe30102,
|
||||
0x21a00902, 0x40804003, 0x21a00983, 0x21a00a04,
|
||||
0x40805a02, 0x21a00a82, 0x40800083, 0x21a00b83,
|
||||
0x01a00c02, 0x01a00d83, 0x3420c282, 0x21a00e02,
|
||||
0x34210283, 0x21a00f03, 0x34200284, 0x77400200,
|
||||
0x3421c282, 0x21a00702, 0x34218283, 0x21a00083,
|
||||
0x34214282, 0x21a00b02, 0x4200480c, 0x00200000,
|
||||
0x1c010286, 0x34220284, 0x34220302, 0x0f608203,
|
||||
0x5c024204, 0x3b81810b, 0x42013c02, 0x00200000,
|
||||
0x18008185, 0x38808183, 0x3b814182, 0x21004e84,
|
||||
0x4020007f, 0x35000100, 0x000004e0, 0x000002a0,
|
||||
0x000002e8, 0x00000428, 0x00000360, 0x000002e8,
|
||||
0x000004a0, 0x00000468, 0x000003c8, 0x00000360,
|
||||
0x409ffe02, 0x30801203, 0x40800204, 0x3ec40085,
|
||||
0x10009c09, 0x3ac10606, 0xb060c105, 0x4020007f,
|
||||
0x4020007f, 0x20801203, 0x38810602, 0xb0408586,
|
||||
0x28810602, 0x32004180, 0x34204702, 0x21a00382,
|
||||
0x4020007f, 0x327fdc80, 0x409ffe02, 0x30801203,
|
||||
0x40800204, 0x3ec40087, 0x40800405, 0x00200000,
|
||||
0x40800606, 0x3ac10608, 0x3ac14609, 0x3ac1860a,
|
||||
0xb060c107, 0x20801203, 0x41004003, 0x38810602,
|
||||
0x4020007f, 0xb0408188, 0x4020007f, 0x28810602,
|
||||
0x41201002, 0x38814603, 0x10009c09, 0xb060c109,
|
||||
0x4020007f, 0x28814603, 0x41193f83, 0x38818602,
|
||||
0x60ffc003, 0xb040818a, 0x28818602, 0x32003080,
|
||||
0x409ffe02, 0x30801203, 0x40800204, 0x3ec40087,
|
||||
0x41201008, 0x10009c14, 0x40800405, 0x3ac10609,
|
||||
0x40800606, 0x3ac1460a, 0xb060c107, 0x3ac1860b,
|
||||
0x20801203, 0x38810602, 0xb0408409, 0x28810602,
|
||||
0x38814603, 0xb060c40a, 0x4020007f, 0x28814603,
|
||||
0x41193f83, 0x38818602, 0x60ffc003, 0xb040818b,
|
||||
0x28818602, 0x32002380, 0x409ffe02, 0x30801204,
|
||||
0x40800205, 0x3ec40083, 0x40800406, 0x3ac14607,
|
||||
0x3ac18608, 0xb0810103, 0x41004002, 0x20801204,
|
||||
0x4020007f, 0x38814603, 0x10009c0b, 0xb060c107,
|
||||
0x4020007f, 0x4020007f, 0x28814603, 0x38818602,
|
||||
0x4020007f, 0x4020007f, 0xb0408588, 0x28818602,
|
||||
0x4020007f, 0x32001780, 0x409ffe02, 0x1000640e,
|
||||
0x40800204, 0x30801203, 0x40800405, 0x3ec40087,
|
||||
0x40800606, 0x3ac10608, 0x3ac14609, 0x3ac1860a,
|
||||
0xb060c107, 0x20801203, 0x413d8003, 0x38810602,
|
||||
0x4020007f, 0x327fd780, 0x409ffe02, 0x10007f0c,
|
||||
0x40800205, 0x30801204, 0x40800406, 0x3ec40083,
|
||||
0x3ac14607, 0x3ac18608, 0xb0810103, 0x413d8002,
|
||||
0x20801204, 0x38814603, 0x4020007f, 0x327feb80,
|
||||
0x409ffe02, 0x30801203, 0x40800204, 0x3ec40087,
|
||||
0x40800405, 0x1000650a, 0x40800606, 0x3ac10608,
|
||||
0x3ac14609, 0x3ac1860a, 0xb060c107, 0x20801203,
|
||||
0x38810602, 0xb0408588, 0x4020007f, 0x327fc980,
|
||||
0x00400000, 0x40800003, 0x4020007f, 0x35000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
195
arch/powerpc/platforms/cell/spufs/spu_save.c
Normal file
195
arch/powerpc/platforms/cell/spufs/spu_save.c
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* spu_save.c
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005
|
||||
*
|
||||
* SPU-side context save sequence outlined in
|
||||
* Synergistic Processor Element Book IV
|
||||
*
|
||||
* Author: Mark Nutter <mnutter@us.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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LS_SIZE
|
||||
#define LS_SIZE 0x40000 /* 256K (in bytes) */
|
||||
#endif
|
||||
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#include <spu_intrinsics.h>
|
||||
#include <asm/spu_csa.h>
|
||||
#include "spu_utils.h"
|
||||
|
||||
static inline void save_event_mask(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
/* Save, Step 2:
|
||||
* Read the SPU_RdEventMsk channel and save to the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(event_mask);
|
||||
regs_spill[offset].slot[0] = spu_readch(SPU_RdEventStatMask);
|
||||
}
|
||||
|
||||
static inline void save_tag_mask(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
/* Save, Step 3:
|
||||
* Read the SPU_RdTagMsk channel and save to the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(tag_mask);
|
||||
regs_spill[offset].slot[0] = spu_readch(MFC_RdTagMask);
|
||||
}
|
||||
|
||||
static inline void save_upper_240kb(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ls = 16384;
|
||||
unsigned int list = (unsigned int)&dma_list[0];
|
||||
unsigned int size = sizeof(dma_list);
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0x24; /* PUTL */
|
||||
|
||||
/* Save, Step 7:
|
||||
* Enqueue the PUTL command (tag 0) to the MFC SPU command
|
||||
* queue to transfer the remaining 240 kb of LS to CSA.
|
||||
*/
|
||||
spu_writech(MFC_LSA, ls);
|
||||
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
|
||||
spu_writech(MFC_EAL, list);
|
||||
spu_writech(MFC_Size, size);
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void save_fpcr(void)
|
||||
{
|
||||
// vector unsigned int fpcr;
|
||||
unsigned int offset;
|
||||
|
||||
/* Save, Step 9:
|
||||
* Issue the floating-point status and control register
|
||||
* read instruction, and save to the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(fpcr);
|
||||
regs_spill[offset].v = spu_mffpscr();
|
||||
}
|
||||
|
||||
static inline void save_decr(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
/* Save, Step 10:
|
||||
* Read and save the SPU_RdDec channel data to
|
||||
* the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(decr);
|
||||
regs_spill[offset].slot[0] = spu_readch(SPU_RdDec);
|
||||
}
|
||||
|
||||
static inline void save_srr0(void)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
/* Save, Step 11:
|
||||
* Read and save the SPU_WSRR0 channel data to
|
||||
* the LSCSA.
|
||||
*/
|
||||
offset = LSCSA_QW_OFFSET(srr0);
|
||||
regs_spill[offset].slot[0] = spu_readch(SPU_RdSRR0);
|
||||
}
|
||||
|
||||
static inline void spill_regs_to_mem(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ls = (unsigned int)®s_spill[0];
|
||||
unsigned int size = sizeof(regs_spill);
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0x20; /* PUT */
|
||||
|
||||
/* Save, Step 13:
|
||||
* Enqueue a PUT command (tag 0) to send the LSCSA
|
||||
* to the CSA.
|
||||
*/
|
||||
spu_writech(MFC_LSA, ls);
|
||||
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
|
||||
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
|
||||
spu_writech(MFC_Size, size);
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void enqueue_sync(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0xCC;
|
||||
|
||||
/* Save, Step 14:
|
||||
* Enqueue an MFC_SYNC command (tag 0).
|
||||
*/
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void save_complete(void)
|
||||
{
|
||||
/* Save, Step 18:
|
||||
* Issue a stop-and-signal instruction indicating
|
||||
* "save complete". Note: This function will not
|
||||
* return!!
|
||||
*/
|
||||
spu_stop(SPU_SAVE_COMPLETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* main - entry point for SPU-side context save.
|
||||
*
|
||||
* This code deviates from the documented sequence as follows:
|
||||
*
|
||||
* 1. The EA for LSCSA is passed from PPE in the
|
||||
* signal notification channels.
|
||||
* 2. All 128 registers are saved by crt0.o.
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
addr64 lscsa_ea;
|
||||
|
||||
lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
|
||||
lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
|
||||
|
||||
/* Step 1: done by exit(). */
|
||||
save_event_mask(); /* Step 2. */
|
||||
save_tag_mask(); /* Step 3. */
|
||||
set_event_mask(); /* Step 4. */
|
||||
set_tag_mask(); /* Step 5. */
|
||||
build_dma_list(lscsa_ea); /* Step 6. */
|
||||
save_upper_240kb(lscsa_ea); /* Step 7. */
|
||||
/* Step 8: done by exit(). */
|
||||
save_fpcr(); /* Step 9. */
|
||||
save_decr(); /* Step 10. */
|
||||
save_srr0(); /* Step 11. */
|
||||
enqueue_putllc(lscsa_ea); /* Step 12. */
|
||||
spill_regs_to_mem(lscsa_ea); /* Step 13. */
|
||||
enqueue_sync(lscsa_ea); /* Step 14. */
|
||||
set_tag_update(); /* Step 15. */
|
||||
read_tag_status(); /* Step 16. */
|
||||
read_llar_status(); /* Step 17. */
|
||||
save_complete(); /* Step 18. */
|
||||
|
||||
return 0;
|
||||
}
|
102
arch/powerpc/platforms/cell/spufs/spu_save_crt0.S
Normal file
102
arch/powerpc/platforms/cell/spufs/spu_save_crt0.S
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* crt0_s.S: Entry function for SPU-side context save.
|
||||
*
|
||||
* Copyright (C) 2005 IBM
|
||||
*
|
||||
* Entry function for SPU-side of the context save sequence.
|
||||
* Saves all 128 GPRs, sets up an initial stack frame, then
|
||||
* branches to 'main'.
|
||||
*
|
||||
*
|
||||
* 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 <asm/spu_csa.h>
|
||||
|
||||
.data
|
||||
.align 7
|
||||
.globl regs_spill
|
||||
regs_spill:
|
||||
.space SIZEOF_SPU_SPILL_REGS, 0x0
|
||||
|
||||
.text
|
||||
.global _start
|
||||
_start:
|
||||
/* SPU Context Save Step 1: Save the first 16 GPRs. */
|
||||
stqa $0, regs_spill + 0
|
||||
stqa $1, regs_spill + 16
|
||||
stqa $2, regs_spill + 32
|
||||
stqa $3, regs_spill + 48
|
||||
stqa $4, regs_spill + 64
|
||||
stqa $5, regs_spill + 80
|
||||
stqa $6, regs_spill + 96
|
||||
stqa $7, regs_spill + 112
|
||||
stqa $8, regs_spill + 128
|
||||
stqa $9, regs_spill + 144
|
||||
stqa $10, regs_spill + 160
|
||||
stqa $11, regs_spill + 176
|
||||
stqa $12, regs_spill + 192
|
||||
stqa $13, regs_spill + 208
|
||||
stqa $14, regs_spill + 224
|
||||
stqa $15, regs_spill + 240
|
||||
|
||||
/* SPU Context Save, Step 8: Save the remaining 112 GPRs. */
|
||||
ila $3, regs_spill + 256
|
||||
save_regs:
|
||||
lqr $4, save_reg_insts
|
||||
save_reg_loop:
|
||||
ai $4, $4, 4
|
||||
.balignl 16, 0x40200000
|
||||
save_reg_insts: /* must be quad-word aligned. */
|
||||
stqd $16, 0($3)
|
||||
stqd $17, 16($3)
|
||||
stqd $18, 32($3)
|
||||
stqd $19, 48($3)
|
||||
andi $5, $4, 0x7F
|
||||
stqr $4, save_reg_insts
|
||||
ai $3, $3, 64
|
||||
brnz $5, save_reg_loop
|
||||
|
||||
/* Initialize the stack pointer to point to 16368
|
||||
* (16kb-16). The back chain pointer is initialized
|
||||
* to NULL.
|
||||
*/
|
||||
il $0, 0
|
||||
il $SP, 16368
|
||||
stqd $0, 0($SP)
|
||||
|
||||
/* Allocate a minimum stack frame for the called main.
|
||||
* This is needed so that main has a place to save the
|
||||
* link register when it calls another function.
|
||||
*/
|
||||
stqd $SP, -160($SP)
|
||||
ai $SP, $SP, -160
|
||||
|
||||
/* Call the program's main function. */
|
||||
brsl $0, main
|
||||
|
||||
/* In this case main should not return; if it does
|
||||
* there has been an error in the sequence. Execute
|
||||
* stop-and-signal with code=0.
|
||||
*/
|
||||
.global exit
|
||||
.global _exit
|
||||
exit:
|
||||
_exit:
|
||||
stop 0x0
|
||||
|
||||
/* Pad the size of this crt0.o to be multiple of 16 bytes. */
|
||||
.balignl 16, 0x0
|
||||
|
191
arch/powerpc/platforms/cell/spufs/spu_save_dump.h_shipped
Normal file
191
arch/powerpc/platforms/cell/spufs/spu_save_dump.h_shipped
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* spu_save_dump.h: Copyright (C) 2005 IBM.
|
||||
* Hex-dump auto generated from spu_save.c.
|
||||
* Do not edit!
|
||||
*/
|
||||
static unsigned int spu_save_code[] __page_aligned = {
|
||||
0x20805000, 0x20805201, 0x20805402, 0x20805603,
|
||||
0x20805804, 0x20805a05, 0x20805c06, 0x20805e07,
|
||||
0x20806008, 0x20806209, 0x2080640a, 0x2080660b,
|
||||
0x2080680c, 0x20806a0d, 0x20806c0e, 0x20806e0f,
|
||||
0x4201c003, 0x33800184, 0x1c010204, 0x40200000,
|
||||
0x24000190, 0x24004191, 0x24008192, 0x2400c193,
|
||||
0x141fc205, 0x23fffd84, 0x1c100183, 0x217ffb85,
|
||||
0x40800000, 0x409ff801, 0x24000080, 0x24fd8081,
|
||||
0x1cd80081, 0x33000180, 0x00000000, 0x00000000,
|
||||
0x01a00182, 0x3ec00083, 0xb1c38103, 0x01a00204,
|
||||
0x3ec10082, 0x4201400d, 0xb1c38202, 0x01a00583,
|
||||
0x34218682, 0x3ed80684, 0xb0408184, 0x24218682,
|
||||
0x01a00603, 0x00200000, 0x34214682, 0x3ed40684,
|
||||
0xb0408184, 0x40800003, 0x24214682, 0x21a00083,
|
||||
0x40800082, 0x21a00b02, 0x4020007f, 0x1000251e,
|
||||
0x40a80002, 0x32800008, 0x4205c00c, 0x00200000,
|
||||
0x40a0000b, 0x3f82070f, 0x4080020a, 0x40800709,
|
||||
0x3fe3078f, 0x3fbf0783, 0x3f200183, 0x3fbe0183,
|
||||
0x3fe30187, 0x18008387, 0x4205c002, 0x3ac30404,
|
||||
0x1cffc489, 0x00200000, 0x18008403, 0x38830402,
|
||||
0x4cffc486, 0x3ac28185, 0xb0408584, 0x28830402,
|
||||
0x1c020408, 0x38828182, 0xb0408385, 0x1802c387,
|
||||
0x28828182, 0x217ff886, 0x04000582, 0x32800007,
|
||||
0x21a00802, 0x3fbf0705, 0x3f200285, 0x3fbe0285,
|
||||
0x3fe30285, 0x21a00885, 0x04000603, 0x21a00903,
|
||||
0x40803c02, 0x21a00982, 0x04000386, 0x21a00a06,
|
||||
0x40801202, 0x21a00a82, 0x73000003, 0x24200683,
|
||||
0x01a00404, 0x00200000, 0x34204682, 0x3ec40683,
|
||||
0xb0408203, 0x24204682, 0x01a00783, 0x00200000,
|
||||
0x3421c682, 0x3edc0684, 0xb0408184, 0x2421c682,
|
||||
0x21a00806, 0x21a00885, 0x3fbf0784, 0x3f200204,
|
||||
0x3fbe0204, 0x3fe30204, 0x21a00904, 0x40804002,
|
||||
0x21a00982, 0x21a00a06, 0x40805a02, 0x21a00a82,
|
||||
0x04000683, 0x21a00803, 0x21a00885, 0x21a00904,
|
||||
0x40848002, 0x21a00982, 0x21a00a06, 0x40801002,
|
||||
0x21a00a82, 0x21a00a06, 0x40806602, 0x00200000,
|
||||
0x35800009, 0x21a00a82, 0x40800083, 0x21a00b83,
|
||||
0x01a00c02, 0x01a00d83, 0x00003ffb, 0x40800003,
|
||||
0x4020007f, 0x35000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
160
arch/powerpc/platforms/cell/spufs/spu_utils.h
Normal file
160
arch/powerpc/platforms/cell/spufs/spu_utils.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* utils.h: Utilities for SPU-side of the context switch operation.
|
||||
*
|
||||
* (C) Copyright IBM 2005
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _SPU_CONTEXT_UTILS_H_
|
||||
#define _SPU_CONTEXT_UTILS_H_
|
||||
|
||||
/*
|
||||
* 64-bit safe EA.
|
||||
*/
|
||||
typedef union {
|
||||
unsigned long long ull;
|
||||
unsigned int ui[2];
|
||||
} addr64;
|
||||
|
||||
/*
|
||||
* 128-bit register template.
|
||||
*/
|
||||
typedef union {
|
||||
unsigned int slot[4];
|
||||
vector unsigned int v;
|
||||
} spu_reg128v;
|
||||
|
||||
/*
|
||||
* DMA list structure.
|
||||
*/
|
||||
struct dma_list_elem {
|
||||
unsigned int size;
|
||||
unsigned int ea_low;
|
||||
};
|
||||
|
||||
/*
|
||||
* Declare storage for 8-byte aligned DMA list.
|
||||
*/
|
||||
struct dma_list_elem dma_list[15] __attribute__ ((aligned(8)));
|
||||
|
||||
/*
|
||||
* External definition for storage
|
||||
* declared in crt0.
|
||||
*/
|
||||
extern spu_reg128v regs_spill[NR_SPU_SPILL_REGS];
|
||||
|
||||
/*
|
||||
* Compute LSCSA byte offset for a given field.
|
||||
*/
|
||||
static struct spu_lscsa *dummy = (struct spu_lscsa *)0;
|
||||
#define LSCSA_BYTE_OFFSET(_field) \
|
||||
((char *)(&(dummy->_field)) - (char *)(&(dummy->gprs[0].slot[0])))
|
||||
#define LSCSA_QW_OFFSET(_field) (LSCSA_BYTE_OFFSET(_field) >> 4)
|
||||
|
||||
static inline void set_event_mask(void)
|
||||
{
|
||||
unsigned int event_mask = 0;
|
||||
|
||||
/* Save, Step 4:
|
||||
* Restore, Step 1:
|
||||
* Set the SPU_RdEventMsk channel to zero to mask
|
||||
* all events.
|
||||
*/
|
||||
spu_writech(SPU_WrEventMask, event_mask);
|
||||
}
|
||||
|
||||
static inline void set_tag_mask(void)
|
||||
{
|
||||
unsigned int tag_mask = 1;
|
||||
|
||||
/* Save, Step 5:
|
||||
* Restore, Step 2:
|
||||
* Set the SPU_WrTagMsk channel to '01' to unmask
|
||||
* only tag group 0.
|
||||
*/
|
||||
spu_writech(MFC_WrTagMask, tag_mask);
|
||||
}
|
||||
|
||||
static inline void build_dma_list(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ea_low;
|
||||
int i;
|
||||
|
||||
/* Save, Step 6:
|
||||
* Restore, Step 3:
|
||||
* Update the effective address for the CSA in the
|
||||
* pre-canned DMA-list in local storage.
|
||||
*/
|
||||
ea_low = lscsa_ea.ui[1];
|
||||
ea_low += LSCSA_BYTE_OFFSET(ls[16384]);
|
||||
|
||||
for (i = 0; i < 15; i++, ea_low += 16384) {
|
||||
dma_list[i].size = 16384;
|
||||
dma_list[i].ea_low = ea_low;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void enqueue_putllc(addr64 lscsa_ea)
|
||||
{
|
||||
unsigned int ls = 0;
|
||||
unsigned int size = 128;
|
||||
unsigned int tag_id = 0;
|
||||
unsigned int cmd = 0xB4; /* PUTLLC */
|
||||
|
||||
/* Save, Step 12:
|
||||
* Restore, Step 7:
|
||||
* Send a PUTLLC (tag 0) command to the MFC using
|
||||
* an effective address in the CSA in order to
|
||||
* remove any possible lock-line reservation.
|
||||
*/
|
||||
spu_writech(MFC_LSA, ls);
|
||||
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
|
||||
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
|
||||
spu_writech(MFC_Size, size);
|
||||
spu_writech(MFC_TagID, tag_id);
|
||||
spu_writech(MFC_Cmd, cmd);
|
||||
}
|
||||
|
||||
static inline void set_tag_update(void)
|
||||
{
|
||||
unsigned int update_any = 1;
|
||||
|
||||
/* Save, Step 15:
|
||||
* Restore, Step 8:
|
||||
* Write the MFC_TagUpdate channel with '01'.
|
||||
*/
|
||||
spu_writech(MFC_WrTagUpdate, update_any);
|
||||
}
|
||||
|
||||
static inline void read_tag_status(void)
|
||||
{
|
||||
/* Save, Step 16:
|
||||
* Restore, Step 9:
|
||||
* Read the MFC_TagStat channel data.
|
||||
*/
|
||||
spu_readch(MFC_RdTagStat);
|
||||
}
|
||||
|
||||
static inline void read_llar_status(void)
|
||||
{
|
||||
/* Save, Step 17:
|
||||
* Restore, Step 10:
|
||||
* Read the MFC_AtomicStat channel data.
|
||||
*/
|
||||
spu_readch(MFC_RdAtomicStat);
|
||||
}
|
||||
|
||||
#endif /* _SPU_CONTEXT_UTILS_H_ */
|
163
arch/powerpc/platforms/cell/spufs/spufs.h
Normal file
163
arch/powerpc/platforms/cell/spufs/spufs.h
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* SPU file system
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
#ifndef SPUFS_H
|
||||
#define SPUFS_H
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <asm/spu.h>
|
||||
#include <asm/spu_csa.h>
|
||||
|
||||
/* The magic number for our file system */
|
||||
enum {
|
||||
SPUFS_MAGIC = 0x23c9b64e,
|
||||
};
|
||||
|
||||
struct spu_context_ops;
|
||||
|
||||
#define SPU_CONTEXT_PREEMPT 0UL
|
||||
|
||||
struct spu_context {
|
||||
struct spu *spu; /* pointer to a physical SPU */
|
||||
struct spu_state csa; /* SPU context save area. */
|
||||
spinlock_t mmio_lock; /* protects mmio access */
|
||||
struct address_space *local_store;/* local store backing store */
|
||||
|
||||
enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
|
||||
struct rw_semaphore state_sema;
|
||||
struct semaphore run_sema;
|
||||
|
||||
struct mm_struct *owner;
|
||||
|
||||
struct kref kref;
|
||||
wait_queue_head_t ibox_wq;
|
||||
wait_queue_head_t wbox_wq;
|
||||
wait_queue_head_t stop_wq;
|
||||
struct fasync_struct *ibox_fasync;
|
||||
struct fasync_struct *wbox_fasync;
|
||||
struct spu_context_ops *ops;
|
||||
struct work_struct reap_work;
|
||||
u64 flags;
|
||||
};
|
||||
|
||||
/* SPU context query/set operations. */
|
||||
struct spu_context_ops {
|
||||
int (*mbox_read) (struct spu_context * ctx, u32 * data);
|
||||
u32(*mbox_stat_read) (struct spu_context * ctx);
|
||||
unsigned int (*mbox_stat_poll)(struct spu_context *ctx,
|
||||
unsigned int events);
|
||||
int (*ibox_read) (struct spu_context * ctx, u32 * data);
|
||||
int (*wbox_write) (struct spu_context * ctx, u32 data);
|
||||
u32(*signal1_read) (struct spu_context * ctx);
|
||||
void (*signal1_write) (struct spu_context * ctx, u32 data);
|
||||
u32(*signal2_read) (struct spu_context * ctx);
|
||||
void (*signal2_write) (struct spu_context * ctx, u32 data);
|
||||
void (*signal1_type_set) (struct spu_context * ctx, u64 val);
|
||||
u64(*signal1_type_get) (struct spu_context * ctx);
|
||||
void (*signal2_type_set) (struct spu_context * ctx, u64 val);
|
||||
u64(*signal2_type_get) (struct spu_context * ctx);
|
||||
u32(*npc_read) (struct spu_context * ctx);
|
||||
void (*npc_write) (struct spu_context * ctx, u32 data);
|
||||
u32(*status_read) (struct spu_context * ctx);
|
||||
char*(*get_ls) (struct spu_context * ctx);
|
||||
void (*runcntl_write) (struct spu_context * ctx, u32 data);
|
||||
void (*runcntl_stop) (struct spu_context * ctx);
|
||||
};
|
||||
|
||||
extern struct spu_context_ops spu_hw_ops;
|
||||
extern struct spu_context_ops spu_backing_ops;
|
||||
|
||||
struct spufs_inode_info {
|
||||
struct spu_context *i_ctx;
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
#define SPUFS_I(inode) \
|
||||
container_of(inode, struct spufs_inode_info, vfs_inode)
|
||||
|
||||
extern struct tree_descr spufs_dir_contents[];
|
||||
|
||||
/* system call implementation */
|
||||
long spufs_run_spu(struct file *file,
|
||||
struct spu_context *ctx, u32 *npc, u32 *status);
|
||||
long spufs_create_thread(struct nameidata *nd,
|
||||
unsigned int flags, mode_t mode);
|
||||
extern struct file_operations spufs_context_fops;
|
||||
|
||||
/* context management */
|
||||
struct spu_context * alloc_spu_context(struct address_space *local_store);
|
||||
void destroy_spu_context(struct kref *kref);
|
||||
struct spu_context * get_spu_context(struct spu_context *ctx);
|
||||
int put_spu_context(struct spu_context *ctx);
|
||||
void spu_unmap_mappings(struct spu_context *ctx);
|
||||
|
||||
void spu_forget(struct spu_context *ctx);
|
||||
void spu_acquire(struct spu_context *ctx);
|
||||
void spu_release(struct spu_context *ctx);
|
||||
int spu_acquire_runnable(struct spu_context *ctx);
|
||||
void spu_acquire_saved(struct spu_context *ctx);
|
||||
|
||||
int spu_activate(struct spu_context *ctx, u64 flags);
|
||||
void spu_deactivate(struct spu_context *ctx);
|
||||
void spu_yield(struct spu_context *ctx);
|
||||
int __init spu_sched_init(void);
|
||||
void __exit spu_sched_exit(void);
|
||||
|
||||
/*
|
||||
* spufs_wait
|
||||
* Same as wait_event_interruptible(), except that here
|
||||
* we need to call spu_release(ctx) before sleeping, and
|
||||
* then spu_acquire(ctx) when awoken.
|
||||
*/
|
||||
|
||||
#define spufs_wait(wq, condition) \
|
||||
({ \
|
||||
int __ret = 0; \
|
||||
DEFINE_WAIT(__wait); \
|
||||
for (;;) { \
|
||||
prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \
|
||||
if (condition) \
|
||||
break; \
|
||||
if (!signal_pending(current)) { \
|
||||
spu_release(ctx); \
|
||||
schedule(); \
|
||||
spu_acquire(ctx); \
|
||||
continue; \
|
||||
} \
|
||||
__ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
finish_wait(&(wq), &__wait); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
size_t spu_wbox_write(struct spu_context *ctx, u32 data);
|
||||
size_t spu_ibox_read(struct spu_context *ctx, u32 *data);
|
||||
|
||||
/* irq callback funcs. */
|
||||
void spufs_ibox_callback(struct spu *spu);
|
||||
void spufs_wbox_callback(struct spu *spu);
|
||||
void spufs_stop_callback(struct spu *spu);
|
||||
|
||||
#endif
|
2180
arch/powerpc/platforms/cell/spufs/switch.c
Normal file
2180
arch/powerpc/platforms/cell/spufs/switch.c
Normal file
File diff suppressed because it is too large
Load Diff
101
arch/powerpc/platforms/cell/spufs/syscalls.c
Normal file
101
arch/powerpc/platforms/cell/spufs/syscalls.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "spufs.h"
|
||||
|
||||
/**
|
||||
* sys_spu_run - run code loaded into an SPU
|
||||
*
|
||||
* @unpc: next program counter for the SPU
|
||||
* @ustatus: status of the SPU
|
||||
*
|
||||
* This system call transfers the control of execution of a
|
||||
* user space thread to an SPU. It will return when the
|
||||
* SPU has finished executing or when it hits an error
|
||||
* condition and it will be interrupted if a signal needs
|
||||
* to be delivered to a handler in user space.
|
||||
*
|
||||
* The next program counter is set to the passed value
|
||||
* before the SPU starts fetching code and the user space
|
||||
* pointer gets updated with the new value when returning
|
||||
* from kernel space.
|
||||
*
|
||||
* The status value returned from spu_run reflects the
|
||||
* value of the spu_status register after the SPU has stopped.
|
||||
*
|
||||
*/
|
||||
long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus)
|
||||
{
|
||||
long ret;
|
||||
struct spufs_inode_info *i;
|
||||
u32 npc, status;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (get_user(npc, unpc) || get_user(status, ustatus))
|
||||
goto out;
|
||||
|
||||
/* check if this file was created by spu_create */
|
||||
ret = -EINVAL;
|
||||
if (filp->f_op != &spufs_context_fops)
|
||||
goto out;
|
||||
|
||||
i = SPUFS_I(filp->f_dentry->d_inode);
|
||||
ret = spufs_run_spu(filp, i->i_ctx, &npc, &status);
|
||||
|
||||
if (put_user(npc, unpc) || put_user(status, ustatus))
|
||||
ret = -EFAULT;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
|
||||
{
|
||||
int fput_needed;
|
||||
struct file *filp;
|
||||
long ret;
|
||||
|
||||
ret = -EBADF;
|
||||
filp = fget_light(fd, &fput_needed);
|
||||
if (filp) {
|
||||
ret = do_spu_run(filp, unpc, ustatus);
|
||||
fput_light(filp, fput_needed);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
asmlinkage long sys_spu_create(const char __user *pathname,
|
||||
unsigned int flags, mode_t mode)
|
||||
{
|
||||
char *tmp;
|
||||
int ret;
|
||||
|
||||
tmp = getname(pathname);
|
||||
ret = PTR_ERR(tmp);
|
||||
if (!IS_ERR(tmp)) {
|
||||
struct nameidata nd;
|
||||
|
||||
ret = path_lookup(tmp, LOOKUP_PARENT|
|
||||
LOOKUP_OPEN|LOOKUP_CREATE, &nd);
|
||||
if (!ret) {
|
||||
ret = spufs_create_thread(&nd, flags, mode);
|
||||
path_release(&nd);
|
||||
}
|
||||
putname(tmp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct spufs_calls spufs_calls = {
|
||||
.create_thread = sys_spu_create,
|
||||
.spu_run = do_spu_run,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
@ -49,7 +49,6 @@
|
||||
#include <asm/hydra.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/btext.h>
|
||||
#include <asm/i8259.h>
|
||||
#include <asm/mpic.h>
|
||||
#include <asm/rtas.h>
|
||||
@ -58,7 +57,6 @@
|
||||
#include "chrp.h"
|
||||
|
||||
void rtas_indicator_progress(char *, unsigned short);
|
||||
void btext_progress(char *, unsigned short);
|
||||
|
||||
int _chrp_type;
|
||||
EXPORT_SYMBOL(_chrp_type);
|
||||
@ -264,11 +262,6 @@ void __init chrp_setup_arch(void)
|
||||
ppc_md.set_rtc_time = rtas_set_rtc_time;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
if (ppc_md.progress == NULL && boot_text_mapped)
|
||||
ppc_md.progress = btext_progress;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
/* this is fine for chrp */
|
||||
initrd_below_start_ok = 1;
|
||||
@ -522,12 +515,3 @@ void __init chrp_init(void)
|
||||
smp_ops = &chrp_smp_ops;
|
||||
#endif /* CONFIG_SMP */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
void
|
||||
btext_progress(char *s, unsigned short hex)
|
||||
{
|
||||
btext_drawstring(s);
|
||||
btext_drawstring("\n");
|
||||
}
|
||||
#endif /* CONFIG_BOOTX_TEXT */
|
||||
|
@ -35,161 +35,138 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/paca.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "call_pci.h"
|
||||
|
||||
static long Pci_Interrupt_Count;
|
||||
static long Pci_Event_Count;
|
||||
#if defined(CONFIG_SMP)
|
||||
extern void iSeries_smp_message_recv(struct pt_regs *);
|
||||
#endif
|
||||
|
||||
enum XmPciLpEvent_Subtype {
|
||||
XmPciLpEvent_BusCreated = 0, // PHB has been created
|
||||
XmPciLpEvent_BusError = 1, // PHB has failed
|
||||
XmPciLpEvent_BusFailed = 2, // Msg to Secondary, Primary failed bus
|
||||
XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed
|
||||
XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered
|
||||
XmPciLpEvent_BusRecovered = 12, // PHB has been recovered
|
||||
XmPciLpEvent_UnQuiesceBus = 18, // Secondary bus unqiescing
|
||||
XmPciLpEvent_BridgeError = 21, // Bridge Error
|
||||
XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt
|
||||
enum pci_event_type {
|
||||
pe_bus_created = 0, /* PHB has been created */
|
||||
pe_bus_error = 1, /* PHB has failed */
|
||||
pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */
|
||||
pe_node_failed = 4, /* Multi-adapter bridge has failed */
|
||||
pe_node_recovered = 5, /* Multi-adapter bridge has recovered */
|
||||
pe_bus_recovered = 12, /* PHB has been recovered */
|
||||
pe_unquiese_bus = 18, /* Secondary bus unqiescing */
|
||||
pe_bridge_error = 21, /* Bridge Error */
|
||||
pe_slot_interrupt = 22 /* Slot interrupt */
|
||||
};
|
||||
|
||||
struct XmPciLpEvent_BusInterrupt {
|
||||
HvBusNumber busNumber;
|
||||
HvSubBusNumber subBusNumber;
|
||||
};
|
||||
|
||||
struct XmPciLpEvent_NodeInterrupt {
|
||||
HvBusNumber busNumber;
|
||||
HvSubBusNumber subBusNumber;
|
||||
HvAgentId deviceId;
|
||||
};
|
||||
|
||||
struct XmPciLpEvent {
|
||||
struct HvLpEvent hvLpEvent;
|
||||
|
||||
struct pci_event {
|
||||
struct HvLpEvent event;
|
||||
union {
|
||||
u64 alignData; // Align on an 8-byte boundary
|
||||
|
||||
u64 __align; /* Align on an 8-byte boundary */
|
||||
struct {
|
||||
u32 fisr;
|
||||
HvBusNumber busNumber;
|
||||
HvSubBusNumber subBusNumber;
|
||||
HvAgentId deviceId;
|
||||
} slotInterrupt;
|
||||
|
||||
struct XmPciLpEvent_BusInterrupt busFailed;
|
||||
struct XmPciLpEvent_BusInterrupt busRecovered;
|
||||
struct XmPciLpEvent_BusInterrupt busCreated;
|
||||
|
||||
struct XmPciLpEvent_NodeInterrupt nodeFailed;
|
||||
struct XmPciLpEvent_NodeInterrupt nodeRecovered;
|
||||
|
||||
} eventData;
|
||||
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
HvAgentId dev_id;
|
||||
} slot;
|
||||
struct {
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
} bus;
|
||||
struct {
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
HvAgentId dev_id;
|
||||
} node;
|
||||
} data;
|
||||
};
|
||||
|
||||
static void intReceived(struct XmPciLpEvent *eventParm,
|
||||
struct pt_regs *regsParm)
|
||||
static DEFINE_SPINLOCK(pending_irqs_lock);
|
||||
static int num_pending_irqs;
|
||||
static int pending_irqs[NR_IRQS];
|
||||
|
||||
static void int_received(struct pci_event *event, struct pt_regs *regs)
|
||||
{
|
||||
int irq;
|
||||
#ifdef CONFIG_IRQSTACKS
|
||||
struct thread_info *curtp, *irqtp;
|
||||
#endif
|
||||
|
||||
++Pci_Interrupt_Count;
|
||||
|
||||
switch (eventParm->hvLpEvent.xSubtype) {
|
||||
case XmPciLpEvent_SlotInterrupt:
|
||||
irq = eventParm->hvLpEvent.xCorrelationToken;
|
||||
/* Dispatch the interrupt handlers for this irq */
|
||||
#ifdef CONFIG_IRQSTACKS
|
||||
/* Switch to the irq stack to handle this */
|
||||
curtp = current_thread_info();
|
||||
irqtp = hardirq_ctx[smp_processor_id()];
|
||||
if (curtp != irqtp) {
|
||||
irqtp->task = curtp->task;
|
||||
irqtp->flags = 0;
|
||||
call___do_IRQ(irq, regsParm, irqtp);
|
||||
irqtp->task = NULL;
|
||||
if (irqtp->flags)
|
||||
set_bits(irqtp->flags, &curtp->flags);
|
||||
} else
|
||||
#endif
|
||||
__do_IRQ(irq, regsParm);
|
||||
HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber,
|
||||
eventParm->eventData.slotInterrupt.subBusNumber,
|
||||
eventParm->eventData.slotInterrupt.deviceId);
|
||||
switch (event->event.xSubtype) {
|
||||
case pe_slot_interrupt:
|
||||
irq = event->event.xCorrelationToken;
|
||||
if (irq < NR_IRQS) {
|
||||
spin_lock(&pending_irqs_lock);
|
||||
pending_irqs[irq]++;
|
||||
num_pending_irqs++;
|
||||
spin_unlock(&pending_irqs_lock);
|
||||
} else {
|
||||
printk(KERN_WARNING "int_received: bad irq number %d\n",
|
||||
irq);
|
||||
HvCallPci_eoi(event->data.slot.bus_number,
|
||||
event->data.slot.sub_bus_number,
|
||||
event->data.slot.dev_id);
|
||||
}
|
||||
break;
|
||||
/* Ignore error recovery events for now */
|
||||
case XmPciLpEvent_BusCreated:
|
||||
printk(KERN_INFO "intReceived: system bus %d created\n",
|
||||
eventParm->eventData.busCreated.busNumber);
|
||||
case pe_bus_created:
|
||||
printk(KERN_INFO "int_received: system bus %d created\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case XmPciLpEvent_BusError:
|
||||
case XmPciLpEvent_BusFailed:
|
||||
printk(KERN_INFO "intReceived: system bus %d failed\n",
|
||||
eventParm->eventData.busFailed.busNumber);
|
||||
case pe_bus_error:
|
||||
case pe_bus_failed:
|
||||
printk(KERN_INFO "int_received: system bus %d failed\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case XmPciLpEvent_BusRecovered:
|
||||
case XmPciLpEvent_UnQuiesceBus:
|
||||
printk(KERN_INFO "intReceived: system bus %d recovered\n",
|
||||
eventParm->eventData.busRecovered.busNumber);
|
||||
case pe_bus_recovered:
|
||||
case pe_unquiese_bus:
|
||||
printk(KERN_INFO "int_received: system bus %d recovered\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case XmPciLpEvent_NodeFailed:
|
||||
case XmPciLpEvent_BridgeError:
|
||||
case pe_node_failed:
|
||||
case pe_bridge_error:
|
||||
printk(KERN_INFO
|
||||
"intReceived: multi-adapter bridge %d/%d/%d failed\n",
|
||||
eventParm->eventData.nodeFailed.busNumber,
|
||||
eventParm->eventData.nodeFailed.subBusNumber,
|
||||
eventParm->eventData.nodeFailed.deviceId);
|
||||
"int_received: multi-adapter bridge %d/%d/%d failed\n",
|
||||
event->data.node.bus_number,
|
||||
event->data.node.sub_bus_number,
|
||||
event->data.node.dev_id);
|
||||
break;
|
||||
case XmPciLpEvent_NodeRecovered:
|
||||
case pe_node_recovered:
|
||||
printk(KERN_INFO
|
||||
"intReceived: multi-adapter bridge %d/%d/%d recovered\n",
|
||||
eventParm->eventData.nodeRecovered.busNumber,
|
||||
eventParm->eventData.nodeRecovered.subBusNumber,
|
||||
eventParm->eventData.nodeRecovered.deviceId);
|
||||
"int_received: multi-adapter bridge %d/%d/%d recovered\n",
|
||||
event->data.node.bus_number,
|
||||
event->data.node.sub_bus_number,
|
||||
event->data.node.dev_id);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"intReceived: unrecognized event subtype 0x%x\n",
|
||||
eventParm->hvLpEvent.xSubtype);
|
||||
"int_received: unrecognized event subtype 0x%x\n",
|
||||
event->event.xSubtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void XmPciLpEvent_handler(struct HvLpEvent *eventParm,
|
||||
struct pt_regs *regsParm)
|
||||
static void pci_event_handler(struct HvLpEvent *event, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_PCI
|
||||
++Pci_Event_Count;
|
||||
|
||||
if (eventParm && (eventParm->xType == HvLpEvent_Type_PciIo)) {
|
||||
switch (eventParm->xFlags.xFunction) {
|
||||
if (event && (event->xType == HvLpEvent_Type_PciIo)) {
|
||||
switch (event->xFlags.xFunction) {
|
||||
case HvLpEvent_Function_Int:
|
||||
intReceived((struct XmPciLpEvent *)eventParm, regsParm);
|
||||
int_received((struct pci_event *)event, regs);
|
||||
break;
|
||||
case HvLpEvent_Function_Ack:
|
||||
printk(KERN_ERR
|
||||
"XmPciLpEvent_handler: unexpected ack received\n");
|
||||
"pci_event_handler: unexpected ack received\n");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"XmPciLpEvent_handler: unexpected event function %d\n",
|
||||
(int)eventParm->xFlags.xFunction);
|
||||
"pci_event_handler: unexpected event function %d\n",
|
||||
(int)event->xFlags.xFunction);
|
||||
break;
|
||||
}
|
||||
} else if (eventParm)
|
||||
} else if (event)
|
||||
printk(KERN_ERR
|
||||
"XmPciLpEvent_handler: Unrecognized PCI event type 0x%x\n",
|
||||
(int)eventParm->xType);
|
||||
"pci_event_handler: Unrecognized PCI event type 0x%x\n",
|
||||
(int)event->xType);
|
||||
else
|
||||
printk(KERN_ERR "XmPciLpEvent_handler: NULL event received\n");
|
||||
#endif
|
||||
printk(KERN_ERR "pci_event_handler: NULL event received\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -199,20 +176,21 @@ static void XmPciLpEvent_handler(struct HvLpEvent *eventParm,
|
||||
void __init iSeries_init_IRQ(void)
|
||||
{
|
||||
/* Register PCI event handler and open an event path */
|
||||
int xRc;
|
||||
int ret;
|
||||
|
||||
xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
|
||||
&XmPciLpEvent_handler);
|
||||
if (xRc == 0) {
|
||||
xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
|
||||
if (xRc != 0)
|
||||
printk(KERN_ERR "iSeries_init_IRQ: open event path "
|
||||
"failed with rc 0x%x\n", xRc);
|
||||
ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
|
||||
&pci_event_handler);
|
||||
if (ret == 0) {
|
||||
ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
|
||||
if (ret != 0)
|
||||
printk(KERN_ERR "iseries_init_IRQ: open event path "
|
||||
"failed with rc 0x%x\n", ret);
|
||||
} else
|
||||
printk(KERN_ERR "iSeries_init_IRQ: register handler "
|
||||
"failed with rc 0x%x\n", xRc);
|
||||
printk(KERN_ERR "iseries_init_IRQ: register handler "
|
||||
"failed with rc 0x%x\n", ret);
|
||||
}
|
||||
|
||||
#define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff)
|
||||
#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1)
|
||||
#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1)
|
||||
#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7)
|
||||
@ -221,40 +199,40 @@ void __init iSeries_init_IRQ(void)
|
||||
* This will be called by device drivers (via enable_IRQ)
|
||||
* to enable INTA in the bridge interrupt status register.
|
||||
*/
|
||||
static void iSeries_enable_IRQ(unsigned int irq)
|
||||
static void iseries_enable_IRQ(unsigned int irq)
|
||||
{
|
||||
u32 bus, deviceId, function, mask;
|
||||
const u32 subBus = 0;
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = virt_irq_to_real_map[irq];
|
||||
|
||||
/* The IRQ has already been locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Unmask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
|
||||
HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
/* This is called by iSeries_activate_IRQs */
|
||||
static unsigned int iSeries_startup_IRQ(unsigned int irq)
|
||||
/* This is called by iseries_activate_IRQs */
|
||||
static unsigned int iseries_startup_IRQ(unsigned int irq)
|
||||
{
|
||||
u32 bus, deviceId, function, mask;
|
||||
const u32 subBus = 0;
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = virt_irq_to_real_map[irq];
|
||||
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Link the IRQ number to the bridge */
|
||||
HvCallXm_connectBusUnit(bus, subBus, deviceId, irq);
|
||||
HvCallXm_connectBusUnit(bus, sub_bus, dev_id, irq);
|
||||
|
||||
/* Unmask bridge interrupts in the FISR */
|
||||
mask = 0x01010000 << function;
|
||||
HvCallPci_unmaskFisr(bus, subBus, deviceId, mask);
|
||||
iSeries_enable_IRQ(irq);
|
||||
HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask);
|
||||
iseries_enable_IRQ(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -279,78 +257,115 @@ void __init iSeries_activate_IRQs()
|
||||
}
|
||||
|
||||
/* this is not called anywhere currently */
|
||||
static void iSeries_shutdown_IRQ(unsigned int irq)
|
||||
static void iseries_shutdown_IRQ(unsigned int irq)
|
||||
{
|
||||
u32 bus, deviceId, function, mask;
|
||||
const u32 subBus = 0;
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = virt_irq_to_real_map[irq];
|
||||
|
||||
/* irq should be locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Invalidate the IRQ number in the bridge */
|
||||
HvCallXm_connectBusUnit(bus, subBus, deviceId, 0);
|
||||
HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0);
|
||||
|
||||
/* Mask bridge interrupts in the FISR */
|
||||
mask = 0x01010000 << function;
|
||||
HvCallPci_maskFisr(bus, subBus, deviceId, mask);
|
||||
HvCallPci_maskFisr(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* This will be called by device drivers (via disable_IRQ)
|
||||
* to disable INTA in the bridge interrupt status register.
|
||||
*/
|
||||
static void iSeries_disable_IRQ(unsigned int irq)
|
||||
static void iseries_disable_IRQ(unsigned int irq)
|
||||
{
|
||||
u32 bus, deviceId, function, mask;
|
||||
const u32 subBus = 0;
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = virt_irq_to_real_map[irq];
|
||||
|
||||
/* The IRQ has already been locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
deviceId = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Mask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
|
||||
HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* This does nothing because there is not enough information
|
||||
* provided to do the EOI HvCall. This is done by XmPciLpEvent.c
|
||||
*/
|
||||
static void iSeries_end_IRQ(unsigned int irq)
|
||||
static void iseries_end_IRQ(unsigned int irq)
|
||||
{
|
||||
unsigned int rirq = virt_irq_to_real_map[irq];
|
||||
|
||||
HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq),
|
||||
(REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq));
|
||||
}
|
||||
|
||||
static hw_irq_controller iSeries_IRQ_handler = {
|
||||
.typename = "iSeries irq controller",
|
||||
.startup = iSeries_startup_IRQ,
|
||||
.shutdown = iSeries_shutdown_IRQ,
|
||||
.enable = iSeries_enable_IRQ,
|
||||
.disable = iSeries_disable_IRQ,
|
||||
.end = iSeries_end_IRQ
|
||||
.startup = iseries_startup_IRQ,
|
||||
.shutdown = iseries_shutdown_IRQ,
|
||||
.enable = iseries_enable_IRQ,
|
||||
.disable = iseries_disable_IRQ,
|
||||
.end = iseries_end_IRQ
|
||||
};
|
||||
|
||||
/*
|
||||
* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot
|
||||
* It calculates the irq value for the slot.
|
||||
* Note that subBusNumber is always 0 (at the moment at least).
|
||||
* Note that sub_bus is always 0 (at the moment at least).
|
||||
*/
|
||||
int __init iSeries_allocate_IRQ(HvBusNumber busNumber,
|
||||
HvSubBusNumber subBusNumber, HvAgentId deviceId)
|
||||
int __init iSeries_allocate_IRQ(HvBusNumber bus,
|
||||
HvSubBusNumber sub_bus, HvAgentId dev_id)
|
||||
{
|
||||
int virtirq;
|
||||
unsigned int realirq;
|
||||
u8 idsel = (deviceId >> 4);
|
||||
u8 function = deviceId & 7;
|
||||
u8 idsel = (dev_id >> 4);
|
||||
u8 function = dev_id & 7;
|
||||
|
||||
realirq = ((busNumber - 1) << 6) + ((idsel - 1) << 3) + function;
|
||||
realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3)
|
||||
+ function;
|
||||
virtirq = virt_irq_create_mapping(realirq);
|
||||
|
||||
irq_desc[virtirq].handler = &iSeries_IRQ_handler;
|
||||
return virtirq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next pending IRQ.
|
||||
*/
|
||||
int iSeries_get_irq(struct pt_regs *regs)
|
||||
{
|
||||
struct paca_struct *lpaca;
|
||||
/* -2 means ignore this interrupt */
|
||||
int irq = -2;
|
||||
|
||||
lpaca = get_paca();
|
||||
#ifdef CONFIG_SMP
|
||||
if (lpaca->lppaca.int_dword.fields.ipi_cnt) {
|
||||
lpaca->lppaca.int_dword.fields.ipi_cnt = 0;
|
||||
iSeries_smp_message_recv(regs);
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
if (hvlpevent_is_pending())
|
||||
process_hvlpevents(regs);
|
||||
|
||||
if (num_pending_irqs) {
|
||||
spin_lock(&pending_irqs_lock);
|
||||
for (irq = 0; irq < NR_IRQS; irq++) {
|
||||
if (pending_irqs[irq]) {
|
||||
pending_irqs[irq]--;
|
||||
num_pending_irqs--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pending_irqs_lock);
|
||||
if (irq >= NR_IRQS)
|
||||
irq = -2;
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
@ -4,5 +4,6 @@
|
||||
extern void iSeries_init_IRQ(void);
|
||||
extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId);
|
||||
extern void iSeries_activate_IRQs(void);
|
||||
extern int iSeries_get_irq(struct pt_regs *);
|
||||
|
||||
#endif /* _ISERIES_IRQ_H */
|
||||
|
@ -225,3 +225,10 @@ struct ItVpdAreas itVpdAreas = {
|
||||
0,0
|
||||
}
|
||||
};
|
||||
|
||||
struct ItLpRegSave iseries_reg_save[] = {
|
||||
[0 ... (NR_CPUS-1)] = {
|
||||
.xDesc = 0xd397d9e2, /* "LpRS" */
|
||||
.xSize = sizeof(struct ItLpRegSave),
|
||||
},
|
||||
};
|
||||
|
@ -569,16 +569,6 @@ static void iSeries_show_cpuinfo(struct seq_file *m)
|
||||
seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Document me.
|
||||
* and Implement me.
|
||||
*/
|
||||
static int iSeries_get_irq(struct pt_regs *regs)
|
||||
{
|
||||
/* -2 means ignore this interrupt */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Document me.
|
||||
*/
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/bitops.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/machdep.h>
|
||||
@ -191,24 +192,10 @@ static void __init maple_init_early(void)
|
||||
*/
|
||||
hpte_init_native();
|
||||
|
||||
/* Find the serial port */
|
||||
generic_find_legacy_serial_ports(&physport, &default_speed);
|
||||
|
||||
DBG("phys port addr: %lx\n", (long)physport);
|
||||
|
||||
if (physport) {
|
||||
void *comport;
|
||||
/* Map the uart for udbg. */
|
||||
comport = (void *)ioremap(physport, 16);
|
||||
udbg_init_uart(comport, default_speed);
|
||||
|
||||
DBG("Hello World !\n");
|
||||
}
|
||||
|
||||
/* Setup interrupt mapping options */
|
||||
ppc64_interrupt_controller = IC_OPEN_PIC;
|
||||
|
||||
iommu_init_early_u3();
|
||||
iommu_init_early_dart();
|
||||
|
||||
DBG(" <- maple_init_early\n");
|
||||
}
|
||||
@ -270,7 +257,7 @@ static int __init maple_probe(int platform)
|
||||
* occupies having to be broken up so the DART itself is not
|
||||
* part of the cacheable linar mapping
|
||||
*/
|
||||
alloc_u3_dart_table();
|
||||
alloc_dart_table();
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -292,4 +279,9 @@ struct machdep_calls __initdata maple_md = {
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.progress = maple_progress,
|
||||
.idle_loop = native_idle,
|
||||
#ifdef CONFIG_KEXEC
|
||||
.machine_kexec = default_machine_kexec,
|
||||
.machine_kexec_prepare = default_machine_kexec_prepare,
|
||||
.machine_crash_shutdown = default_machine_crash_shutdown,
|
||||
#endif
|
||||
};
|
||||
|
@ -1,9 +1,14 @@
|
||||
CFLAGS_bootx_init.o += -fPIC
|
||||
|
||||
obj-y += pic.o setup.o time.o feature.o pci.o \
|
||||
sleep.o low_i2c.o cache.o
|
||||
sleep.o low_i2c.o cache.o pfunc_core.o \
|
||||
pfunc_base.o
|
||||
obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o
|
||||
obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o
|
||||
obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o
|
||||
obj-$(CONFIG_NVRAM) += nvram.o
|
||||
# ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
|
||||
obj-$(CONFIG_PPC64) += nvram.o
|
||||
obj-$(CONFIG_PPC32) += bootx_init.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_PPC_MERGE) += udbg_scc.o udbg_adb.o
|
||||
|
547
arch/powerpc/platforms/powermac/bootx_init.c
Normal file
547
arch/powerpc/platforms/powermac/bootx_init.c
Normal file
@ -0,0 +1,547 @@
|
||||
/*
|
||||
* Early boot support code for BootX bootloader
|
||||
*
|
||||
* Copyright (C) 2005 Ben. 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.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/version.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/bootx.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/btext.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#undef DEBUG
|
||||
#define SET_BOOT_BAT
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) do { bootx_printf(fmt); } while(0)
|
||||
#else
|
||||
#define DBG(fmt...) do { } while(0)
|
||||
#endif
|
||||
|
||||
extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);
|
||||
|
||||
static unsigned long __initdata bootx_dt_strbase;
|
||||
static unsigned long __initdata bootx_dt_strend;
|
||||
static unsigned long __initdata bootx_node_chosen;
|
||||
static boot_infos_t * __initdata bootx_info;
|
||||
static char __initdata bootx_disp_path[256];
|
||||
|
||||
/* Is boot-info compatible ? */
|
||||
#define BOOT_INFO_IS_COMPATIBLE(bi) \
|
||||
((bi)->compatible_version <= BOOT_INFO_VERSION)
|
||||
#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2)
|
||||
#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4)
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
static void __init bootx_printf(const char *format, ...)
|
||||
{
|
||||
const char *p, *q, *s;
|
||||
va_list args;
|
||||
unsigned long v;
|
||||
|
||||
va_start(args, format);
|
||||
for (p = format; *p != 0; p = q) {
|
||||
for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
|
||||
;
|
||||
if (q > p)
|
||||
btext_drawtext(p, q - p);
|
||||
if (*q == 0)
|
||||
break;
|
||||
if (*q == '\n') {
|
||||
++q;
|
||||
btext_flushline();
|
||||
btext_drawstring("\r\n");
|
||||
btext_flushline();
|
||||
continue;
|
||||
}
|
||||
++q;
|
||||
if (*q == 0)
|
||||
break;
|
||||
switch (*q) {
|
||||
case 's':
|
||||
++q;
|
||||
s = va_arg(args, const char *);
|
||||
if (s == NULL)
|
||||
s = "<NULL>";
|
||||
btext_drawstring(s);
|
||||
break;
|
||||
case 'x':
|
||||
++q;
|
||||
v = va_arg(args, unsigned long);
|
||||
btext_drawhex(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* CONFIG_BOOTX_TEXT */
|
||||
static void __init bootx_printf(const char *format, ...) {}
|
||||
#endif /* CONFIG_BOOTX_TEXT */
|
||||
|
||||
static void * __init bootx_early_getprop(unsigned long base,
|
||||
unsigned long node,
|
||||
char *prop)
|
||||
{
|
||||
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
|
||||
u32 *ppp = &np->properties;
|
||||
|
||||
while(*ppp) {
|
||||
struct bootx_dt_prop *pp =
|
||||
(struct bootx_dt_prop *)(base + *ppp);
|
||||
|
||||
if (strcmp((char *)((unsigned long)pp->name + base),
|
||||
prop) == 0) {
|
||||
return (void *)((unsigned long)pp->value + base);
|
||||
}
|
||||
ppp = &pp->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define dt_push_token(token, mem) \
|
||||
do { \
|
||||
*(mem) = _ALIGN_UP(*(mem),4); \
|
||||
*((u32 *)*(mem)) = token; \
|
||||
*(mem) += 4; \
|
||||
} while(0)
|
||||
|
||||
static unsigned long __init bootx_dt_find_string(char *str)
|
||||
{
|
||||
char *s, *os;
|
||||
|
||||
s = os = (char *)bootx_dt_strbase;
|
||||
s += 4;
|
||||
while (s < (char *)bootx_dt_strend) {
|
||||
if (strcmp(s, str) == 0)
|
||||
return s - os;
|
||||
s += strlen(s) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init bootx_dt_add_prop(char *name, void *data, int size,
|
||||
unsigned long *mem_end)
|
||||
{
|
||||
unsigned long soff = bootx_dt_find_string(name);
|
||||
if (data == NULL)
|
||||
size = 0;
|
||||
if (soff == 0) {
|
||||
bootx_printf("WARNING: Can't find string index for <%s>\n",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
if (size > 0x20000) {
|
||||
bootx_printf("WARNING: ignoring large property ");
|
||||
bootx_printf("%s length 0x%x\n", name, size);
|
||||
return;
|
||||
}
|
||||
dt_push_token(OF_DT_PROP, mem_end);
|
||||
dt_push_token(size, mem_end);
|
||||
dt_push_token(soff, mem_end);
|
||||
|
||||
/* push property content */
|
||||
if (size && data) {
|
||||
memcpy((void *)*mem_end, data, size);
|
||||
*mem_end = _ALIGN_UP(*mem_end + size, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init bootx_add_chosen_props(unsigned long base,
|
||||
unsigned long *mem_end)
|
||||
{
|
||||
u32 val = _MACH_Pmac;
|
||||
|
||||
bootx_dt_add_prop("linux,platform", &val, 4, mem_end);
|
||||
|
||||
if (bootx_info->kernelParamsOffset) {
|
||||
char *args = (char *)((unsigned long)bootx_info) +
|
||||
bootx_info->kernelParamsOffset;
|
||||
bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end);
|
||||
}
|
||||
if (bootx_info->ramDisk) {
|
||||
val = ((unsigned long)bootx_info) + bootx_info->ramDisk;
|
||||
bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end);
|
||||
val += bootx_info->ramDiskSize;
|
||||
bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end);
|
||||
}
|
||||
if (strlen(bootx_disp_path))
|
||||
bootx_dt_add_prop("linux,stdout-path", bootx_disp_path,
|
||||
strlen(bootx_disp_path) + 1, mem_end);
|
||||
}
|
||||
|
||||
static void __init bootx_add_display_props(unsigned long base,
|
||||
unsigned long *mem_end)
|
||||
{
|
||||
bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end);
|
||||
bootx_dt_add_prop("linux,opened", NULL, 0, mem_end);
|
||||
}
|
||||
|
||||
static void __init bootx_dt_add_string(char *s, unsigned long *mem_end)
|
||||
{
|
||||
unsigned int l = strlen(s) + 1;
|
||||
memcpy((void *)*mem_end, s, l);
|
||||
bootx_dt_strend = *mem_end = *mem_end + l;
|
||||
}
|
||||
|
||||
static void __init bootx_scan_dt_build_strings(unsigned long base,
|
||||
unsigned long node,
|
||||
unsigned long *mem_end)
|
||||
{
|
||||
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
|
||||
u32 *cpp, *ppp = &np->properties;
|
||||
unsigned long soff;
|
||||
char *namep;
|
||||
|
||||
/* Keep refs to known nodes */
|
||||
namep = np->full_name ? (char *)(base + np->full_name) : NULL;
|
||||
if (namep == NULL) {
|
||||
bootx_printf("Node without a full name !\n");
|
||||
namep = "";
|
||||
}
|
||||
DBG("* strings: %s\n", namep);
|
||||
|
||||
if (!strcmp(namep, "/chosen")) {
|
||||
DBG(" detected /chosen ! adding properties names !\n");
|
||||
bootx_dt_add_string("linux,platform", mem_end);
|
||||
bootx_dt_add_string("linux,stdout-path", mem_end);
|
||||
bootx_dt_add_string("linux,initrd-start", mem_end);
|
||||
bootx_dt_add_string("linux,initrd-end", mem_end);
|
||||
bootx_dt_add_string("bootargs", mem_end);
|
||||
bootx_node_chosen = node;
|
||||
}
|
||||
if (node == bootx_info->dispDeviceRegEntryOffset) {
|
||||
DBG(" detected display ! adding properties names !\n");
|
||||
bootx_dt_add_string("linux,boot-display", mem_end);
|
||||
bootx_dt_add_string("linux,opened", mem_end);
|
||||
strncpy(bootx_disp_path, namep, 255);
|
||||
}
|
||||
|
||||
/* get and store all property names */
|
||||
while (*ppp) {
|
||||
struct bootx_dt_prop *pp =
|
||||
(struct bootx_dt_prop *)(base + *ppp);
|
||||
|
||||
namep = pp->name ? (char *)(base + pp->name) : NULL;
|
||||
if (namep == NULL || strcmp(namep, "name") == 0)
|
||||
goto next;
|
||||
/* get/create string entry */
|
||||
soff = bootx_dt_find_string(namep);
|
||||
if (soff == 0)
|
||||
bootx_dt_add_string(namep, mem_end);
|
||||
next:
|
||||
ppp = &pp->next;
|
||||
}
|
||||
|
||||
/* do all our children */
|
||||
cpp = &np->child;
|
||||
while(*cpp) {
|
||||
np = (struct bootx_dt_node *)(base + *cpp);
|
||||
bootx_scan_dt_build_strings(base, *cpp, mem_end);
|
||||
cpp = &np->sibling;
|
||||
}
|
||||
}
|
||||
|
||||
static void __init bootx_scan_dt_build_struct(unsigned long base,
|
||||
unsigned long node,
|
||||
unsigned long *mem_end)
|
||||
{
|
||||
struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
|
||||
u32 *cpp, *ppp = &np->properties;
|
||||
char *namep, *p, *ep, *lp;
|
||||
int l;
|
||||
|
||||
dt_push_token(OF_DT_BEGIN_NODE, mem_end);
|
||||
|
||||
/* get the node's full name */
|
||||
namep = np->full_name ? (char *)(base + np->full_name) : NULL;
|
||||
if (namep == NULL)
|
||||
namep = "";
|
||||
l = strlen(namep);
|
||||
|
||||
DBG("* struct: %s\n", namep);
|
||||
|
||||
/* Fixup an Apple bug where they have bogus \0 chars in the
|
||||
* middle of the path in some properties, and extract
|
||||
* the unit name (everything after the last '/').
|
||||
*/
|
||||
memcpy((void *)*mem_end, namep, l + 1);
|
||||
namep = (char *)*mem_end;
|
||||
for (lp = p = namep, ep = namep + l; p < ep; p++) {
|
||||
if (*p == '/')
|
||||
lp = namep;
|
||||
else if (*p != 0)
|
||||
*lp++ = *p;
|
||||
}
|
||||
*lp = 0;
|
||||
*mem_end = _ALIGN_UP((unsigned long)lp + 1, 4);
|
||||
|
||||
/* get and store all properties */
|
||||
while (*ppp) {
|
||||
struct bootx_dt_prop *pp =
|
||||
(struct bootx_dt_prop *)(base + *ppp);
|
||||
|
||||
namep = pp->name ? (char *)(base + pp->name) : NULL;
|
||||
/* Skip "name" */
|
||||
if (namep == NULL || !strcmp(namep, "name"))
|
||||
goto next;
|
||||
/* Skip "bootargs" in /chosen too as we replace it */
|
||||
if (node == bootx_node_chosen && !strcmp(namep, "bootargs"))
|
||||
goto next;
|
||||
|
||||
/* push property head */
|
||||
bootx_dt_add_prop(namep,
|
||||
pp->value ? (void *)(base + pp->value): NULL,
|
||||
pp->length, mem_end);
|
||||
next:
|
||||
ppp = &pp->next;
|
||||
}
|
||||
|
||||
if (node == bootx_node_chosen)
|
||||
bootx_add_chosen_props(base, mem_end);
|
||||
if (node == bootx_info->dispDeviceRegEntryOffset)
|
||||
bootx_add_display_props(base, mem_end);
|
||||
|
||||
/* do all our children */
|
||||
cpp = &np->child;
|
||||
while(*cpp) {
|
||||
np = (struct bootx_dt_node *)(base + *cpp);
|
||||
bootx_scan_dt_build_struct(base, *cpp, mem_end);
|
||||
cpp = &np->sibling;
|
||||
}
|
||||
|
||||
dt_push_token(OF_DT_END_NODE, mem_end);
|
||||
}
|
||||
|
||||
static unsigned long __init bootx_flatten_dt(unsigned long start)
|
||||
{
|
||||
boot_infos_t *bi = bootx_info;
|
||||
unsigned long mem_start, mem_end;
|
||||
struct boot_param_header *hdr;
|
||||
unsigned long base;
|
||||
u64 *rsvmap;
|
||||
|
||||
/* Start using memory after the big blob passed by BootX, get
|
||||
* some space for the header
|
||||
*/
|
||||
mem_start = mem_end = _ALIGN_UP(((unsigned long)bi) + start, 4);
|
||||
DBG("Boot params header at: %x\n", mem_start);
|
||||
hdr = (struct boot_param_header *)mem_start;
|
||||
mem_end += sizeof(struct boot_param_header);
|
||||
rsvmap = (u64 *)(_ALIGN_UP(mem_end, 8));
|
||||
hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start;
|
||||
mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64);
|
||||
|
||||
/* Get base of tree */
|
||||
base = ((unsigned long)bi) + bi->deviceTreeOffset;
|
||||
|
||||
/* Build string array */
|
||||
DBG("Building string array at: %x\n", mem_end);
|
||||
DBG("Device Tree Base=%x\n", base);
|
||||
bootx_dt_strbase = mem_end;
|
||||
mem_end += 4;
|
||||
bootx_dt_strend = mem_end;
|
||||
bootx_scan_dt_build_strings(base, 4, &mem_end);
|
||||
hdr->off_dt_strings = bootx_dt_strbase - mem_start;
|
||||
hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase;
|
||||
|
||||
/* Build structure */
|
||||
mem_end = _ALIGN(mem_end, 16);
|
||||
DBG("Building device tree structure at: %x\n", mem_end);
|
||||
hdr->off_dt_struct = mem_end - mem_start;
|
||||
bootx_scan_dt_build_struct(base, 4, &mem_end);
|
||||
dt_push_token(OF_DT_END, &mem_end);
|
||||
|
||||
/* Finish header */
|
||||
hdr->boot_cpuid_phys = 0;
|
||||
hdr->magic = OF_DT_HEADER;
|
||||
hdr->totalsize = mem_end - mem_start;
|
||||
hdr->version = OF_DT_VERSION;
|
||||
/* Version 16 is not backward compatible */
|
||||
hdr->last_comp_version = 0x10;
|
||||
|
||||
/* Reserve the whole thing and copy the reserve map in, we
|
||||
* also bump mem_reserve_cnt to cause further reservations to
|
||||
* fail since it's too late.
|
||||
*/
|
||||
mem_end = _ALIGN(mem_end, PAGE_SIZE);
|
||||
DBG("End of boot params: %x\n", mem_end);
|
||||
rsvmap[0] = mem_start;
|
||||
rsvmap[1] = mem_end;
|
||||
rsvmap[2] = 0;
|
||||
rsvmap[3] = 0;
|
||||
|
||||
return (unsigned long)hdr;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
static void __init btext_welcome(boot_infos_t *bi)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long pvr;
|
||||
|
||||
bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n");
|
||||
bootx_printf("\nlinked at : 0x%x", KERNELBASE);
|
||||
bootx_printf("\nframe buffer at : 0x%x", bi->dispDeviceBase);
|
||||
bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase);
|
||||
bootx_printf(" (log)");
|
||||
bootx_printf("\nklimit : 0x%x",(unsigned long)klimit);
|
||||
bootx_printf("\nboot_info at : 0x%x", bi);
|
||||
__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
|
||||
bootx_printf("\nMSR : 0x%x", flags);
|
||||
__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
|
||||
bootx_printf("\nPVR : 0x%x", pvr);
|
||||
pvr >>= 16;
|
||||
if (pvr > 1) {
|
||||
__asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
|
||||
bootx_printf("\nHID0 : 0x%x", flags);
|
||||
}
|
||||
if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
|
||||
__asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
|
||||
bootx_printf("\nICTC : 0x%x", flags);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
bootx_printf("\n\n");
|
||||
bootx_printf("bi->deviceTreeOffset : 0x%x\n",
|
||||
bi->deviceTreeOffset);
|
||||
bootx_printf("bi->deviceTreeSize : 0x%x\n",
|
||||
bi->deviceTreeSize);
|
||||
#endif
|
||||
bootx_printf("\n\n");
|
||||
}
|
||||
#endif /* CONFIG_BOOTX_TEXT */
|
||||
|
||||
void __init bootx_init(unsigned long r3, unsigned long r4)
|
||||
{
|
||||
boot_infos_t *bi = (boot_infos_t *) r4;
|
||||
unsigned long hdr;
|
||||
unsigned long space;
|
||||
unsigned long ptr, x;
|
||||
char *model;
|
||||
unsigned long offset = reloc_offset();
|
||||
|
||||
reloc_got2(offset);
|
||||
|
||||
bootx_info = bi;
|
||||
|
||||
/* We haven't cleared any bss at this point, make sure
|
||||
* what we need is initialized
|
||||
*/
|
||||
bootx_dt_strbase = bootx_dt_strend = 0;
|
||||
bootx_node_chosen = 0;
|
||||
bootx_disp_path[0] = 0;
|
||||
|
||||
if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
|
||||
bi->logicalDisplayBase = bi->dispDeviceBase;
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0],
|
||||
bi->dispDeviceRect[3] - bi->dispDeviceRect[1],
|
||||
bi->dispDeviceDepth, bi->dispDeviceRowBytes,
|
||||
(unsigned long)bi->logicalDisplayBase);
|
||||
btext_clearscreen();
|
||||
btext_flushscreen();
|
||||
#endif /* CONFIG_BOOTX_TEXT */
|
||||
|
||||
/*
|
||||
* Test if boot-info is compatible. Done only in config
|
||||
* CONFIG_BOOTX_TEXT since there is nothing much we can do
|
||||
* with an incompatible version, except display a message
|
||||
* and eventually hang the processor...
|
||||
*
|
||||
* I'll try to keep enough of boot-info compatible in the
|
||||
* future to always allow display of this message;
|
||||
*/
|
||||
if (!BOOT_INFO_IS_COMPATIBLE(bi)) {
|
||||
bootx_printf(" !!! WARNING - Incompatible version"
|
||||
" of BootX !!!\n\n\n");
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
if (bi->architecture != BOOT_ARCH_PCI) {
|
||||
bootx_printf(" !!! WARNING - Usupported machine"
|
||||
" architecture !\n");
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
btext_welcome(bi);
|
||||
#endif
|
||||
/* New BootX enters kernel with MMU off, i/os are not allowed
|
||||
* here. This hack will have been done by the boostrap anyway.
|
||||
*/
|
||||
if (bi->version < 4) {
|
||||
/*
|
||||
* XXX If this is an iMac, turn off the USB controller.
|
||||
*/
|
||||
model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset,
|
||||
4, "model");
|
||||
if (model
|
||||
&& (strcmp(model, "iMac,1") == 0
|
||||
|| strcmp(model, "PowerMac1,1") == 0)) {
|
||||
bootx_printf("iMac,1 detected, shutting down USB \n");
|
||||
out_le32((unsigned *)0x80880008, 1); /* XXX */
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a pointer that points above the device tree, args, ramdisk,
|
||||
* etc... to use for generating the flattened tree
|
||||
*/
|
||||
if (bi->version < 5) {
|
||||
space = bi->deviceTreeOffset + bi->deviceTreeSize;
|
||||
if (bi->ramDisk)
|
||||
space = bi->ramDisk + bi->ramDiskSize;
|
||||
} else
|
||||
space = bi->totalParamsSize;
|
||||
|
||||
bootx_printf("Total space used by parameters & ramdisk: %x \n", space);
|
||||
|
||||
/* New BootX will have flushed all TLBs and enters kernel with
|
||||
* MMU switched OFF, so this should not be useful anymore.
|
||||
*/
|
||||
if (bi->version < 4) {
|
||||
bootx_printf("Touching pages...\n");
|
||||
|
||||
/*
|
||||
* Touch each page to make sure the PTEs for them
|
||||
* are in the hash table - the aim is to try to avoid
|
||||
* getting DSI exceptions while copying the kernel image.
|
||||
*/
|
||||
for (ptr = ((unsigned long) &_stext) & PAGE_MASK;
|
||||
ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
|
||||
x = *(volatile unsigned long *)ptr;
|
||||
}
|
||||
|
||||
/* Ok, now we need to generate a flattened device-tree to pass
|
||||
* to the kernel
|
||||
*/
|
||||
bootx_printf("Preparing boot params...\n");
|
||||
|
||||
hdr = bootx_flatten_dt(space);
|
||||
|
||||
#ifdef CONFIG_BOOTX_TEXT
|
||||
#ifdef SET_BOOT_BAT
|
||||
bootx_printf("Preparing BAT...\n");
|
||||
btext_prepare_BAT();
|
||||
#else
|
||||
btext_unmap();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
reloc_got2(-offset);
|
||||
|
||||
__start(hdr, KERNELBASE + offset, 0);
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/smu.h>
|
||||
#include <asm/pmac_pfunc.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
@ -85,6 +86,10 @@ static u32 *g5_pmode_data;
|
||||
static int g5_pmode_max;
|
||||
static int g5_pmode_cur;
|
||||
|
||||
static void (*g5_switch_volt)(int speed_mode);
|
||||
static int (*g5_switch_freq)(int speed_mode);
|
||||
static int (*g5_query_freq)(void);
|
||||
|
||||
static DECLARE_MUTEX(g5_switch_mutex);
|
||||
|
||||
|
||||
@ -92,9 +97,11 @@ static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */
|
||||
static int g5_fvt_count; /* number of op. points */
|
||||
static int g5_fvt_cur; /* current op. point */
|
||||
|
||||
/* ----------------- real hardware interface */
|
||||
/*
|
||||
* SMU based voltage switching for Neo2 platforms
|
||||
*/
|
||||
|
||||
static void g5_switch_volt(int speed_mode)
|
||||
static void g5_smu_switch_volt(int speed_mode)
|
||||
{
|
||||
struct smu_simple_cmd cmd;
|
||||
|
||||
@ -105,26 +112,57 @@ static void g5_switch_volt(int speed_mode)
|
||||
wait_for_completion(&comp);
|
||||
}
|
||||
|
||||
static int g5_switch_freq(int speed_mode)
|
||||
/*
|
||||
* Platform function based voltage/vdnap switching for Neo2
|
||||
*/
|
||||
|
||||
static struct pmf_function *pfunc_set_vdnap0;
|
||||
static struct pmf_function *pfunc_vdnap0_complete;
|
||||
|
||||
static void g5_vdnap_switch_volt(int speed_mode)
|
||||
{
|
||||
struct cpufreq_freqs freqs;
|
||||
struct pmf_args args;
|
||||
u32 slew, done = 0;
|
||||
unsigned long timeout;
|
||||
|
||||
slew = (speed_mode == CPUFREQ_LOW) ? 1 : 0;
|
||||
args.count = 1;
|
||||
args.u[0].p = &slew;
|
||||
|
||||
pmf_call_one(pfunc_set_vdnap0, &args);
|
||||
|
||||
/* It's an irq GPIO so we should be able to just block here,
|
||||
* I'll do that later after I've properly tested the IRQ code for
|
||||
* platform functions
|
||||
*/
|
||||
timeout = jiffies + HZ/10;
|
||||
while(!time_after(jiffies, timeout)) {
|
||||
args.count = 1;
|
||||
args.u[0].p = &done;
|
||||
pmf_call_one(pfunc_vdnap0_complete, &args);
|
||||
if (done)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
if (done == 0)
|
||||
printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SCOM based frequency switching for 970FX rev3
|
||||
*/
|
||||
static int g5_scom_switch_freq(int speed_mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
int to;
|
||||
|
||||
if (g5_pmode_cur == speed_mode)
|
||||
return 0;
|
||||
|
||||
down(&g5_switch_mutex);
|
||||
|
||||
freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency;
|
||||
freqs.new = g5_cpu_freqs[speed_mode].frequency;
|
||||
freqs.cpu = 0;
|
||||
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
|
||||
/* If frequency is going up, first ramp up the voltage */
|
||||
if (speed_mode < g5_pmode_cur)
|
||||
g5_switch_volt(speed_mode);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/* Clear PCR high */
|
||||
scom970_write(SCOM_PCR, 0);
|
||||
/* Clear PCR low */
|
||||
@ -147,6 +185,8 @@ static int g5_switch_freq(int speed_mode)
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* If frequency is going down, last ramp the voltage */
|
||||
if (speed_mode > g5_pmode_cur)
|
||||
g5_switch_volt(speed_mode);
|
||||
@ -154,14 +194,10 @@ static int g5_switch_freq(int speed_mode)
|
||||
g5_pmode_cur = speed_mode;
|
||||
ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
|
||||
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
up(&g5_switch_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int g5_query_freq(void)
|
||||
static int g5_scom_query_freq(void)
|
||||
{
|
||||
unsigned long psr = scom970_read(SCOM_PSR);
|
||||
int i;
|
||||
@ -173,7 +209,104 @@ static int g5_query_freq(void)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* ----------------- cpufreq bookkeeping */
|
||||
/*
|
||||
* Platform function based voltage switching for PowerMac7,2 & 7,3
|
||||
*/
|
||||
|
||||
static struct pmf_function *pfunc_cpu0_volt_high;
|
||||
static struct pmf_function *pfunc_cpu0_volt_low;
|
||||
static struct pmf_function *pfunc_cpu1_volt_high;
|
||||
static struct pmf_function *pfunc_cpu1_volt_low;
|
||||
|
||||
static void g5_pfunc_switch_volt(int speed_mode)
|
||||
{
|
||||
if (speed_mode == CPUFREQ_HIGH) {
|
||||
if (pfunc_cpu0_volt_high)
|
||||
pmf_call_one(pfunc_cpu0_volt_high, NULL);
|
||||
if (pfunc_cpu1_volt_high)
|
||||
pmf_call_one(pfunc_cpu1_volt_high, NULL);
|
||||
} else {
|
||||
if (pfunc_cpu0_volt_low)
|
||||
pmf_call_one(pfunc_cpu0_volt_low, NULL);
|
||||
if (pfunc_cpu1_volt_low)
|
||||
pmf_call_one(pfunc_cpu1_volt_low, NULL);
|
||||
}
|
||||
msleep(10); /* should be faster , to fix */
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform function based frequency switching for PowerMac7,2 & 7,3
|
||||
*/
|
||||
|
||||
static struct pmf_function *pfunc_cpu_setfreq_high;
|
||||
static struct pmf_function *pfunc_cpu_setfreq_low;
|
||||
static struct pmf_function *pfunc_cpu_getfreq;
|
||||
static struct pmf_function *pfunc_slewing_done;;
|
||||
|
||||
static int g5_pfunc_switch_freq(int speed_mode)
|
||||
{
|
||||
struct pmf_args args;
|
||||
u32 done = 0;
|
||||
unsigned long timeout;
|
||||
|
||||
/* If frequency is going up, first ramp up the voltage */
|
||||
if (speed_mode < g5_pmode_cur)
|
||||
g5_switch_volt(speed_mode);
|
||||
|
||||
/* Do it */
|
||||
if (speed_mode == CPUFREQ_HIGH)
|
||||
pmf_call_one(pfunc_cpu_setfreq_high, NULL);
|
||||
else
|
||||
pmf_call_one(pfunc_cpu_setfreq_low, NULL);
|
||||
|
||||
/* It's an irq GPIO so we should be able to just block here,
|
||||
* I'll do that later after I've properly tested the IRQ code for
|
||||
* platform functions
|
||||
*/
|
||||
timeout = jiffies + HZ/10;
|
||||
while(!time_after(jiffies, timeout)) {
|
||||
args.count = 1;
|
||||
args.u[0].p = &done;
|
||||
pmf_call_one(pfunc_slewing_done, &args);
|
||||
if (done)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
if (done == 0)
|
||||
printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n");
|
||||
|
||||
/* If frequency is going down, last ramp the voltage */
|
||||
if (speed_mode > g5_pmode_cur)
|
||||
g5_switch_volt(speed_mode);
|
||||
|
||||
g5_pmode_cur = speed_mode;
|
||||
ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int g5_pfunc_query_freq(void)
|
||||
{
|
||||
struct pmf_args args;
|
||||
u32 val = 0;
|
||||
|
||||
args.count = 1;
|
||||
args.u[0].p = &val;
|
||||
pmf_call_one(pfunc_cpu_getfreq, &args);
|
||||
return val ? CPUFREQ_HIGH : CPUFREQ_LOW;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fake voltage switching for platforms with missing support
|
||||
*/
|
||||
|
||||
static void g5_dummy_switch_volt(int speed_mode)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Common interface to the cpufreq core
|
||||
*/
|
||||
|
||||
static int g5_cpufreq_verify(struct cpufreq_policy *policy)
|
||||
{
|
||||
@ -183,13 +316,30 @@ static int g5_cpufreq_verify(struct cpufreq_policy *policy)
|
||||
static int g5_cpufreq_target(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq, unsigned int relation)
|
||||
{
|
||||
unsigned int newstate = 0;
|
||||
unsigned int newstate = 0;
|
||||
struct cpufreq_freqs freqs;
|
||||
int rc;
|
||||
|
||||
if (cpufreq_frequency_table_target(policy, g5_cpu_freqs,
|
||||
target_freq, relation, &newstate))
|
||||
return -EINVAL;
|
||||
|
||||
return g5_switch_freq(newstate);
|
||||
if (g5_pmode_cur == newstate)
|
||||
return 0;
|
||||
|
||||
down(&g5_switch_mutex);
|
||||
|
||||
freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency;
|
||||
freqs.new = g5_cpu_freqs[newstate].frequency;
|
||||
freqs.cpu = 0;
|
||||
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
rc = g5_switch_freq(newstate);
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
up(&g5_switch_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static unsigned int g5_cpufreq_get_speed(unsigned int cpu)
|
||||
@ -205,6 +355,7 @@ static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
||||
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
|
||||
policy->cur = g5_cpu_freqs[g5_query_freq()].frequency;
|
||||
policy->cpus = cpu_possible_map;
|
||||
cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu);
|
||||
|
||||
return cpufreq_frequency_table_cpuinfo(policy,
|
||||
@ -224,19 +375,39 @@ static struct cpufreq_driver g5_cpufreq_driver = {
|
||||
};
|
||||
|
||||
|
||||
static int __init g5_cpufreq_init(void)
|
||||
static int __init g5_neo2_cpufreq_init(struct device_node *cpus)
|
||||
{
|
||||
struct device_node *cpunode;
|
||||
unsigned int psize, ssize;
|
||||
struct smu_sdbp_header *shdr;
|
||||
unsigned long max_freq;
|
||||
u32 *valp;
|
||||
char *freq_method, *volt_method;
|
||||
u32 *valp, pvr_hi;
|
||||
int use_volts_vdnap = 0;
|
||||
int use_volts_smu = 0;
|
||||
int rc = -ENODEV;
|
||||
|
||||
/* Look for CPU and SMU nodes */
|
||||
cpunode = of_find_node_by_type(NULL, "cpu");
|
||||
if (!cpunode) {
|
||||
DBG("No CPU node !\n");
|
||||
/* Check supported platforms */
|
||||
if (machine_is_compatible("PowerMac8,1") ||
|
||||
machine_is_compatible("PowerMac8,2") ||
|
||||
machine_is_compatible("PowerMac9,1"))
|
||||
use_volts_smu = 1;
|
||||
else if (machine_is_compatible("PowerMac11,2"))
|
||||
use_volts_vdnap = 1;
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
/* Get first CPU node */
|
||||
for (cpunode = NULL;
|
||||
(cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {
|
||||
u32 *reg =
|
||||
(u32 *)get_property(cpunode, "reg", NULL);
|
||||
if (reg == NULL || (*reg) != 0)
|
||||
continue;
|
||||
if (!strcmp(cpunode->type, "cpu"))
|
||||
break;
|
||||
}
|
||||
if (cpunode == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -246,8 +417,9 @@ static int __init g5_cpufreq_init(void)
|
||||
DBG("No cpu-version property !\n");
|
||||
goto bail_noprops;
|
||||
}
|
||||
if (((*valp) >> 16) != 0x3c) {
|
||||
DBG("Wrong CPU version: %08x\n", *valp);
|
||||
pvr_hi = (*valp) >> 16;
|
||||
if (pvr_hi != 0x3c && pvr_hi != 0x44) {
|
||||
printk(KERN_ERR "cpufreq: Unsupported CPU version\n");
|
||||
goto bail_noprops;
|
||||
}
|
||||
|
||||
@ -259,18 +431,50 @@ static int __init g5_cpufreq_init(void)
|
||||
}
|
||||
g5_pmode_max = psize / sizeof(u32) - 1;
|
||||
|
||||
/* Look for the FVT table */
|
||||
shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
|
||||
if (!shdr)
|
||||
goto bail_noprops;
|
||||
g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];
|
||||
ssize = (shdr->len * sizeof(u32)) - sizeof(struct smu_sdbp_header);
|
||||
g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt);
|
||||
g5_fvt_cur = 0;
|
||||
if (use_volts_smu) {
|
||||
struct smu_sdbp_header *shdr;
|
||||
|
||||
/* Sanity checking */
|
||||
if (g5_fvt_count < 1 || g5_pmode_max < 1)
|
||||
goto bail_noprops;
|
||||
/* Look for the FVT table */
|
||||
shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
|
||||
if (!shdr)
|
||||
goto bail_noprops;
|
||||
g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];
|
||||
ssize = (shdr->len * sizeof(u32)) -
|
||||
sizeof(struct smu_sdbp_header);
|
||||
g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt);
|
||||
g5_fvt_cur = 0;
|
||||
|
||||
/* Sanity checking */
|
||||
if (g5_fvt_count < 1 || g5_pmode_max < 1)
|
||||
goto bail_noprops;
|
||||
|
||||
g5_switch_volt = g5_smu_switch_volt;
|
||||
volt_method = "SMU";
|
||||
} else if (use_volts_vdnap) {
|
||||
struct device_node *root;
|
||||
|
||||
root = of_find_node_by_path("/");
|
||||
if (root == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find root of "
|
||||
"device tree\n");
|
||||
goto bail_noprops;
|
||||
}
|
||||
pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0");
|
||||
pfunc_vdnap0_complete =
|
||||
pmf_find_function(root, "slewing-done");
|
||||
if (pfunc_set_vdnap0 == NULL ||
|
||||
pfunc_vdnap0_complete == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find required "
|
||||
"platform function\n");
|
||||
goto bail_noprops;
|
||||
}
|
||||
|
||||
g5_switch_volt = g5_vdnap_switch_volt;
|
||||
volt_method = "GPIO";
|
||||
} else {
|
||||
g5_switch_volt = g5_dummy_switch_volt;
|
||||
volt_method = "none";
|
||||
}
|
||||
|
||||
/*
|
||||
* From what I see, clock-frequency is always the maximal frequency.
|
||||
@ -286,19 +490,23 @@ static int __init g5_cpufreq_init(void)
|
||||
g5_cpu_freqs[0].frequency = max_freq;
|
||||
g5_cpu_freqs[1].frequency = max_freq/2;
|
||||
|
||||
/* Check current frequency */
|
||||
g5_pmode_cur = g5_query_freq();
|
||||
if (g5_pmode_cur > 1)
|
||||
/* We don't support anything but 1:1 and 1:2, fixup ... */
|
||||
g5_pmode_cur = 1;
|
||||
/* Set callbacks */
|
||||
g5_switch_freq = g5_scom_switch_freq;
|
||||
g5_query_freq = g5_scom_query_freq;
|
||||
freq_method = "SCOM";
|
||||
|
||||
/* Force apply current frequency to make sure everything is in
|
||||
* sync (voltage is right for example). Firmware may leave us with
|
||||
* a strange setting ...
|
||||
*/
|
||||
g5_switch_freq(g5_pmode_cur);
|
||||
g5_switch_volt(CPUFREQ_HIGH);
|
||||
msleep(10);
|
||||
g5_pmode_cur = -1;
|
||||
g5_switch_freq(g5_query_freq());
|
||||
|
||||
printk(KERN_INFO "Registering G5 CPU frequency driver\n");
|
||||
printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n",
|
||||
freq_method, volt_method);
|
||||
printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",
|
||||
g5_cpu_freqs[1].frequency/1000,
|
||||
g5_cpu_freqs[0].frequency/1000,
|
||||
@ -317,6 +525,200 @@ static int __init g5_cpufreq_init(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init g5_pm72_cpufreq_init(struct device_node *cpus)
|
||||
{
|
||||
struct device_node *cpuid = NULL, *hwclock = NULL, *cpunode = NULL;
|
||||
u8 *eeprom = NULL;
|
||||
u32 *valp;
|
||||
u64 max_freq, min_freq, ih, il;
|
||||
int has_volt = 1, rc = 0;
|
||||
|
||||
/* Get first CPU node */
|
||||
for (cpunode = NULL;
|
||||
(cpunode = of_get_next_child(cpus, cpunode)) != NULL;) {
|
||||
if (!strcmp(cpunode->type, "cpu"))
|
||||
break;
|
||||
}
|
||||
if (cpunode == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find any CPU node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Lookup the cpuid eeprom node */
|
||||
cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0");
|
||||
if (cpuid != NULL)
|
||||
eeprom = (u8 *)get_property(cpuid, "cpuid", NULL);
|
||||
if (eeprom == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n");
|
||||
rc = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Lookup the i2c hwclock */
|
||||
for (hwclock = NULL;
|
||||
(hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){
|
||||
char *loc = get_property(hwclock, "hwctrl-location", NULL);
|
||||
if (loc == NULL)
|
||||
continue;
|
||||
if (strcmp(loc, "CPU CLOCK"))
|
||||
continue;
|
||||
if (!get_property(hwclock, "platform-get-frequency", NULL))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (hwclock == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n");
|
||||
rc = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name);
|
||||
|
||||
/* Now get all the platform functions */
|
||||
pfunc_cpu_getfreq =
|
||||
pmf_find_function(hwclock, "get-frequency");
|
||||
pfunc_cpu_setfreq_high =
|
||||
pmf_find_function(hwclock, "set-frequency-high");
|
||||
pfunc_cpu_setfreq_low =
|
||||
pmf_find_function(hwclock, "set-frequency-low");
|
||||
pfunc_slewing_done =
|
||||
pmf_find_function(hwclock, "slewing-done");
|
||||
pfunc_cpu0_volt_high =
|
||||
pmf_find_function(hwclock, "set-voltage-high-0");
|
||||
pfunc_cpu0_volt_low =
|
||||
pmf_find_function(hwclock, "set-voltage-low-0");
|
||||
pfunc_cpu1_volt_high =
|
||||
pmf_find_function(hwclock, "set-voltage-high-1");
|
||||
pfunc_cpu1_volt_low =
|
||||
pmf_find_function(hwclock, "set-voltage-low-1");
|
||||
|
||||
/* Check we have minimum requirements */
|
||||
if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL ||
|
||||
pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) {
|
||||
printk(KERN_ERR "cpufreq: Can't find platform functions !\n");
|
||||
rc = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Check that we have complete sets */
|
||||
if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) {
|
||||
pmf_put_function(pfunc_cpu0_volt_high);
|
||||
pmf_put_function(pfunc_cpu0_volt_low);
|
||||
pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL;
|
||||
has_volt = 0;
|
||||
}
|
||||
if (!has_volt ||
|
||||
pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) {
|
||||
pmf_put_function(pfunc_cpu1_volt_high);
|
||||
pmf_put_function(pfunc_cpu1_volt_low);
|
||||
pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL;
|
||||
}
|
||||
|
||||
/* Note: The device tree also contains a "platform-set-values"
|
||||
* function for which I haven't quite figured out the usage. It
|
||||
* might have to be called on init and/or wakeup, I'm not too sure
|
||||
* but things seem to work fine without it so far ...
|
||||
*/
|
||||
|
||||
/* Get max frequency from device-tree */
|
||||
valp = (u32 *)get_property(cpunode, "clock-frequency", NULL);
|
||||
if (!valp) {
|
||||
printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n");
|
||||
rc = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
max_freq = (*valp)/1000;
|
||||
|
||||
/* Now calculate reduced frequency by using the cpuid input freq
|
||||
* ratio. This requires 64 bits math unless we are willing to lose
|
||||
* some precision
|
||||
*/
|
||||
ih = *((u32 *)(eeprom + 0x10));
|
||||
il = *((u32 *)(eeprom + 0x20));
|
||||
min_freq = 0;
|
||||
if (ih != 0 && il != 0)
|
||||
min_freq = (max_freq * il) / ih;
|
||||
|
||||
/* Sanity check */
|
||||
if (min_freq >= max_freq || min_freq < 1000) {
|
||||
printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n");
|
||||
rc = -ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
g5_cpu_freqs[0].frequency = max_freq;
|
||||
g5_cpu_freqs[1].frequency = min_freq;
|
||||
|
||||
/* Set callbacks */
|
||||
g5_switch_volt = g5_pfunc_switch_volt;
|
||||
g5_switch_freq = g5_pfunc_switch_freq;
|
||||
g5_query_freq = g5_pfunc_query_freq;
|
||||
|
||||
/* Force apply current frequency to make sure everything is in
|
||||
* sync (voltage is right for example). Firmware may leave us with
|
||||
* a strange setting ...
|
||||
*/
|
||||
g5_switch_volt(CPUFREQ_HIGH);
|
||||
msleep(10);
|
||||
g5_pmode_cur = -1;
|
||||
g5_switch_freq(g5_query_freq());
|
||||
|
||||
printk(KERN_INFO "Registering G5 CPU frequency driver\n");
|
||||
printk(KERN_INFO "Frequency method: i2c/pfunc, "
|
||||
"Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none");
|
||||
printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n",
|
||||
g5_cpu_freqs[1].frequency/1000,
|
||||
g5_cpu_freqs[0].frequency/1000,
|
||||
g5_cpu_freqs[g5_pmode_cur].frequency/1000);
|
||||
|
||||
rc = cpufreq_register_driver(&g5_cpufreq_driver);
|
||||
bail:
|
||||
if (rc != 0) {
|
||||
pmf_put_function(pfunc_cpu_getfreq);
|
||||
pmf_put_function(pfunc_cpu_setfreq_high);
|
||||
pmf_put_function(pfunc_cpu_setfreq_low);
|
||||
pmf_put_function(pfunc_slewing_done);
|
||||
pmf_put_function(pfunc_cpu0_volt_high);
|
||||
pmf_put_function(pfunc_cpu0_volt_low);
|
||||
pmf_put_function(pfunc_cpu1_volt_high);
|
||||
pmf_put_function(pfunc_cpu1_volt_low);
|
||||
}
|
||||
of_node_put(hwclock);
|
||||
of_node_put(cpuid);
|
||||
of_node_put(cpunode);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init g5_rm31_cpufreq_init(struct device_node *cpus)
|
||||
{
|
||||
/* NYI */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init g5_cpufreq_init(void)
|
||||
{
|
||||
struct device_node *cpus;
|
||||
int rc;
|
||||
|
||||
cpus = of_find_node_by_path("/cpus");
|
||||
if (cpus == NULL) {
|
||||
DBG("No /cpus node !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (machine_is_compatible("PowerMac7,2") ||
|
||||
machine_is_compatible("PowerMac7,3"))
|
||||
rc = g5_pm72_cpufreq_init(cpus);
|
||||
else if (machine_is_compatible("RackMac3,1"))
|
||||
rc = g5_rm31_cpufreq_init(cpus);
|
||||
else
|
||||
rc = g5_neo2_cpufreq_init(cpus);
|
||||
|
||||
of_node_put(cpus);
|
||||
return rc;
|
||||
}
|
||||
|
||||
module_init(g5_cpufreq_init);
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user