Commit 2e45b436 authored by Paul Mackerras's avatar Paul Mackerras

PPC32: remove support for IBM iSeries machines.

All the iSeries machines that can run Linux are 64-bit PowerPC
machines and are now supported by the ppc64 kernel.  Since no-one
is interested in maintaining the iSeries support in the ppc32
kernel, I am removing it.
parent 62b0c735
...@@ -77,9 +77,6 @@ config POWER3 ...@@ -77,9 +77,6 @@ config POWER3
config 8xx config 8xx
bool "8xx" bool "8xx"
config PPC_ISERIES
bool "iSeries"
endchoice endchoice
config 4xx config 4xx
...@@ -105,7 +102,7 @@ config GENERIC_ISA_DMA ...@@ -105,7 +102,7 @@ config GENERIC_ISA_DMA
config PPC64BRIDGE config PPC64BRIDGE
bool bool
depends on PPC_ISERIES || POWER3 depends on POWER3
default y default y
config ALL_PPC config ALL_PPC
...@@ -855,8 +852,7 @@ config MCA ...@@ -855,8 +852,7 @@ config MCA
config PCI config PCI
bool "PCI support" if 4xx || 8260 bool "PCI support" if 4xx || 8260
default y if !4xx && !8260 && !8xx && !APUS && !PPC_ISERIES default y if !4xx && !8260 && !8xx && !APUS
default PCI_ISERIES if !4xx && !8260 && !8xx && !APUS && PPC_ISERIES
default PCI_PERMEDIA if !4xx && !8260 && !8xx && APUS default PCI_PERMEDIA if !4xx && !8260 && !8xx && APUS
default PCI_QSPAN if !4xx && !8260 && 8xx default PCI_QSPAN if !4xx && !8260 && 8xx
help help
...@@ -880,10 +876,6 @@ config PCI_PERMEDIA ...@@ -880,10 +876,6 @@ config PCI_PERMEDIA
bool "PCI for Permedia2" bool "PCI for Permedia2"
depends on !4xx && !8xx && APUS depends on !4xx && !8xx && APUS
config PCI_ISERIES
bool "IBM iSeries Native I/O Support"
depends on !4xx && !8260 && !8xx && !APUS && PPC_ISERIES
# only elf supported, a.out is not -- Cort # only elf supported, a.out is not -- Cort
config KCORE_ELF config KCORE_ELF
bool bool
...@@ -1423,78 +1415,7 @@ source "drivers/isdn/Kconfig" ...@@ -1423,78 +1415,7 @@ source "drivers/isdn/Kconfig"
source "drivers/video/Kconfig" source "drivers/video/Kconfig"
menu "iSeries device drivers"
depends on PPC_ISERIES
config VIOCONS
tristate "iSeries Virtual Console Support"
config VIODASD
tristate "iSeries Virtual I/O disk support"
config VIODASD_IDE
bool "iSeries Virtual disk IDE emulation"
depends on VIODASD
config VIOCD
tristate "iSeries Virtual I/O CD support"
config VIOCD_AZTECH
bool "iSeries Virtual CD Aztech emulation"
depends on VIOCD
config VIOTAPE
tristate "iSeries Virtual Tape Support"
config VETH
tristate "iSeries Virtual Ethernet driver support"
config VIOPATH
bool
depends on VIOCONS || VIODASD || VIOTAPE || VIOCD
default y
config CD_NO_IDESCSI
bool
depends on VIOCD=y
default y
---help---
If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y
here, otherwise N. Read the CD-ROM-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>.
Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all
the questions about these CD-ROM drives. If you are unsure what you
have, say Y and find out whether you have one of the following
drives.
For each of these drivers, a file Documentation/cdrom/{driver_name}
exists. Especially in cases where you do not know exactly which kind
of drive you have you should read there. Most of these drivers use a
file drivers/cdrom/{driver_name}.h where you can define your
interface parameters and switch some internal goodies.
All these CD-ROM drivers are also usable as a module ( = code which
can be inserted in and removed from the running kernel whenever you
want). If you want to compile them as module, say M instead of Y and
read <file:Documentation/modules.txt>.
If you want to use any of these CD-ROM drivers, you also have to
answer Y or M to "ISO 9660 CD-ROM file system support" below (this
answer will get "defaulted" for you if you enable any of the Linux
CD-ROM drivers).
config BLK_DEV_IDECD
tristate
depends on VIOCD=y
default y
endmenu
menu "Old CD-ROM drivers (not SCSI, not IDE)" menu "Old CD-ROM drivers (not SCSI, not IDE)"
depends on !PPC_ISERIES
config CD_NO_IDESCSI config CD_NO_IDESCSI
bool "Support non-SCSI/IDE/ATAPI CDROM drives" bool "Support non-SCSI/IDE/ATAPI CDROM drives"
...@@ -1830,4 +1751,3 @@ endmenu ...@@ -1830,4 +1751,3 @@ endmenu
source "security/Kconfig" source "security/Kconfig"
source "crypto/Kconfig" source "crypto/Kconfig"
...@@ -37,7 +37,6 @@ ifdef CONFIG_MORE_COMPILE_OPTIONS ...@@ -37,7 +37,6 @@ ifdef CONFIG_MORE_COMPILE_OPTIONS
endif endif
head-y := head.o head-y := head.o
head-$(CONFIG_PPC_ISERIES) := iSeries_head.o
head-$(CONFIG_8xx) := head_8xx.o head-$(CONFIG_8xx) := head_8xx.o
head-$(CONFIG_4xx) := head_4xx.o head-$(CONFIG_4xx) := head_4xx.o
head-$(CONFIG_440) := head_44x.o head-$(CONFIG_440) := head_44x.o
...@@ -53,7 +52,6 @@ core-$(CONFIG_4xx) += arch/ppc/platforms/4xx/ ...@@ -53,7 +52,6 @@ core-$(CONFIG_4xx) += arch/ppc/platforms/4xx/
core-$(CONFIG_MATH_EMULATION) += arch/ppc/math-emu/ core-$(CONFIG_MATH_EMULATION) += arch/ppc/math-emu/
core-$(CONFIG_XMON) += arch/ppc/xmon/ core-$(CONFIG_XMON) += arch/ppc/xmon/
core-$(CONFIG_APUS) += arch/ppc/amiga/ core-$(CONFIG_APUS) += arch/ppc/amiga/
core-$(CONFIG_PPC_ISERIES) += arch/ppc/iSeries/
drivers-$(CONFIG_8xx) += arch/ppc/8xx_io/ drivers-$(CONFIG_8xx) += arch/ppc/8xx_io/
drivers-$(CONFIG_4xx) += arch/ppc/4xx_io/ drivers-$(CONFIG_4xx) += arch/ppc/4xx_io/
drivers-$(CONFIG_8260) += arch/ppc/8260_io/ drivers-$(CONFIG_8260) += arch/ppc/8260_io/
...@@ -116,5 +114,4 @@ endif ...@@ -116,5 +114,4 @@ endif
CLEAN_FILES += include/asm-$(ARCH)/offsets.h.tmp \ CLEAN_FILES += include/asm-$(ARCH)/offsets.h.tmp \
include/asm-$(ARCH)/offsets.h \ include/asm-$(ARCH)/offsets.h \
arch/$(ARCH)/kernel/asm-offsets.s \ arch/$(ARCH)/kernel/asm-offsets.s
arch/$(ARCH)/iSeries/ReleaseData.h
#
# Automatically generated make config: don't edit
#
# CONFIG_UID16 is not set
# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
#
# Loadable module support
#
CONFIG_MODULES=y
CONFIG_MODVERSIONS=y
CONFIG_KMOD=y
#
# Platform support
#
CONFIG_PPC=y
CONFIG_PPC32=y
# CONFIG_6xx is not set
# CONFIG_4xx is not set
# CONFIG_POWER3 is not set
# CONFIG_8xx is not set
CONFIG_PPC_ISERIES=y
CONFIG_PPC64BRIDGE=y
# CONFIG_ALL_PPC is not set
# CONFIG_SMP is not set
#
# General setup
#
CONFIG_HIGHMEM=y
# CONFIG_ISA is not set
# CONFIG_EISA is not set
# CONFIG_SBUS is not set
# CONFIG_MCA is not set
# CONFIG_PCI_ISERIES is not set
# CONFIG_PCI is not set
CONFIG_NET=y
CONFIG_SYSCTL=y
CONFIG_SYSVIPC=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_KCORE_ELF=y
CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
CONFIG_BINFMT_MISC=y
# CONFIG_HOTPLUG is not set
# CONFIG_PCMCIA is not set
#
# Parallel port support
#
# CONFIG_PARPORT is not set
# CONFIG_CMDLINE_BOOL is not set
#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set
#
# Plug and Play configuration
#
# CONFIG_PNP is not set
# CONFIG_ISAPNP is not set
# CONFIG_PNPBIOS is not set
#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_DEV_XD is not set
# CONFIG_PARIDE is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_INITRD is not set
#
# Multi-device support (RAID and LVM)
#
# CONFIG_MD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_MD_LINEAR is not set
# CONFIG_MD_RAID0 is not set
# CONFIG_MD_RAID1 is not set
# CONFIG_MD_RAID5 is not set
# CONFIG_MD_MULTIPATH is not set
# CONFIG_BLK_DEV_LVM is not set
#
# Networking options
#
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
CONFIG_NETLINK=y
# CONFIG_RTNETLINK is not set
# CONFIG_NETLINK_DEV is not set
# CONFIG_NETFILTER is not set
# CONFIG_FILTER is not set
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
# CONFIG_IP_PNP_BOOTP is not set
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_INET_ECN is not set
CONFIG_SYN_COOKIES=y
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
#
#
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_DECNET is not set
# CONFIG_BRIDGE is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_LLC is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
#
# ATA/IDE/MFM/RLL support
#
# CONFIG_IDE is not set
# CONFIG_BLK_DEV_IDE_MODES is not set
# CONFIG_BLK_DEV_HD is not set
#
# SCSI support
#
# CONFIG_SCSI is not set
#
# Network device support
#
CONFIG_NETDEVICES=y
#
# ARCnet devices
#
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_ETHERTAP is not set
#
# Ethernet (10 or 100Mbit)
#
# CONFIG_NET_ETHERNET is not set
#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
# CONFIG_DL2K is not set
# CONFIG_MYRI_SBUS is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PLIP is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
#
# Wireless LAN (non-hamradio)
#
# CONFIG_NET_RADIO is not set
#
# Token Ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
# CONFIG_RCPCI is not set
# CONFIG_SHAPER is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
#
# Amateur Radio support
#
# CONFIG_HAMRADIO is not set
#
# IrDA (infrared) support
#
# CONFIG_IRDA is not set
#
# ISDN subsystem
#
# CONFIG_ISDN is not set
#
# Old CD-ROM drivers (not SCSI, not IDE)
#
CONFIG_CD_NO_IDESCSI=y
# CONFIG_AZTCD is not set
# CONFIG_GSCD is not set
# CONFIG_SBPCD is not set
# CONFIG_MCD is not set
# CONFIG_MCDX is not set
# CONFIG_OPTCD is not set
# CONFIG_CM206 is not set
# CONFIG_SJCD is not set
# CONFIG_ISP16_CDI is not set
# CONFIG_CDU31A is not set
# CONFIG_CDU535 is not set
#
# Console drivers
#
# CONFIG_VGA_CONSOLE is not set
#
# Frame-buffer support
#
# CONFIG_FB is not set
#
# iSeries device drivers
#
CONFIG_VIOCONS=y
CONFIG_VIODASD=y
# CONFIG_VIODASD_IDE is not set
CONFIG_VIOCD=y
# CONFIG_VIOCD_AZTECH is not set
CONFIG_VIOTAPE=y
CONFIG_VETH=y
CONFIG_VIOPATH=y
CONFIG_CD_NO_IDESCSI=y
CONFIG_BLK_DEV_IDECD=y
#
# Input core support
#
# CONFIG_INPUT is not set
# CONFIG_INPUT_KEYBDEV is not set
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_EVDEV is not set
#
# Macintosh device drivers
#
#
# Character devices
#
# CONFIG_VT is not set
# CONFIG_SERIAL is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
#
# I2C support
#
# CONFIG_I2C is not set
#
# Mice
#
# CONFIG_BUSMOUSE is not set
# CONFIG_MOUSE is not set
#
# Joysticks
#
# CONFIG_INPUT_GAMEPORT is not set
#
# Input core support is needed for gameports
#
#
# Input core support is needed for joysticks
#
# CONFIG_QIC02_TAPE is not set
#
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
# CONFIG_INTEL_RNG is not set
# CONFIG_NVRAM is not set
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
#
# Ftape, the floppy tape device driver
#
# CONFIG_FTAPE is not set
# CONFIG_AGP is not set
# CONFIG_DRM is not set
#
# Multimedia devices
#
# CONFIG_VIDEO_DEV is not set
#
# File systems
#
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
CONFIG_REISERFS_FS=y
# CONFIG_REISERFS_CHECK is not set
# CONFIG_ADFS_FS is not set
# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS_FS is not set
# CONFIG_JFFS2_FS is not set
CONFIG_CRAMFS=y
# CONFIG_TMPFS is not set
CONFIG_RAMFS=y
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
# CONFIG_MINIX_FS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_NTFS_DEBUG is not set
# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
CONFIG_DEVFS_FS=y
CONFIG_DEVFS_MOUNT=y
# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_QNX4FS_RW is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
# CONFIG_UDF_FS is not set
# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
# CONFIG_UFS_FS_WRITE is not set
#
# Network File Systems
#
# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
# CONFIG_NFSD is not set
# CONFIG_NFSD_V3 is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
# CONFIG_NCPFS_STRONG is not set
# CONFIG_NCPFS_NFS_NS is not set
# CONFIG_NCPFS_OS2_NS is not set
# CONFIG_NCPFS_SMALLDOS is not set
# CONFIG_NCPFS_NLS is not set
# CONFIG_NCPFS_EXTRAS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
# CONFIG_SMB_NLS is not set
CONFIG_NLS=y
#
# Native Language Support
#
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
#
# Sound
#
# CONFIG_SOUND is not set
#
# USB support
#
# CONFIG_USB is not set
#
# Bluetooth support
#
# CONFIG_BT is not set
#
# Kernel hacking
#
# CONFIG_MAGIC_SYSRQ is not set
# CONFIG_KGDB is not set
# CONFIG_XMON is not set
# CONFIG_BDI_SWITCH is not set
/*
* HvCall.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/system.h>
#include <asm/page.h>
#include <asm/iSeries/HvCall.h>
#include <asm/iSeries/HvCallEvent.h>
#ifndef _HVCALLSC_H
#include <asm/iSeries/HvCallSc.h>
#endif
#include <asm/iSeries/LparData.h>
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
//=====================================================================
// Note that this call takes at MOST one page worth of data
int HvCall_readLogBuffer(HvLpIndex lpIndex, void *buffer, u64 bufLen)
{
struct HvLpBufferList *bufList;
u64 bytesLeft = bufLen;
u64 leftThisPage;
u64 curPtr = (unsigned long) buffer;
u64 retVal;
int npages;
int i;
npages = 0;
while (bytesLeft)
{
npages++;
leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr;
if (leftThisPage > bytesLeft)
bytesLeft = 0;
else
bytesLeft -= leftThisPage;
curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE;
}
if (npages == 0)
return 0;
bufList = (struct HvLpBufferList *)kmalloc(npages * sizeof(struct HvLpBufferList), GFP_ATOMIC);
bytesLeft = bufLen;
curPtr = (unsigned long) buffer;
for(i=0; i<npages; i++)
{
bufList[i].addr = virt_to_absolute(curPtr);
leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr;
if (leftThisPage > bytesLeft)
{
bufList[i].len = bytesLeft;
bytesLeft = 0;
}
else
{
bufList[i].len = leftThisPage;
bytesLeft -= leftThisPage;
}
curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE;
}
retVal = HvCall3(HvCallBaseReadLogBuffer,lpIndex, virt_to_absolute((unsigned long)bufList), bufLen);
kfree(bufList);
return (int)retVal;
}
//=====================================================================
void HvCall_writeLogBuffer(const void *buffer, u64 bufLen)
{
struct HvLpBufferList bufList;
u64 bytesLeft = bufLen;
u64 leftThisPage;
u64 curPtr = (unsigned long) buffer;
while (bytesLeft)
{
bufList.addr = virt_to_absolute( curPtr );
leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr;
if (leftThisPage > bytesLeft)
{
bufList.len = bytesLeft;
bytesLeft = 0;
}
else
{
bufList.len = leftThisPage;
bytesLeft -= leftThisPage;
}
HvCall2(HvCallBaseWriteLogBuffer, virt_to_absolute((unsigned long)&bufList), bufList.len);
curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE;
}
}
/*
* HvLpConfig.c
* Copyright (C) 2001 Kyle A. Lucke, IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _HVLPCONFIG_H
#include <asm/iSeries/HvLpConfig.h>
#endif
HvLpIndex HvLpConfig_getLpIndex_outline(void)
{
return HvLpConfig_getLpIndex();
}
/*
* HvLpEvent.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/HvCallEvent.h>
#include <asm/iSeries/LparData.h>
// Array of LpEvent handler functions
LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes];
// Register a handler for an LpEvent type
int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler handler )
{
int rc = 1;
if ( eventType < HvLpEvent_Type_NumTypes ) {
lpEventHandler[eventType] = handler;
rc = 0;
}
return rc;
}
int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType )
{
int rc = 1;
if ( eventType < HvLpEvent_Type_NumTypes ) {
if ( !lpEventHandlerPaths[eventType] ) {
lpEventHandler[eventType] = NULL;
rc = 0;
}
}
return rc;
}
// (lpIndex is the partition index of the target partition.
// needed only for VirtualIo, VirtualLan and SessionMgr. Zero
// indicates to use our partition index - for the other types)
int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex )
{
int rc = 1;
if ( eventType < HvLpEvent_Type_NumTypes &&
lpEventHandler[eventType] ) {
if ( lpIndex == 0 )
lpIndex = itLpNaca.xLpIndex;
HvCallEvent_openLpEventPath( lpIndex, eventType );
++lpEventHandlerPaths[eventType];
rc = 0;
}
return rc;
}
int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex )
{
int rc = 1;
if ( eventType < HvLpEvent_Type_NumTypes &&
lpEventHandler[eventType] &&
lpEventHandlerPaths[eventType] ) {
if ( lpIndex == 0 )
lpIndex = itLpNaca.xLpIndex;
HvCallEvent_closeLpEventPath( lpIndex, eventType );
--lpEventHandlerPaths[eventType];
rc = 0;
}
return rc;
}
/*
* ItLpQueue.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/HvCallEvent.h>
#include <asm/iSeries/LparData.h>
static __inline__ int set_inUse( struct ItLpQueue * lpQueue )
{
int t;
u32 * inUseP = &(lpQueue->xInUseWord);
__asm__ __volatile__("\n\
1: lwarx %0,0,%2 \n\
cmpi 0,%0,0 \n\
li %0,0 \n\
bne- 2f \n\
addi %0,%0,1 \n\
stwcx. %0,0,%2 \n\
bne- 1b \n\
2: eieio"
: "=&r" (t), "=m" (lpQueue->xInUseWord)
: "r" (inUseP), "m" (lpQueue->xInUseWord)
: "cc");
return t;
}
static __inline__ void clear_inUse( struct ItLpQueue * lpQueue )
{
lpQueue->xInUseWord = 0;
}
// Array of LpEvent handler functions
extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
unsigned long ItLpQueueInProcess = 0;
struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue )
{
struct HvLpEvent * nextLpEvent =
(struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
if ( nextLpEvent->xFlags.xValid ) {
// Set pointer to next potential event
lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 +
LpEventAlign ) /
LpEventAlign ) *
LpEventAlign;
// Wrap to beginning if no room at end
if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr)
lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr;
}
else
nextLpEvent = NULL;
return nextLpEvent;
}
int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue )
{
struct HvLpEvent * nextLpEvent =
(struct HvLpEvent *)lpQueue->xSlicCurEventPtr;
return ( nextLpEvent->xFlags.xValid |
lpQueue->xPlicOverflowIntPending);
}
void ItLpQueue_clearValid( struct HvLpEvent * event )
{
// Clear the valid bit of the event
// Also clear bits within this event that might
// look like valid bits (on 64-byte boundaries)
unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) /
LpEventAlign ) - 1;
switch ( extra ) {
case 3:
((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0;
case 2:
((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0;
case 1:
((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0;
case 0:
}
mb();
event->xFlags.xValid = 0;
}
// No lock is necessary when processing the Lp Queue because a single
// processor is assigned to each lpqueue. Interrupts are disabled
// while processing events.
// Some device drivers' interrupt handlers run with interrupts
// enabled. This requires us to prevent being re-entered here.
// We use the xInUse flag for that.
unsigned lpQueue_proc_count[32] = {};
unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs )
{
unsigned numIntsProcessed = 0;
struct HvLpEvent * nextLpEvent;
// If we have recursed, just return
if ( !set_inUse( lpQueue ) )
return 0;
if (ItLpQueueInProcess == 0)
ItLpQueueInProcess = 1;
else
BUG();
++lpQueue_proc_count[current->processor];
for (;;) {
nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue );
if ( nextLpEvent ) {
// Count events to return to caller
// and count processed events in lpQueue
++numIntsProcessed;
lpQueue->xLpIntCount++;
// Call appropriate handler here, passing
// a pointer to the LpEvent. The handler
// must make a copy of the LpEvent if it
// needs it in a bottom half. (perhaps for
// an ACK)
// Handlers are responsible for ACK processing
// The Hypervisor guarantees that LpEvents will
// only be delivered with types that we have
// registered for, so no type check is necessary
// here!
if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes )
lpQueue->xLpIntCountByType[nextLpEvent->xType]++;
if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes &&
lpEventHandler[nextLpEvent->xType] )
lpEventHandler[nextLpEvent->xType](nextLpEvent, regs);
else
printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType );
ItLpQueue_clearValid( nextLpEvent );
}
else // No more valid events
// If overflow events are pending
// process them
if ( lpQueue->xPlicOverflowIntPending ) {
HvCallEvent_getOverflowLpEvents(
lpQueue->xIndex);
}
else // If nothing left then we are done
break;
}
ItLpQueueInProcess = 0;
mb();
clear_inUse( lpQueue );
return numIntsProcessed;
}
/*
* LparData.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define __KERNEL__ 1
#include <asm/types.h>
#include <asm/page.h>
#include <stddef.h>
#include <linux/config.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/iSeries/Naca.h>
#include <asm/iSeries/ItLpNaca.h>
#include <asm/iSeries/ItLpPaca.h>
#include <asm/iSeries/ItLpRegSave.h>
#include <asm/iSeries/Paca.h>
#include <asm/iSeries/HvReleaseData.h>
#include <asm/iSeries/LparMap.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/ItVpdAreas.h>
#include <asm/iSeries/ItIplParmsReal.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/IoHriProcessorVpd.h>
#include <asm/iSeries/ItSpCommArea.h>
#include "ReleaseData.h"
// maxProcessors is the number of physical processors
// The number of logical processors is twice that
// number to support hardware multi-threading.
// If CONFIG_SMP is not defined, then logical
// processors will be defined, but the other threads
// will spin forever in iSeries_head.S
#define maxProcessors 16
extern char _start_boltedStacks[];
unsigned maxPacas = maxProcessors * 2;
// The LparMap is used by the hypervisor to map the load area.
// This indicates that the load area should be mapped to VSID
// 0x000000000000C and that this should be made addressable at
// ESID 0x00000000C. On 32-bit machines this is equivalent to
// loading segment register 12 with VSID 12.
// 8192 indicates to map 8192 pages (32 MB) of the load area.
struct LparMap xLparMap = {
.xNumberEsids =4, // Number ESID/VSID pairs
.xNumberRanges =1, // Number of memory ranges
.xSegmentTableOffs =0, // Segment Table Page (unused)
.xKernelEsidC =0xC, // ESID to map
.xKernelVsidC =0xCCC, // VSID to map
.xKernelEsidD =0xD, // ESID to map
.xKernelVsidD =0xDDD, // VSID to map
.xKernelEsidE =0xE, // ESID to map
.xKernelVsidE =0xEEE, // VSID to map
.xKernelEsidF =0xF, // ESID to map
.xKernelVsidF =0xFFF, // VSID to map
.xPages = HvPagesToMap, // # of pages to map (8192)
.xOffset = 0, // Offset into load area
.xVPN = 0xCCC0000 // VPN of first mapped page
};
// The Naca has a pointer to the ItVpdAreas. The hypervisor finds
// the Naca via the HvReleaseData area. The HvReleaseData has the
// offset into the Naca of the pointer to the ItVpdAreas.
extern struct ItVpdAreas itVpdAreas;
struct Naca xNaca = {
0, (void *)&itVpdAreas,
0, 0, // Ram Disk start
0, 0 // Ram Disk size
};
// The LpQueue is used to pass event data from the hypervisor to
// the partition. This is where I/O interrupt events are communicated.
// The ItLpQueue must be initialized (even though only to all zeros)
// If it were uninitialized (in .bss) it would get zeroed after the
// kernel gets control. The hypervisor will have filled in some fields
// before the kernel gets control. By initializing it we keep it out
// of the .bss
struct ItLpQueue xItLpQueue = {};
// The Paca is an array with one entry per processor. Each contains an
// ItLpPaca, 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.
// 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
// processors. The processor VPD array needs one entry per physical
// processor (not thread).
#define PacaInit( n, start, lpq ) \
{ 0, (struct ItLpPaca *)(((char *)(&xPaca[(n)]))+offsetof(struct Paca, xLpPaca)), \
0, (struct ItLpRegSave *)(((char *)(&xPaca[(n)]))+offsetof(struct Paca, xRegSav)), \
0, 0, 0, \
(n), (start), \
0, \
0, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0},\
(lpq), \
0, 0, {0}, \
{ /* LpPaca */ \
.xDesc = 0xd397d781, /* "LpPa" */ \
.xSize = sizeof(struct ItLpPaca), \
.xFPRegsInUse =1, \
.xDynProcStatus = 2, \
.xEndOfQuantum =0xffffffffffffffff \
}, \
{ /* LpRegSave */ \
0xd397d9e2, /* "LpRS" */ \
sizeof(struct ItLpRegSave) \
} \
}
struct Paca xPaca[maxProcessors*2] = {
PacaInit( 0, 1, &xItLpQueue )
,PacaInit( 1, 0, 0 )
,PacaInit( 2, 0, 0 )
,PacaInit( 3, 0, 0 )
,PacaInit( 4, 0, 0 )
,PacaInit( 5, 0, 0 )
,PacaInit( 6, 0, 0 )
,PacaInit( 7, 0, 0 )
,PacaInit( 8, 0, 0 )
,PacaInit( 9, 0, 0 )
,PacaInit( 10, 0, 0 )
,PacaInit( 11, 0, 0 )
,PacaInit( 12, 0, 0 )
,PacaInit( 13, 0, 0 )
,PacaInit( 14, 0, 0 )
,PacaInit( 15, 0, 0 )
,PacaInit( 16, 0, 0 )
,PacaInit( 17, 0, 0 )
,PacaInit( 18, 0, 0 )
,PacaInit( 19, 0, 0 )
,PacaInit( 20, 0, 0 )
,PacaInit( 21, 0, 0 )
,PacaInit( 22, 0, 0 )
,PacaInit( 23, 0, 0 )
,PacaInit( 24, 0, 0 )
,PacaInit( 25, 0, 0 )
,PacaInit( 26, 0, 0 )
,PacaInit( 27, 0, 0 )
,PacaInit( 28, 0, 0 )
,PacaInit( 29, 0, 0 )
,PacaInit( 30, 0, 0 )
,PacaInit( 31, 0, 0 )
};
// The HvReleaseData is the root of the information shared between
// the hypervisor and Linux.
struct HvReleaseData
hvReleaseData = { 0xc8a5d9c4, // desc = "HvRD" ebcdic
sizeof(struct HvReleaseData),
offsetof(struct Naca, xItVpdAreas64),
0, &xNaca, // 64-bit Naca address
((u32)&xLparMap-KERNELBASE),
0,
1, // tags inactive mode
1, // 32-bit mode
0, // shared processors
0, // hardware multithreading
6, // TEMP: set me back to 0
// this allows non-ga 450 driver
3, // We are v5r1m0
3, // Min supported PLIC = v5r1m0
3, // Min usuable PLIC = v5r1m0
RELEASEDATA,
{0}
};
struct ItLpNaca itLpNaca = { 0xd397d581, // desc = "LpNa" ebcdic
0x0400, // size of ItLpNaca
0x0300, 19, // offset to int array, # ents
0, 0, 0, // Part # of primary, serv, me
0, 0x100, // # of LP queues, offset
0, 0, 0, // Piranha stuff
{ 0,0,0,0,0 }, // reserved
0,0,0,0,0,0,0, // stuff
{ 0,0,0,0,0 }, // reserved
0, // reserved
0, // VRM index of PLIC
0, 0, // min supported, compat SLIC
0, // 64-bit addr of load area
0, // chunks for load area
0, // reserved
{ 0 }, // 72 reserved bytes
{ 0 }, // 128 reserved bytes
{ 0 }, // Old LP Queue
{ 0 }, // 384 reserved bytes
{
0xc0000100, // int 0x100
0xc0000200, // int 0x200
0xc0000300, // int 0x300
0xc0000400, // int 0x400
0xc0000500, // int 0x500
0xc0000600, // int 0x600
0xc0000700, // int 0x700
0xc0000800, // int 0x800
0xc0000900, // int 0x900
0xc0000a00, // int 0xa00
0xc0000b00, // int 0xb00
0xc0000c00, // int 0xc00
0xc0000d00, // int 0xd00
0xc0000e00, // int 0xe00
0xc0000f00, // int 0xf00
0xc0001000, // int 0x1000
0xc0001010, // int 0x1010
0xc0001020, // int 0x1020 CPU ctls
0xc0000500 // SC Ret Hdlr
// int 0x380
// int 0x480
}
};
// All of the data structures that will be filled in by the hypervisor
// must be initialized (even if only to zeroes) to keep them out of
// the bss. If in bss, they will be zeroed by the kernel startup code
// after the hypervisor has filled them in.
struct ItIplParmsReal xItIplParmsReal = {};
struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = {
{
.xTimeBaseFreq = 50000000
}
};
u64 xMsVpd[3400] = {}; // Space for Main Store Vpd 27,200 bytes
u64 xRecoveryLogBuffer[32] = {}; // Space for Recovery Log Buffer
struct SpCommArea xSpCommArea = {
0xE2D7C3C2,
1,
{0},
0, 0, 0, 0, {0}
};
struct ItVpdAreas itVpdAreas = {
0xc9a3e5c1, // "ItVA"
sizeof( struct ItVpdAreas ),
0, 0,
26, // # VPD array entries
10, // # DMA array entries
maxProcessors*2, maxProcessors, // Max logical, physical procs
offsetof(struct ItVpdAreas,xPlicDmaToks),// offset to DMA toks
offsetof(struct ItVpdAreas,xSlicVpdAdrs),// offset to VPD addrs
offsetof(struct ItVpdAreas,xPlicDmaLens),// offset to DMA lens
offsetof(struct ItVpdAreas,xSlicVpdLens),// offset to VPD lens
0, // max slot labels
1, // max LP queues
{0}, {0}, // reserved
{0}, // DMA lengths
{0}, // DMA tokens
{ // VPD lengths
0,0,0,0, // 0 - 3
sizeof(struct Paca), // 4 length of Paca
0, // 5
sizeof(struct ItIplParmsReal),// 6 length of IPL parms
26992, // 7 length of MS VPD
0, // 8
sizeof(struct ItLpNaca),// 9 length of LP Naca
0, // 10
256, // 11 length of Recovery Log Buf
sizeof(struct SpCommArea), // 12 length of SP Comm area
0,0,0, // 13 - 15
sizeof(struct IoHriProcessorVpd),// 16 length of Proc Vpd
0,0,0,0,0,0, // 17 - 22
sizeof(struct ItLpQueue),// 23 length of Lp Queue
0,0 // 24 - 25
},
{ // VPD addresses
{0},{0},{0},{0}, // 0 - 3
{0, &xPaca[0]}, // 4 first Paca
{0}, // 5
{0, &xItIplParmsReal}, // 6 IPL parms
{0, &xMsVpd}, // 7 MS Vpd
{0}, // 8
{0, &itLpNaca}, // 9 LpNaca
{0}, // 10
{0, &xRecoveryLogBuffer},// 11 Recovery Log Buffer
{0, &xSpCommArea}, // 12 Sp Comm Area
{0},{0},{0}, // 13 - 15
{0, &xIoHriProcessorVpd},// 16 Proc Vpd
{0},{0},{0},{0},{0},{0},// 17 - 22
{0, &xItLpQueue}, // 23 Lp Queue
{0},{0}
}
};
// The size of this array determines how much main store can be
// configured for use in the partition. 16384 allows 16384 * 256KB
// which is 4GB. This is enough for the 32-bit
// implementation, but the msChunks array will need to be dynamically
// allocated for really big partitions.
u32 msChunks[16384] = {0};
u32 totalLpChunks = 0;
// Data area used in flush_hash_page
long long flush_hash_page_hpte[2] = {0,0};
u64 virt_to_absolute_outline(u32 address)
{
return virt_to_absolute(address);
}
#
# Makefile for Linux arch/ppc/iSeries source directory
#
export-objs := iSeries_ksyms.o
obj-y += LparData.o ItLpQueue.o HvLpEvent.o HvCall.o mf.o iSeries_proc.o mf_proc.o iSeries_ksyms.o HvLpConfig.o pmc_proc.o rtc.o
obj-$(CONFIG_PCI) += XmPciLpEvent.o iSeries_FlightRecorder.o iSeries_IoMmTable.o iSeries_VpdInfo.o iSeries_fixup.o iSeries_irq.o iSeries_pci.o iSeries_pci_proc.o iSeries_reset_device.o
LparData.c:: ReleaseData.h
ReleaseData.h: $(TOPDIR)/Makefile
/bin/bash ./createReleaseData $(KERNELRELEASE) > $@
clean:
rm -f ReleaseData.h
/*
* File XmPciLpEvent.h created by Wayne Holm on Mon Jan 15 2001.
*
* This module handles PCI interrupt events sent by the AS400 Hypervisor.
*/
#include <linux/pci.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/smp.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/bootmem.h>
#include <linux/blk.h>
#include <linux/ide.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/XmPciLpEvent.h>
enum XmPciLpEvent_Subtype {
XmPciLpEvent_BusCreated = 0, // PHB has been created
XmPciLpEvent_BusFailed = 1, // PHB has failed
XmPciLpEvent_BusRecovered = 12, // PHB has been recovered
XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed
XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered
XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt
};
struct XmPciLpEvent_BusInterrupt {
HvBusNumber busNumber;
HvSubBusNumber subBusNumber;
};
struct XmPciLpEvent_NodeInterrupt {
HvBusNumber busNumber;
HvSubBusNumber subBusNumber;
HvAgentId deviceId;
};
struct XmPciLpEvent {
struct HvLpEvent hvLpEvent;
union {
u64 alignData; // 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;
};
static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm);
static void XmPciLpEvent_handler( struct HvLpEvent* eventParm, struct pt_regs* regsParm) {
if (eventParm && eventParm->xType == HvLpEvent_Type_PciIo) {
switch( eventParm->xFlags.xFunction ) {
case HvLpEvent_Function_Int:
intReceived( (struct XmPciLpEvent*)eventParm, regsParm );
break;
case HvLpEvent_Function_Ack:
printk(KERN_ERR "XmPciLpEvent.c: unexpected ack received\n");
break;
default:
printk(KERN_ERR "XmPciLpEvent.c: unexpected event function %d\n",
(int)eventParm->xFlags.xFunction);
break;
};
}
else {
if (event) {
printk(KERN_ERR "XmPciLpEvent.c: unrecognized event type 0x%x\n",
(int)eventParm->xType);
}
else {
printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n");
}
}
}
static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm) {
int irq;
switch (eventParm->hvLpEvent.xSubtype) {
case XmPciLpEvent_SlotInterrupt:
irq = eventParm->hvLpEvent.xCorrelationToken;
/* Dispatch the interrupt handlers for this irq */
ppc_irq_dispatch_handler(regsParm, irq);
HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber,
eventParm->eventData.slotInterrupt.subBusNumber,
eventParm->eventData.slotInterrupt.deviceId);
break;
/* Ignore error recovery events for now */
case XmPciLpEvent_BusCreated:
printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n", eventParm->eventData.busCreated.busNumber);
break;
case XmPciLpEvent_BusFailed:
printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n", eventParm->eventData.busFailed.busNumber);
break;
case XmPciLpEvent_BusRecovered:
printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n", eventParm->eventData.busRecovered.busNumber);
break;
case XmPciLpEvent_NodeFailed:
printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n", eventParm->eventData.nodeFailed.busNumber, eventParm->eventData.nodeFailed.subBusNumber, eventParm->eventData.nodeFailed.deviceId);
break;
case XmPciLpEvent_NodeRecovered:
printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n", eventParm->eventData.nodeRecovered.busNumber, eventParm->eventData.nodeRecovered.subBusNumber, eventParm->eventData.nodeRecovered.deviceId);
break;
default:
printk(KERN_ERR "XmPciLpEvent.c: unrecognized event subtype 0x%x\n",
eventParm->hvLpEvent.xSubtype);
break;
};
}
/* This should be called sometime prior to buswalk (init_IRQ would be good) */
int XmPciLpEvent_init() {
int xRc;
xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, &XmPciLpEvent_handler);
if (xRc == 0) {
xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
if (xRc != 0) {
printk(KERN_ERR "XmPciLpEvent.c: open event path failed with rc 0x%x\n", xRc);
}
}
else {
printk(KERN_ERR "XmPciLpEvent.c: register handler failed with rc 0x%x\n", xRc);
}
return xRc;
}
#!/bin/bash
####################################################
# Build a hex ebcdic representation of the
# KERNELRELEASE for use in the iSeries
# hvReleaseData structure
####################################################
if [ $# -ne 1 ]
then
echo "Syntax: createReleaseData kernelversion"
exit 1
fi
len=${#1}
rd=''
####################################################
# ReleaseData is maximum of 12 chars
####################################################
if [ $len -gt 12 ]
then
len=12
fi
#for (( i=0 ; $i < $len ; i=$i+1 )) ;
i=0
while (($i<$len));
do
char=${1:$i:1}
case $char in
'a') xchar='0x81';;
'b') xchar='0x82';;
'c') xchar='0x83';;
'd') xchar='0x84';;
'e') xchar='0x85';;
'f') xchar='0x86';;
'g') xchar='0x87';;
'h') xchar='0x88';;
'i') xchar='0x89';;
'j') xchar='0x91';;
'k') xchar='0x92';;
'l') xchar='0x93';;
'm') xchar='0x94';;
'n') xchar='0x95';;
'o') xchar='0x96';;
'p') xchar='0x97';;
'q') xchar='0x98';;
'r') xchar='0x99';;
's') xchar='0xA2';;
't') xchar='0xA3';;
'u') xchar='0xA4';;
'v') xchar='0xA5';;
'w') xchar='0xA6';;
'x') xchar='0xA7';;
'y') xchar='0xA8';;
'z') xchar='0xA9';;
'A') xchar='0xC1';;
'B') xchar='0xC2';;
'C') xchar='0xC3';;
'D') xchar='0xC4';;
'E') xchar='0xC5';;
'F') xchar='0xC6';;
'G') xchar='0xC7';;
'H') xchar='0xC8';;
'I') xchar='0xC9';;
'J') xchar='0xD1';;
'K') xchar='0xD2';;
'L') xchar='0xD3';;
'M') xchar='0xD4';;
'N') xchar='0xD5';;
'O') xchar='0xD6';;
'P') xchar='0xD7';;
'Q') xchar='0xD8';;
'R') xchar='0xD9';;
'S') xchar='0xE2';;
'T') xchar='0xE3';;
'U') xchar='0xE4';;
'V') xchar='0xE5';;
'W') xchar='0xE6';;
'X') xchar='0xE7';;
'Y') xchar='0xE8';;
'Z') xchar='0xE9';;
'0') xchar='0xF0';;
'1') xchar='0xF1';;
'2') xchar='0xF2';;
'3') xchar='0xF3';;
'4') xchar='0xF4';;
'5') xchar='0xF5';;
'6') xchar='0xF6';;
'7') xchar='0xF7';;
'8') xchar='0xF8';;
'9') xchar='0xF9';;
'.') xchar='0x4B';;
'-') xchar='0x60';;
'_') xchar='0x6D';;
'+') xchar='0x4E';;
'@') xchar='0x7C';;
'$') xchar='0x5B';;
'%') xchar='0x6C';;
*) xchar='';;
esac
rd=${rd}${xchar}
if [ $(($i+1)) -lt $len ]
then
rd=${rd}', '
fi
i=$i+1
done
#echo "#define RELEASEDATA { $rd }">ReleaseData.h
echo "#define RELEASEDATA { $rd }"
/************************************************************************/
/* File iSeries_FlightRecorder.c created by Al Trautman on Jan 22 2001. */
/************************************************************************/
/* This code supports the pci interface on the IBM iSeries systems. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Jan 22, 2001 */
/* Added Time Stamps, April 12, 2001 */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <linux/kernel.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <linux/rtc.h>
#include <asm/iSeries/mf.h>
/************************************************************************/
/* Log entry into buffer, */
/* ->If entry is going to wrap, log "WRAP" and start at the top. */
/************************************************************************/
void iSeries_LogFr_Entry(FlightRecorder* Fr, char* LogText) {
int Residual, TextLen;
if(Fr->StartingPointer > 0) { /* Initialized yet? */
Residual = FlightRecorderSize - (Fr->CurrentPointer - Fr->StartingPointer);
TextLen = strlen(LogText); /* Length of Text */
if(TextLen+16 > Residual) { /* Room for text or need to wrap*/
strcpy(Fr->CurrentPointer,"WRAP");
++Fr->WrapCount; /* Increment Wraps */
Fr->CurrentPointer = Fr->StartingPointer;
}
strcpy(Fr->CurrentPointer,LogText);
Fr->CurrentPointer += TextLen+1;
strcpy(Fr->CurrentPointer,"<=");
}
}
/************************************************************************/
/* Log entry with time */
/************************************************************************/
void iSeries_LogFr_Time(FlightRecorder* Fr, char* LogText) {
struct rtc_time Rtc;
char LogBuffer[256];
mf_getRtc(&Rtc);
sprintf(LogBuffer,"%02d:%02d:%02d %s",
Rtc.tm_hour,Rtc.tm_min,Rtc.tm_sec,
LogText);
iSeries_LogFr_Entry(Fr,LogBuffer);
}
/************************************************************************/
/* Log Entry with Date and call Time Log */
/************************************************************************/
void iSeries_LogFr_Date(FlightRecorder* Fr, char* LogText) {
struct rtc_time Rtc;
char LogBuffer[256];
mf_getRtc(&Rtc);
sprintf(LogBuffer,"%02d.%02d.%02d %02d:%02d:%02d %s",
Rtc.tm_year+1900, Rtc.tm_mon, Rtc.tm_mday,
Rtc.tm_hour,Rtc.tm_min,Rtc.tm_sec,
LogText);
iSeries_LogFr_Entry(Fr,LogBuffer);
}
/************************************************************************/
/* Initialized the Flight Recorder */
/************************************************************************/
void iSeries_Fr_Initialize(FlightRecorder* Fr, char* Signature) {
if(strlen(Signature) > 16) memcpy(Fr->Signature,Signature,16);
else strcpy(Fr->Signature,Signature);
Fr->StartingPointer = &Fr->Buffer[0];
Fr->CurrentPointer = Fr->StartingPointer;
Fr->logEntry = iSeries_LogFr_Entry;
Fr->logDate = iSeries_LogFr_Date;
Fr->logTime = iSeries_LogFr_Time;
Fr->logEntry(Fr,"FR Initialized."); /* Note, can't use time yet! */
}
/************************************************************************/
/* This module supports the iSeries I/O Address translation mapping */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, December 14, 2000 */
/* Added Bar table for IoMm performance. */
/* End Change Activity */
/************************************************************************/
#include <asm/types.h>
#include <asm/resource.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include "iSeries_IoMmTable.h"
#include "iSeries_pci.h"
void iSeries_allocateDeviceBars(struct pci_dev* PciDevPtr);
/*******************************************************************/
/* Table defines */
/* Entry size is 4 MB * 1024 Entries = 4GB. */
/*******************************************************************/
#define iSeries_IoMmTable_Entry_Size 0x00400000
#define iSeries_IoMmTable_Size 1024
#define iSeries_Base_Io_Memory 0xFFFFFFFF
/*******************************************************************/
/* Static and Global variables */
/*******************************************************************/
struct pci_dev* iSeries_IoMmTable[iSeries_IoMmTable_Size];
u8 iSeries_IoBarTable[iSeries_IoMmTable_Size];
static int iSeries_CurrentIndex;
static char* iSeriesPciIoText = "iSeries PCI I/O";
static spinlock_t iSeriesIoMmTableLock = SPIN_LOCK_UNLOCKED;
/*******************************************************************/
/* iSeries_IoMmTable_Initialize */
/*******************************************************************/
/* - Initalizes the Address Translation Table and get it ready for */
/* use. Must be called before any client calls any of the other */
/* methods. */
/*******************************************************************/
void iSeries_IoMmTable_Initialize(void) {
int Index;
spin_lock(&iSeriesIoMmTableLock);
for(Index=0;Index<iSeries_IoMmTable_Size ; ++Index) {
iSeries_IoMmTable[Index] = NULL;
iSeries_IoBarTable[Index] = 0xFF;
}
spin_unlock(&iSeriesIoMmTableLock);
iSeries_CurrentIndex = iSeries_IoMmTable_Size-1;
ISERIES_PCI_FR("IoMmTable Init.");
}
/*******************************************************************/
/* iSeries_allocateDeviceBars */
/*******************************************************************/
/* - Allocates ALL pci_dev BAR's and updates the resources with the*/
/* BAR value. BARS with zero length will have the resources */
/* The HvCallPci_getBarParms is used to get the size of the BAR */
/* space. It calls iSeries_IoMmTable_AllocateEntry to allocate */
/* each entry. */
/* - Loops through The Bar resourses(0 - 5) including the the ROM */
/* is resource(6). */
/*******************************************************************/
void iSeries_allocateDeviceBars(struct pci_dev* PciDevPtr) {
struct resource* BarResource;
int BarNumber = 0; /* Current Bar Number */
if(PciTraceFlag > 0) {
printk("PCI: iSeries_allocateDeviceBars 0x%08X\n",(int)PciDevPtr);
sprintf(PciFrBuffer,"IoBars %08X",(int)PciDevPtr);
ISERIES_PCI_FR(PciFrBuffer);
}
for(BarNumber = 0; BarNumber <= PCI_ROM_RESOURCE; ++BarNumber) {
BarResource = &PciDevPtr->resource[BarNumber];
iSeries_IoMmTable_AllocateEntry(PciDevPtr, BarNumber);
}
}
/*******************************************************************/
/* iSeries_IoMmTable_AllocateEntry */
/*******************************************************************/
/* Adds pci_dev entry in address translation table */
/*******************************************************************/
/* - Allocates the number of entries required in table base on BAR */
/* size. */
/* - This version, allocates top down, starting at 4GB. */
/* - The size is round up to be a multiple of entry size. */
/* - CurrentIndex is decremented to keep track of the last entry. */
/* - Builds the resource entry for allocated BARs. */
/*******************************************************************/
void iSeries_IoMmTable_AllocateEntry(struct pci_dev* PciDevPtr, u32 BarNumber) {
struct resource* BarResource = &PciDevPtr->resource[BarNumber];
int BarSize = BarResource->end - BarResource->start;
u32 BarStartAddr;
u32 BarEndAddr;
/***************************************************************/
/* No space to allocate, skip Allocation. */
/***************************************************************/
if(BarSize == 0) return; /* Quick stage exit */
/***************************************************************/
/* Allocate the table entries needed. */
/***************************************************************/
spin_lock(&iSeriesIoMmTableLock);
while(BarSize > 0) {
iSeries_IoMmTable[iSeries_CurrentIndex] = PciDevPtr;
iSeries_IoBarTable[iSeries_CurrentIndex] = BarNumber;
BarSize -= iSeries_IoMmTable_Entry_Size;
--iSeries_CurrentIndex; /* Next Free entry */
}
spin_unlock(&iSeriesIoMmTableLock);
BarStartAddr = iSeries_IoMmTable_Entry_Size*(iSeries_CurrentIndex+1);
BarEndAddr = BarStartAddr + (u32)(BarResource->end - BarResource->start);
/***************************************************************/
/* Build Resource info */
/***************************************************************/
BarResource->name = iSeriesPciIoText;
BarResource->start = (long)BarStartAddr;
BarResource->end = (long)BarEndAddr;
/***************************************************************/
/* Tracing */
/***************************************************************/
if(PciTraceFlag > 0) {
printk("PCI: BarAloc %04X-%08X-%08X\n",iSeries_CurrentIndex+1,(int)BarStartAddr, (int)BarEndAddr);
sprintf(PciFrBuffer,"IoMmAloc %04X-%08X-%08X",
iSeries_CurrentIndex+1,(int)BarStartAddr, (int)BarEndAddr);
ISERIES_PCI_FR(PciFrBuffer);
}
}
/*******************************************************************/
/* Translates an I/O Memory address to pci_dev* */
/*******************************************************************/
struct pci_dev* iSeries_xlateIoMmAddress(u32* IoAddress) {
int PciDevIndex;
struct pci_dev* PciDevPtr;
PciDevIndex = (u32)IoAddress/iSeries_IoMmTable_Entry_Size;
PciDevPtr = iSeries_IoMmTable[PciDevIndex];
if(PciDevPtr == 0) {
printk("PCI: Invalid I/O Address: 0x%08X\n",(int)IoAddress);
sprintf(PciFrBuffer,"Invalid MMIO Address 0x%08X",(int)IoAddress);
ISERIES_PCI_FR(PciFrBuffer);
}
return PciDevPtr;
}
/************************************************************************/
/* Returns the Bar number of Address */
/************************************************************************/
int iSeries_IoMmTable_Bar(u32 *IoAddress) {
int BarIndex = (u32)IoAddress/iSeries_IoMmTable_Entry_Size;
int BarNumber = iSeries_IoBarTable[BarIndex];
return BarNumber;
}
/************************************************************************/
/* Return the Bar Base Address or 0. */
/************************************************************************/
u32* iSeries_IoMmTable_BarBase(u32 *IoAddress) {
u32 BaseAddr = -1;
pciDev* PciDev = iSeries_xlateIoMmAddress(IoAddress);
if(PciDev != 0) {
int BarNumber = iSeries_IoMmTable_Bar(IoAddress);
if(BarNumber != -1) {
BaseAddr = (&PciDev->resource[BarNumber])->start;
}
}
return (u32*)BaseAddr;
}
/************************************************************************/
/* Return the Bar offset within the Bar Space */
/* Note: Assumes that address is valid. */
/************************************************************************/
u32 iSeries_IoMmTable_BarOffset(u32* IoAddress) {
u32 BaseAddr = (u32)iSeries_IoMmTable_BarBase(IoAddress);
return (u32)IoAddress-BaseAddr;
}
/************************************************************************/
/* Return 0 if Address is valid I/O Address */
/************************************************************************/
int iSeries_Is_IoMmAddress(unsigned long IoAddress) {
if( iSeries_IoMmTable_Bar((u32*)IoAddress) == -1) return 1;
else return 0;
}
/************************************************************************/
/* Helper Methods to get TableSize and TableSizeEntry. */
/************************************************************************/
u32 iSeries_IoMmTable_TableEntrySize(void) { return iSeries_IoMmTable_Entry_Size; }
u32 iSeries_IoMmTable_TableSize(void) { return iSeries_IoMmTable_Size; }
#ifndef _ISERIES_IOMMTABLE_H
#define _ISERIES_IOMMTABLE_H
/************************************************************************/
/* File iSeries_IoMmTable.h created by Allan Trautman on Dec 12 2001. */
/************************************************************************/
/* Interfaces for the write/read Io address translation table. */
/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created December 12, 2000 */
/* End Change Activity */
/************************************************************************/
/************************************************************************/
/* iSeries_IoMmTable_Initialize */
/************************************************************************/
/* - Initalizes the Address Translation Table and get it ready for use. */
/* Must be called before any client calls any of the other methods. */
/* */
/* Parameters: None. */
/* */
/* Return: None. */
/************************************************************************/
extern void iSeries_IoMmTable_Initialize(void);
/************************************************************************/
/* iSeries_allocateDeviceBars */
/************************************************************************/
/* - Allocates ALL pci_dev BAR's and updates the resources with the BAR */
/* value. BARS with zero length will not have the resources. The */
/* HvCallPci_getBarParms is used to get the size of the BAR space. */
/* It calls as400_IoMmTable_AllocateEntry to allocate each entry. */
/* */
/* Parameters: */
/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */
/* I/O Address. */
/* */
/* Return: */
/* The pci_dev I/O resources updated with pseudo I/O Addresses. */
/************************************************************************/
extern void iSeries_allocateDeviceBars(struct pci_dev* Device);
/************************************************************************/
/* iSeries_IoMmTable_AllocateEntry */
/************************************************************************/
/* - Allocates(adds) the pci_dev entry in the Address Translation Table */
/* and updates the Resources for the device. */
/* */
/* Parameters: */
/* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */
/* I/O Address. */
/* */
/* BarNumber = Which Bar to be allocated. */
/* */
/* Return: */
/* The pseudo I/O Address in the resources that will map to the */
/* pci_dev on iSeries_xlateIoMmAddress call. */
/************************************************************************/
extern void iSeries_IoMmTable_AllocateEntry(struct pci_dev* Device, u32 BarNumber);
/************************************************************************/
/* iSeries_xlateIoMmAddress */
/************************************************************************/
/* - Translates an I/O Memory address to pci_dev that has been allocated*/
/* the psuedo I/O Address. */
/* */
/* Parameters: */
/* IoAddress = I/O Memory Address. */
/* */
/* Return: */
/* A pci_dev pointer to the device mapped to the I/O address. */
/************************************************************************/
extern struct pci_dev* iSeries_xlateIoMmAddress(u32* IoAddress);
/************************************************************************/
/* Helper Methods */
/************************************************************************/
extern int iSeries_IoMmTable_Bar(u32 *IoAddress);
extern u32* iSeries_IoMmTable_BarBase(u32* IoAddress);
extern u32 iSeries_IoMmTable_BarOffset(u32* IoAddress);
extern int iSeries_Is_IoMmAddress(unsigned long address);
/************************************************************************/
/* Helper Methods to get TableSize and TableSizeEntry. */
/************************************************************************/
extern u32 iSeries_IoMmTable_TableEntrySize(void);
extern u32 iSeries_IoMmTable_TableSize(void);
#endif /* _ISERIES_IOMMTABLE_H */
/************************************************************************/
/* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */
/************************************************************************/
/* This code gets the card location of the hardware */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Feb 2, 2001 */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/types.h>
#include <asm/resource.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/mf.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/iSeries_VpdInfo.h>
#include "iSeries_pci.h"
/************************************************/
/* Size of Bus VPD data */
/************************************************/
#define BUS_VPDSIZE 1024
/************************************************/
/* Bus Vpd Tags */
/************************************************/
#define VpdEndOfDataTag 0x78
#define VpdEndOfAreaTag 0x79
#define VpdIdStringTag 0x82
#define VpdVendorAreaTag 0x84
/************************************************/
/* Mfg Area Tags */
/************************************************/
#define VpdAsmPartNumber 0x504E // "PN"
#define VpdFruFlag 0x4647 // "FG"
#define VpdFruLocation 0x464C // "FL"
#define VpdFruFrameId 0x4649 // "FI"
#define VpdFruPartNumber 0x464E // "FN"
#define VpdFruPowerData 0x5052 // "PR"
#define VpdFruSerial 0x534E // "SN"
#define VpdFruType 0x4343 // "CC"
#define VpdFruCcinExt 0x4345 // "CE"
#define VpdFruRevision 0x5256 // "RV"
#define VpdSlotMapFormat 0x4D46 // "MF"
#define VpdSlotMap 0x534D // "SM"
/************************************************/
/* Structures of the areas */
/************************************************/
struct BusVpdAreaStruct {
u8 Tag;
u8 LowLength;
u8 HighLength;
};
typedef struct BusVpdAreaStruct BusVpdArea;
#define BUS_ENTRY_SIZE 3
struct MfgVpdAreaStruct {
u16 Tag;
u8 TagLength;
};
typedef struct MfgVpdAreaStruct MfgVpdArea;
#define MFG_ENTRY_SIZE 3
struct SlotMapFormatStruct {
u16 Tag;
u8 TagLength;
u16 Format;
};
struct FrameIdMapStruct{
u16 Tag;
u8 TagLength;
u8 FrameId;
};
typedef struct FrameIdMapStruct FrameIdMap;
struct SlotMapStruct {
u8 AgentId;
u8 SecondaryAgentId;
u8 PhbId;
char CardLocation[3];
char Parms[8];
char Reserved[2];
};
typedef struct SlotMapStruct SlotMap;
#define SLOT_ENTRY_SIZE 16
/****************************************************************/
/* Prototypes */
/****************************************************************/
static void iSeries_Parse_Vpd(BusVpdArea*, int, LocationData*);
static void iSeries_Parse_MfgArea(MfgVpdArea*,int, LocationData*);
static void iSeries_Parse_SlotArea(SlotMap*, int, LocationData*);
static void iSeries_Parse_PhbId(BusVpdArea*, int, LocationData*);
/****************************************************************/
/* */
/* */
/* */
/****************************************************************/
LocationData* iSeries_GetLocationData(struct pci_dev* PciDev) {
LocationData* LocationPtr = 0;
BusVpdArea* BusVpdPtr = 0;
int BusVpdLen = 0;
BusVpdPtr = (BusVpdArea*)kmalloc(BUS_VPDSIZE, GFP_KERNEL);
BusVpdLen = HvCallPci_getBusVpd(ISERIES_GET_LPAR_BUS(PciDev->bus->number),REALADDR(BusVpdPtr),BUS_VPDSIZE);
/* printk("PCI: VpdBuffer 0x%08X \n",(int)BusVpdPtr); */
/***************************************************************/
/* Need to set Agent Id, Bus in location info before the call */
/***************************************************************/
LocationPtr = (LocationData*)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL);
LocationPtr->Bus = ISERIES_GET_LPAR_BUS(PciDev->bus->number);
LocationPtr->Board = 0;
LocationPtr->FrameId = 0;
iSeries_Parse_PhbId(BusVpdPtr,BusVpdLen,LocationPtr);
LocationPtr->Card = PCI_SLOT(PciDev->devfn);
strcpy(LocationPtr->CardLocation,"Cxx");
LocationPtr->AgentId = ISERIES_DECODE_DEVICE(PciDev->devfn);
LocationPtr->SecondaryAgentId = 0x10;
/* And for Reference only. */
LocationPtr->LinuxBus = PciDev->bus->number;
LocationPtr->LinuxDevFn = PciDev->devfn;
/***************************************************************/
/* Any data to process? */
/***************************************************************/
if(BusVpdLen > 0) {
iSeries_Parse_Vpd(BusVpdPtr, BUS_VPDSIZE, LocationPtr);
}
else {
ISERIES_PCI_FR("No VPD Data....");
}
kfree(BusVpdPtr);
return LocationPtr;
}
/****************************************************************/
/* */
/****************************************************************/
void iSeries_Parse_Vpd(BusVpdArea* VpdData,int VpdLen, LocationData* LocationPtr) {
MfgVpdArea* MfgVpdPtr = 0;
int BusTagLen = 0;
BusVpdArea* BusVpdPtr = VpdData;
int BusVpdLen = VpdLen;
/*************************************************************/
/* Make sure this is what I think it is */
/*************************************************************/
if(BusVpdPtr->Tag != VpdIdStringTag) { /*0x82 */
ISERIES_PCI_FR("Not 0x82 start.");
return;
}
BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength;
BusTagLen += BUS_ENTRY_SIZE;
BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen);
BusVpdLen -= BusTagLen;
/*************************************************************/
/* Parse the Data */
/*************************************************************/
while(BusVpdLen > 0 ) {
BusTagLen = (BusVpdPtr->HighLength*256)+BusVpdPtr->LowLength;
/*********************************************************/
/* End of data Found */
/*********************************************************/
if(BusVpdPtr->Tag == VpdEndOfAreaTag) {
BusVpdLen = 0; /* Done, Make sure */
}
/*********************************************************/
/* Was Mfg Data Found */
/*********************************************************/
else if(BusVpdPtr->Tag == VpdVendorAreaTag) {
MfgVpdPtr = (MfgVpdArea*)((u32)BusVpdPtr+BUS_ENTRY_SIZE);
iSeries_Parse_MfgArea(MfgVpdPtr,BusTagLen,LocationPtr);
}
/********************************************************/
/* On to the next tag. */
/********************************************************/
if(BusVpdLen > 0) {
BusTagLen += BUS_ENTRY_SIZE;
BusVpdPtr = (BusVpdArea*)((u32)BusVpdPtr+BusTagLen);
BusVpdLen -= BusTagLen;
}
}
}
/*****************************************************************/
/* Parse the Mfg Area */
/*****************************************************************/
void iSeries_Parse_MfgArea(MfgVpdArea* VpdDataPtr,int VpdLen, LocationData* LocationPtr) {
SlotMap* SlotMapPtr = 0;
u16 SlotMapFmt = 0;
int MfgTagLen = 0;
MfgVpdArea* MfgVpdPtr = VpdDataPtr;
int MfgVpdLen = VpdLen;
/*************************************************************/
/* Parse Mfg Data */
/*************************************************************/
while(MfgVpdLen > 0) {
MfgTagLen = MfgVpdPtr->TagLength;
if (MfgVpdPtr->Tag == VpdFruFlag) {} /* FG */
else if(MfgVpdPtr->Tag == VpdFruSerial) {} /* SN */
else if(MfgVpdPtr->Tag == VpdAsmPartNumber){} /* PN */
/*********************************************************/
/* Frame ID */
/*********************************************************/
if(MfgVpdPtr->Tag == VpdFruFrameId) { /* FI */
LocationPtr->FrameId = ((FrameIdMap*)MfgVpdPtr)->FrameId;
}
/*********************************************************/
/* Slot Map Format */
/*********************************************************/
else if(MfgVpdPtr->Tag == VpdSlotMapFormat){ /* MF */
SlotMapFmt = ((struct SlotMapFormatStruct*)MfgVpdPtr)->Format;
}
/*********************************************************/
/* Slot Labels */
/*********************************************************/
else if(MfgVpdPtr->Tag == VpdSlotMap){ /* SM */
if(SlotMapFmt == 0x1004) SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE+1);
else SlotMapPtr = (SlotMap*)((u32)MfgVpdPtr+MFG_ENTRY_SIZE);
iSeries_Parse_SlotArea(SlotMapPtr,MfgTagLen, LocationPtr);
}
/*********************************************************/
/* Point to the next Mfg Area */
/* Use defined size, sizeof give wrong answer */
/*********************************************************/
MfgTagLen += MFG_ENTRY_SIZE;
MfgVpdPtr = (MfgVpdArea*)( (u32)MfgVpdPtr + MfgTagLen);
MfgVpdLen -= MfgTagLen;
}
}
/*****************************************************************/
/* Look for "BUS" Tag to set the PhbId. */
/*****************************************************************/
void iSeries_Parse_PhbId(BusVpdArea* VpdData,int VpdLen,LocationData* LocationPtr) {
int PhbId = 0xff; /* Not found flag */
char* PhbPtr = (char*)VpdData+3; /* Skip over 82 tag */
int DataLen = VpdLen;
while(DataLen > 0) {
if(*PhbPtr == 'B' && *(PhbPtr+1) == 'U' && *(PhbPtr+2) == 'S') {
if(*(PhbPtr+3) == ' ') PhbPtr += 4;/* Skip white spac*/
else PhbPtr += 3;
if (*PhbPtr == '0') PhbId = 0; /* Don't convert, */
else if(*PhbPtr == '1') PhbId = 1; /* Sanity check */
else if(*PhbPtr == '2') PhbId = 2; /* values */
DataLen = 0; /* Exit loop. */
}
++PhbPtr;
--DataLen;
}
LocationPtr->PhbId = PhbId;
}
/*****************************************************************/
/* Parse the Slot Area */
/*****************************************************************/
void iSeries_Parse_SlotArea(SlotMap* MapPtr,int MapLen, LocationData* LocationPtr) {
int SlotMapLen = MapLen;
SlotMap* SlotMapPtr = MapPtr;
/*************************************************************/
/* Parse Slot label until we find the one requrested */
/*************************************************************/
while(SlotMapLen > 0) {
if(SlotMapPtr->AgentId == LocationPtr->AgentId &&
SlotMapPtr->SecondaryAgentId == LocationPtr->SecondaryAgentId) {
/*****************************************************/
/* If Phb wasn't found, grab the first one found. */
/*****************************************************/
if(LocationPtr->PhbId == 0xff) LocationPtr->PhbId = SlotMapPtr->PhbId;
if( SlotMapPtr->PhbId == LocationPtr->PhbId ) {
/*****************************************************/
/* Found what we were looking for, extract the data. */
/*****************************************************/
memcpy(&LocationPtr->CardLocation,&SlotMapPtr->CardLocation,3);
LocationPtr->CardLocation[3] = 0; /* Null terminate*/
SlotMapLen = 0; /* We are done */
}
}
/*********************************************************/
/* Point to the next Slot */
/* Use defined size, sizeof may give wrong answer */
/*********************************************************/
SlotMapLen -= SLOT_ENTRY_SIZE;
SlotMapPtr = (SlotMap*)((u32)SlotMapPtr+SLOT_ENTRY_SIZE);
}
}
/************************************************************************/
/* Formats the device information. */
/* - Pass in pci_dev* pointer to the device. */
/* - Pass in buffer to place the data. Danger here is the buffer must */
/* be as big as the client says it is. Should be at least 128 bytes.*/
/* Return will the length of the string data put in the buffer. */
/* Format: */
/* PCI: Bus 0, Device 26, Vendor 0x12AE iSeries: Bus 2, Device 34, Fr*/
/* ame 1, Card C10 Ethernet controller */
/************************************************************************/
int iSeries_Device_Information(struct pci_dev* PciDev,char* Buffer, int BufferSize) {
LocationData* LocationPtr; /* VPD Information */
char* BufPtr = Buffer;
int LineLen = 0;
if(BufferSize >= 128) {
LineLen = sprintf(BufPtr+LineLen,"PCI: Bus%3d, Device%3d, Vendor %04X ",
PciDev->bus->number, PCI_SLOT(PciDev->devfn),PciDev->vendor);
LocationPtr = iSeries_GetLocationData(PciDev);
LineLen += sprintf(BufPtr+LineLen,"iSeries: Bus%3d, Device%3d, Frame%3d, Card %4s ",
LocationPtr->Bus,LocationPtr->AgentId,
LocationPtr->FrameId,LocationPtr->CardLocation);
kfree(LocationPtr);
if(pci_class_name(PciDev->class >> 8) == 0) {
LineLen += sprintf(BufPtr+LineLen,"0x%04X ",(int)(PciDev->class >> 8));
}
else {
LineLen += sprintf(BufPtr+LineLen,"%s",pci_class_name(PciDev->class >> 8) );
}
}
return LineLen;
}
/************************************************************************/
/* This module supports the iSeries PCI bus device detection */
/* Copyright (C) 20yy <Robert L Holtorf> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, December 13, 2000 by Wayne Holm */
/* End Change Activity */
/************************************************************************/
#include <linux/pci.h>
#include <linux/ide.h>
#include <asm/pci-bridge.h>
#include <asm/iSeries/HvCallXm.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/iSeries_fixup.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/iSeries_irq.h>
#include <asm/iSeries/iSeries_dma.h>
#include <asm/iSeries/iSeries_VpdInfo.h>
#include <asm/iSeries/iSeries_pci.h>
#include "iSeries_IoMmTable.h"
#include "iSeries_pci.h"
unsigned int __init iSeries_scan_slot(struct pci_dev* temp_dev,
u16 hvBus, u8 slotSubBus, u8 maxAgents)
{
u8 hvIdSel, hvFunction, hvAgentId;
u64 hvRc = 0;
u64 noConnectRc = 0xFFFF;
HvAgentId slotAgentId;
int irq;
slotAgentId = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(slotSubBus), ISERIES_GET_FUNCTION_FROM_SUBBUS(slotSubBus));
irq = iSeries_allocate_IRQ(hvBus, 0, slotAgentId);
for (hvIdSel = 1; hvIdSel <= maxAgents; ++hvIdSel) {
/* Connect all functions of any device found. However, only call pci_scan_slot
once for each idsel. pci_scan_slot handles multifunction devices appropriately */
for (hvFunction = 0; hvFunction < 8 && hvRc == 0; ++hvFunction) {
hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction);
hvRc = HvCallXm_connectBusUnit(hvBus, slotSubBus, hvAgentId, irq);
if (hvRc == 0) {
noConnectRc = 0;
HvCallPci_configStore8(hvBus, slotSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the function config space
}
}
if (noConnectRc == 0) {
/* This assumes that the node slot is always on the primary bus! */
temp_dev->devfn = ISERIES_ENCODE_SUBBUS(ISERIES_GET_DEVICE_FROM_SUBBUS(slotSubBus),
ISERIES_GET_FUNCTION_FROM_SUBBUS(slotSubBus),
0);
iSeries_assign_IRQ(irq, hvBus, 0, slotAgentId);
pci_scan_slot(temp_dev);
}
}
return 0;
}
void __init iSeries_fixup_bus(struct pci_bus* bus)
{
struct pci_dev temp_dev;
struct pci_controller* hose;
u16 hvBus;
u8 hvSubBus, hvIdSel, hvFunction, hvAgentId, maxAgents, irq;
u64 hvRc, devInfoRealAdd, bridgeInfoRealAdd;
struct HvCallPci_DeviceInfo* devInfo;
struct HvCallPci_BridgeInfo* bridgeInfo;
u64 noConnectRc = 0xFFFF;
hose = (struct pci_controller*)(bus->sysdata);
/* Get the hv bus number from the hose arch_data */
hvBus = ISERIES_GET_HOSE_HV_BUSNUM(hose);
/* Initialize the global bus number map for this bus */
iSeries_GlobalBusMap[bus->number][_HVBUSNUMBER_] = hvBus;
iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_] = 0;
maxAgents = 7;
/* If not a primary bus, set the hypervisor subBus number of this bus into the map */
if (bus->parent != NULL) {
bridgeInfo = kmalloc(sizeof(*bridgeInfo), GFP_KERNEL);
/* Perform linux virtual address to iSeries PLIC real address translation */
bridgeInfoRealAdd = virt_to_absolute((u32)bridgeInfo);
bridgeInfoRealAdd = bridgeInfoRealAdd | 0x8000000000000000;
/* Find the Hypervisor address of the bridge which created this bus... */
if (ISERIES_IS_SUBBUS_ENCODED_IN_DEVFN(bus->self->devfn)) { // This assumes that the bus passed to this function is connected to an EADS slot. The subbus encoding algorithm only applies to bridge cards attached directly to EADS. Future TODO is to allow for n levels of bridging.
hvSubBus = ISERIES_DEVFN_DECODE_SUBBUS(bus->self->devfn);
hvIdSel = 1;
}
else {
/* The hose arch_data needs to map Linux bus number -> PHB bus/subBus number
Could also use a global table, might be cheaper for multiple PHBs */
// hvSubBus = iSeries_GlobalBusMap[bus->parent->number][_HVSUBBUSNUMBER_];
hvSubBus = 0; // The bridge card devfn is not subbus encoded and is directly attached to the PCI primary bus. Its subbus number is 0 and the card config space is accessed via type 0 config cycles.
hvIdSel = PCI_SLOT(bus->self->devfn);
}
hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, PCI_FUNC(bus->self->devfn));
/* Now we know the HV bus/subbus/agent of the bridge creating this bus,
go get the subbus number from HV */
hvRc = HvCallPci_getBusUnitInfo(hvBus, hvSubBus, hvAgentId,
bridgeInfoRealAdd, sizeof(*bridgeInfo));
if (hvRc != 0 || bridgeInfo->busUnitInfo.deviceType != HvCallPci_BridgeDevice) {
kfree(bridgeInfo);
// return -1;
}
iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_] = bridgeInfo->subBusNumber;
maxAgents = bridgeInfo->maxAgents;
kfree(bridgeInfo);
}
/* Bus number mapping is complete, from here on cfgIos should result in HvCalls */
hvSubBus = iSeries_GlobalBusMap[bus->number][_HVSUBBUSNUMBER_];
devInfo = kmalloc(sizeof(*devInfo), GFP_KERNEL);
devInfoRealAdd = virt_to_absolute((u32)devInfo);
devInfoRealAdd = devInfoRealAdd | 0x8000000000000000;
memset(&temp_dev, 0, sizeof(temp_dev));
temp_dev.bus = bus;
temp_dev.sysdata = bus->sysdata;
for (hvIdSel=1; hvIdSel <= maxAgents; ++hvIdSel) {
hvRc = HvCallPci_getDeviceInfo(hvBus, hvSubBus, hvIdSel,
devInfoRealAdd, sizeof(*devInfo));
if (hvRc == 0) {
switch(devInfo->deviceType) {
case HvCallPci_NodeDevice:
/* bridgeInfo = kmalloc(HvCallPci_MaxBusUnitInfoSize, GFP_KERNEL); */
bridgeInfo = kmalloc(sizeof(*bridgeInfo), GFP_KERNEL);
/* Loop through each node function to find usable bridges. Scan
the node bridge to create devices. The devices will appear
as if they were connected to the primary bus. */
for (hvFunction=0; hvFunction < 8; ++hvFunction) {
hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction);
irq = 0;
/* Note: hvSubBus should always be 0 here! */
hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq);
if (hvRc == 0) {
bridgeInfoRealAdd = virt_to_absolute((u32)bridgeInfo);
bridgeInfoRealAdd = bridgeInfoRealAdd | 0x8000000000000000;
hvRc = HvCallPci_getBusUnitInfo(hvBus, hvSubBus, hvAgentId,
bridgeInfoRealAdd, sizeof(*bridgeInfo));
if (hvRc == 0 && bridgeInfo->busUnitInfo.deviceType == HvCallPci_BridgeDevice)
{
// scan any card plugged into this slot
iSeries_scan_slot(&temp_dev, hvBus, bridgeInfo->subBusNumber,
bridgeInfo->maxAgents);
}
}
}
kfree(bridgeInfo);
break;
case HvCallPci_MultiFunctionDevice:
/* Attempt to connect each device function, then use architecture independent
pci_scan_slot to build the device(s) */
irq = bus->self->irq; // Assume that this multi-function device is attached to a secondary bus. Get the irq from the dev struct for the bus and pass to Hv on the function connects as well as write it into the interrupt line registers of each function
for (hvFunction=0; hvFunction < 8 && hvRc == 0; ++hvFunction) {
hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, hvFunction);
/* Try to connect each function. */
hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq);
if (hvRc == 0) {
noConnectRc = 0;
HvCallPci_configStore8(hvBus, hvSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the function config space
}
}
if (noConnectRc == 0) {
noConnectRc = 0xFFFF; // Reset to error value in case other multi-function devices are attached to this bus
// Note: using hvIdSel assumes this device is on a secondary bus!
temp_dev.devfn = PCI_DEVFN(hvIdSel, 0);
pci_scan_slot(&temp_dev);
}
break;
case HvCallPci_BridgeDevice:
case HvCallPci_IoaDevice:
/* Single function devices, just try to connect and use pci_scan_slot to
build the device */
irq = bus->self->irq;
hvAgentId = ISERIES_PCI_AGENTID(hvIdSel, 0);
hvRc = HvCallXm_connectBusUnit(hvBus, hvSubBus, hvAgentId, irq);
if (hvRc == 0) {
HvCallPci_configStore8(hvBus, hvSubBus, hvAgentId, PCI_INTERRUPT_LINE, irq); // Store the irq in the interrupt line register of the device config space
// Note: using hvIdSel assumes this device is on a secondary bus!
temp_dev.devfn = PCI_DEVFN(hvIdSel, 0);
pci_scan_slot(&temp_dev);
}
break;
default : /* Unrecognized device */
break;
}; /* end of switch */
}
}
kfree(devInfo);
// return 0;
}
/* Initialize bar space base and limit addresses for each device in the tree */
void __init iSeries_fixup( void ) {
struct pci_dev *dev;
u8 LinuxBus, iSeriesBus, LastBusNumber;
char DeviceInfoBuffer[256];
/*********************************************************/
/* PCI: Allocate Bars space for each device */
/*********************************************************/
pci_for_each_dev(dev) {
iSeries_allocateDeviceBars(dev);
}
/*********************************************************/
/* Create the TCEs for each iSeries bus now that we know */
/* how many buses there are. Need only create TCE for */
/* for each iSeries bus. Multiple linux buses could */
/* be on the same iSeries bus. AHT */
/*********************************************************/
LastBusNumber = 0xFF; /* Invalid */
for( LinuxBus = 0; LinuxBus < 255; ++ LinuxBus) {
iSeriesBus = ISERIES_GET_LPAR_BUS(LinuxBus);
if(iSeriesBus == 0xFF) break; /* Done */
else if(LastBusNumber != iSeriesBus ){ /* New Bus */
create_pci_bus_tce_table(iSeriesBus);
LastBusNumber = iSeriesBus; /* Remember */
}
}
/*********************************************************/
/* List out all the PCI devices found... This will go */
/* into the etc/proc/iSeries/pci info as well.... */
/* This is to help service figure out who is who...... */
/*********************************************************/
pci_for_each_dev(dev) {
struct resource* BarResource = &dev->resource[0];
iSeries_Device_Information(dev,DeviceInfoBuffer,256);
printk("%s\n",DeviceInfoBuffer);
printk("PCI: Bus%3d, Device%3d, %s at 0x%08x, irq %3d\n",
dev->bus->number, PCI_SLOT(dev->devfn),dev->name,(int)BarResource->start,dev->irq);
}
/* */
iSeries_activate_IRQs(); // Unmask all device interrupts for assigned IRQs
}
/************************************************************************/
/* This module supports the iSeries PCI bus interrupt handling */
/* Copyright (C) 20yy <Robert L Holtorf> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, December 13, 2000 by Wayne Holm */
/* End Change Activity */
/************************************************************************/
#include <linux/pci.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/smp.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/bootmem.h>
#include <linux/blk.h>
#include <linux/ide.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/HvCallXm.h>
#include <asm/iSeries/iSeries_irq.h>
#include <asm/iSeries/XmPciLpEvent.h>
hw_irq_controller iSeries_IRQ_handler = {
"iSeries irq controller",
iSeries_startup_IRQ, /* startup */
iSeries_shutdown_IRQ, /* shutdown */
iSeries_enable_IRQ, /* enable */
iSeries_disable_IRQ, /* disable */
NULL, /* ack */
iSeries_end_IRQ, /* end */
NULL /* set_affinity */
};
struct iSeries_irqEntry {
u32 dsa;
struct iSeries_irqEntry* next;
};
struct iSeries_irqAnchor {
u8 valid : 1;
u8 reserved : 7;
u16 entryCount;
struct iSeries_irqEntry* head;
};
struct iSeries_irqAnchor iSeries_irqMap[NR_IRQS];
void iSeries_init_irqMap(int irq);
/* This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c */
void __init iSeries_init_IRQ(void)
{
int i;
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].handler = &iSeries_IRQ_handler;
irq_desc[i].status = 0;
irq_desc[i].status |= IRQ_DISABLED;
irq_desc[i].depth = 1;
iSeries_init_irqMap(i);
}
/* Register PCI event handler and open an event path */
XmPciLpEvent_init();
return;
}
/* Called by iSeries_init_IRQ */
void __init iSeries_init_irqMap(int irq) {
/* Prevent IRQs 0 and 255 from being used. IRQ 0 appears in
uninitialized devices. IRQ 255 appears in the PCI interrupt
line register if a PCI error occurs */
iSeries_irqMap[irq].valid = (irq == 0 || irq == 255)? 0 : 1;
iSeries_irqMap[irq].entryCount = 0;
iSeries_irqMap[irq].head = NULL;
}
/* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot */
int __init iSeries_allocate_IRQ(HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) {
u8 idsel = (deviceId >> 4);
u8 function = deviceId & 0x0F;
int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 254) + 1;
return irq;
}
/* This is called out of iSeries_scan_slot to assign the EADS slot to its IRQ number */
int __init iSeries_assign_IRQ(int irq, HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) {
int rc;
u32 dsa = (busNumber << 16) | (subBusNumber << 8) | deviceId;
struct iSeries_irqEntry* newEntry;
unsigned long flags;
if (irq < 0 || irq >= NR_IRQS)
return -1;
newEntry = kmalloc(sizeof(*newEntry), GFP_KERNEL);
if (newEntry == NULL)
return -ENOMEM;
newEntry->dsa = dsa;
newEntry->next = NULL;
/* Probably not necessary to lock the irq since allocation is only
done during buswalk, but it should not hurt anything except a little
performance */
spin_lock_irqsave(&irq_desc[irq].lock, flags);
if (iSeries_irqMap[irq].valid) {
/* Push the new element onto the irq stack */
newEntry->next = iSeries_irqMap[irq].head;
iSeries_irqMap[irq].head = newEntry;
++iSeries_irqMap[irq].entryCount;
rc = 0;
}
else
rc = -1;
spin_unlock_irqrestore(&irq_desc[irq].lock, flags);
if (rc != 0 && newEntry)
kfree(newEntry);
return rc;
}
/* This is called by iSeries_activate_IRQs */
unsigned int iSeries_startup_IRQ(unsigned int irq) {
struct iSeries_irqEntry* entry;
u32 bus, subBus, deviceId, function, mask;
/* irq should be locked by the caller */
for(entry=iSeries_irqMap[irq].head; entry!=NULL; entry=entry->next) {
bus = (entry->dsa >> 16) & 0xFFFF;
subBus = (entry->dsa >> 8) & 0xFF;
deviceId = entry->dsa & 0xFF;
function = deviceId & 0x0F;
/* Link the IRQ number to the bridge */
HvCallXm_connectBusUnit(bus, subBus, deviceId, irq);
/* Unmask bridge interrupts in the FISR */
mask = 0x01010000 << function;
HvCallPci_unmaskFisr(bus, subBus, deviceId, mask);
}
return 0;
}
/* This is called out of iSeries_fixup to
activate interrupt generation for usable slots */
void __init iSeries_activate_IRQs() {
int irq;
unsigned long flags;
for (irq=0; irq < NR_IRQS; irq++) {
spin_lock_irqsave(&irq_desc[irq].lock, flags);
irq_desc[irq].handler->startup(irq);
spin_unlock_irqrestore(&irq_desc[irq].lock, flags);
}
}
/* this is not called anywhere currently */
void iSeries_shutdown_IRQ(unsigned int irq) {
struct iSeries_irqEntry* entry;
u32 bus, subBus, deviceId, function, mask;
/* irq should be locked by the caller */
for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) {
bus = (entry->dsa >> 16) & 0xFFFF;
subBus = (entry->dsa >> 8) & 0xFF;
deviceId = entry->dsa & 0xFF;
function = deviceId & 0x0F;
/* Invalidate the IRQ number in the bridge */
HvCallXm_connectBusUnit(bus, subBus, deviceId, 0);
/* Mask bridge interrupts in the FISR */
mask = 0x01010000 << function;
HvCallPci_maskFisr(bus, subBus, deviceId, mask);
}
}
/* This will be called by device drivers (via disable_IRQ to disable
INTA in the bridge interrupt status register */
void iSeries_disable_IRQ(unsigned int irq) {
struct iSeries_irqEntry* entry;
u32 bus, subBus, deviceId, mask;
/* The IRQ has already been locked by the caller */
for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) {
bus = (entry->dsa >> 16) & 0xFFFF;
subBus = (entry->dsa >> 8) & 0xFF;
deviceId = entry->dsa & 0xFF;
/* Mask secondary INTA */
mask = 0x80000000;
HvCallPci_maskInterrupts(bus, subBus, deviceId, mask);
}
}
/* This will be called by device drivers (via enable_IRQ to enable
INTA in the bridge interrupt status register */
void iSeries_enable_IRQ(unsigned int irq) {
struct iSeries_irqEntry* entry;
u32 bus, subBus, deviceId, mask;
/* The IRQ has already been locked by the caller */
for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) {
bus = (entry->dsa >> 16) & 0xFFFF;
subBus = (entry->dsa >> 8) & 0xFF;
deviceId = entry->dsa & 0xFF;
/* Unmask secondary INTA */
mask = 0x80000000;
HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask);
}
}
/* Need to define this so ppc_irq_dispatch_handler will NOT call
enable_IRQ at the end of interrupt handling. However, this
does nothing because there is not enough information provided
to do the EOI HvCall. This is done by XmPciLpEvent.c */
void iSeries_end_IRQ(unsigned int irq) {
}
/* File iSeries_ksyms.c created by root on Tue Feb 13 2001. */
/* Change Activity: */
/* End Change Activity */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/iSeries/iSeries_dma.h>
#include <asm/iSeries/mf.h>
#include <asm/iSeries/HvCallSc.h>
#include <asm/iSeries/HvLpEvent.h>
#include <asm/iSeries/Naca.h>
#include <asm/iSeries/iSeries_proc.h>
#include <asm/iSeries/ItLpNaca.h>
#include <asm/iSeries/HvLpConfig.h>
#include <asm/iSeries/iSeries_io.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <asm/iSeries/iSeries_pci.h>
#include <asm/iSeries/iSeries_VpdInfo.h>
#include <asm/delay.h>
EXPORT_SYMBOL(HvLpEvent_registerHandler);
EXPORT_SYMBOL(HvLpEvent_unregisterHandler);
EXPORT_SYMBOL(HvLpEvent_openPath);
EXPORT_SYMBOL(HvLpEvent_closePath);
EXPORT_SYMBOL(HvCall1);
EXPORT_SYMBOL(HvCall2);
EXPORT_SYMBOL(HvCall3);
EXPORT_SYMBOL(HvCall4);
EXPORT_SYMBOL(HvCall5);
EXPORT_SYMBOL(HvCall6);
EXPORT_SYMBOL(HvCall7);
EXPORT_SYMBOL(HvCall0);
EXPORT_SYMBOL(HvCall0Ret16);
EXPORT_SYMBOL(HvCall1Ret16);
EXPORT_SYMBOL(HvCall2Ret16);
EXPORT_SYMBOL(HvCall3Ret16);
EXPORT_SYMBOL(HvCall4Ret16);
EXPORT_SYMBOL(HvCall5Ret16);
EXPORT_SYMBOL(HvCall6Ret16);
EXPORT_SYMBOL(HvCall7Ret16);
EXPORT_SYMBOL(HvLpConfig_getLpIndex_outline);
EXPORT_SYMBOL(virt_to_absolute_outline);
EXPORT_SYMBOL(mf_allocateLpEvents);
EXPORT_SYMBOL(mf_deallocateLpEvents);
EXPORT_SYMBOL(iSeries_proc_callback);
#ifdef CONFIG_PCI
EXPORT_SYMBOL(pci_map_single);
EXPORT_SYMBOL(pci_unmap_single);
EXPORT_SYMBOL(iSeries_Readb);
EXPORT_SYMBOL(iSeries_Readw);
EXPORT_SYMBOL(iSeries_Readl);
EXPORT_SYMBOL(iSeries_Writeb);
EXPORT_SYMBOL(iSeries_Writew);
EXPORT_SYMBOL(iSeries_Writel);
EXPORT_SYMBOL(iSeries_memcpy_fromio);
EXPORT_SYMBOL(iSeries_memcpy_toio);
EXPORT_SYMBOL(iSeries_GetLocationData);
EXPORT_SYMBOL(iSeries_Set_PciTraceFlag);
EXPORT_SYMBOL(iSeries_Get_PciTraceFlag);
EXPORT_SYMBOL(iSeries_Device_Reset_NoIrq);
EXPORT_SYMBOL(iSeries_Device_Reset_Generic);
EXPORT_SYMBOL(iSeries_Device_Reset);
EXPORT_SYMBOL(iSeries_Device_RestoreConfigRegs);
EXPORT_SYMBOL(iSeries_Device_SaveConfigRegs);
EXPORT_SYMBOL(iSeries_Device_ToggleReset);
#endif
/************************************************************************/
/* File iSeries_pci.c created by Allan Trautman on Tue Jan 9 2001. */
/************************************************************************/
/* This code supports the pci interface on the IBM iSeries systems. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Jan 9, 2001 */
/* August, 10, 2001 Added PCI Retry code. */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
/************************************************************************/
/* Arch specific's */
/************************************************************************/
#include <asm/io.h> /* Has Io Instructions. */
#include <asm/iSeries/iSeries_io.h>
#include <asm/pci-bridge.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/mf.h>
#include <asm/iSeries/iSeries_proc.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <asm/iSeries/iSeries_VpdInfo.h>
#include "iSeries_IoMmTable.h"
#include "iSeries_pci.h"
extern int panic_timeout; /* Panic Timeout reference */
/************************************************************************/
/* /proc/iSeries/pci initialization. */
/************************************************************************/
extern void iSeries_pci_proc_init(struct proc_dir_entry *iSeries_proc);
void iSeries_pci_IoError(char* OpCode,iSeries_Device* Device);
/************************************************************************/
/* PCI Config Space Access functions for IBM iSeries Systems */
/* */
/* Modeled after the IBM Python */
/* int pci_read_config_word(struct pci_dev*, int Offset, u16* ValuePtr) */
/************************************************************************/
/* Note: Normal these routines are defined by a macro and branch */
/* table is created and stuffed in the pci_bus structure. */
/* The following is for reference, if it was done this way. However for*/
/* the first time out, the routines are individual coded. */
/************************************************************************/
/* This is the low level architecture depend interface routines. */
/* 1. They must be in specific order, see <linux/pci.h> for base */
/* structure. */
/* 2. The code in not inline here, it calls specific routines in */
/* this module and HvCalls */
/* 3. The common convention is to put a pointer to the structure in */
/* pci_bus->sysdata. */
/* 4. Direct call is the same as in <linux/pci.h */
/************************************************************************/
int iSeries_pci_read_config_byte( struct pci_dev* PciDev, int Offset, u8* ValuePtr) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configLoad8(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, ValuePtr);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"RCB: %02X,%02X,%02X,%04X Rtn: %04X,%02X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, *ValuePtr);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
int iSeries_pci_read_config_word( struct pci_dev* PciDev, int Offset, u16* ValuePtr) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configLoad16(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, ValuePtr);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"RCW: %02X,%02X,%02X,%04X Rtn: %04X,%04X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, *ValuePtr);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
int iSeries_pci_read_config_dword( struct pci_dev* PciDev, int Offset, u32* ValuePtr) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configLoad32(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, ValuePtr);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"RCL: %02X,%02X,%02X,%04X Rtn: %04X,%08X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, *ValuePtr);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
int iSeries_pci_write_config_byte( struct pci_dev* PciDev, int Offset, u8 Value) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configStore8(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"WCB: %02X,%02X,%02X,%04X Rtn: %04X,%02X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, Value);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
int iSeries_pci_write_config_word( struct pci_dev* PciDev, int Offset, u16 Value) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configStore16(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"WCW: %02X,%02X,%02X,%04X Rtn: %04X,%04X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, Value);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
int iSeries_pci_write_config_dword(struct pci_dev* PciDev, int Offset, u32 Value) {
iSeries_Device Device;
build_iSeries_Device(&Device, PciDev);
if(Device.BusNumber == 0xFF) Device.RCode = 0x301; /* Trap out invalid bus. */
else {
Device.RCode = HvCallPci_configStore32(Device.BusNumber, Device.SubBus, Device.DevFn, Offset, Value);
if(Device.RCode != 0 || PciTraceFlag > 0) {
sprintf(PciFrBuffer,"WCL: %02X,%02X,%02X,%04X Rtn: %04X,%08X",
Device.BusNumber, Device.SubBus, Device.DevFn, Offset,
Device.RCode, Value);
ISERIES_PCI_FR(PciFrBuffer);
if(Device.RCode != 0 ) printk("PCI: I/O Error %s",PciFrBuffer);
}
}
return Device.RCode;
}
/************************************************************************/
/* Branch Table */
/************************************************************************/
struct pci_ops iSeries_pci_ops = {
iSeries_pci_read_config_byte,
iSeries_pci_read_config_word,
iSeries_pci_read_config_dword,
iSeries_pci_write_config_byte,
iSeries_pci_write_config_word,
iSeries_pci_write_config_dword
};
/************************************************************************/
/* Dump the device info into the Flight Recorder */
/* Call should have 3 char text to prefix FR Entry */
/************************************************************************/
void iSeries_DumpDevice(char* Text, iSeries_Device* Device) {
sprintf(PciFrBuffer,"%s:%02X%02X%02X %04X",Text,Device->BusNumber,Device->SubBus,Device->DevFn,Device->RCode);
ISERIES_PCI_FR(PciFrBuffer);
if(Device->BarNumber != 0xFF) {
sprintf(PciFrBuffer,"BAR:%02X %04X ",Device->BarNumber,Device->BarOffset);
ISERIES_PCI_FR(PciFrBuffer);
}
if(Device->RCode != 0) {
/*****************************************************************/
/* PCI I/O ERROR RDL: Bus: 0x01 Device: 0x06 ReturnCode: 0x000B */
/*****************************************************************/
printk("PCI: I/O ERROR %s: Bus: 0x%02X Device: 0x%02X ReturnCode: 0x%04X\n",
Text,Device->PciDevPtr->bus->number,Device->PciDevPtr->devfn,Device->RCode);
}
}
int IoCounts = 0;
int RetryCounts = 0;
int IoRetry = 0;
/************************************************************************
* Check Return Code
* -> On Failure, print and log information.
* Increment Retry Count, if exceeds max, panic partition.
* -> If in retry, print and log success
************************************************************************
* PCI: ReadL: I/O Error( 0): 0x1234
* PCI: Device..: 17:38.10 Bar: 0x00 Offset 0018
* PCI: Device..: 17:38.10 Retry( 1) Operation ReadL:
* PCI: Retry Successful on Device: 17:38.10
************************************************************************/
int CheckReturnCode(char* TextHdr, iSeries_Device* Device) {
++IoCounts;
if(Device->RCode != 0) {
sprintf(PciFrBuffer,"%s: I/O Error(%2d): 0x%04X\n", TextHdr, IoRetry,Device->RCode);
ISERIES_PCI_FR(PciFrBuffer);
printk("PCI: %s",PciFrBuffer);
sprintf(PciFrBuffer,"Device..: %02X:%02X.%02X Bar: 0x%02X Offset %04X\n",
Device->BusNumber, Device->SubBus, Device->DevFn,
Device->BarNumber, Device->BarOffset);
ISERIES_PCI_FR(PciFrBuffer);
printk("PCI: %s",PciFrBuffer);
/* Bump the retry and check for max. */
++RetryCounts;
++IoRetry;
/* Retry Count exceeded or RIO Bus went down. */
if(IoRetry > 7 || Device->RCode == 0x206) {
mf_displaySrc(0xB6000103);
panic_timeout = 0;
panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled.");
}
else {
sprintf(PciFrBuffer,"Device..: %02X:%02X.%02X Retry(%2d) Operation: %s\n",
Device->BusNumber, Device->SubBus, Device->DevFn,
IoRetry,TextHdr);
ISERIES_PCI_FR(PciFrBuffer);
printk("PCI: %s\n",PciFrBuffer);
}
}
else {
if(IoRetry > 0 ) {
sprintf(PciFrBuffer,"Retry Successful on Device: %02X:%02X.%02X\n",
Device->BusNumber, Device->SubBus, Device->DevFn);
ISERIES_PCI_FR(PciFrBuffer);
printk("PCI: %s\n",PciFrBuffer);
}
}
return Device->RCode;
}
/************************************************************************/
/* Read MM I/O Instructions for the iSeries */
/* On MM I/O error, all ones are returned and iSeries_pci_IoError is cal*/
/* else, data is returned in big Endian format. */
/************************************************************************/
/* iSeries_Readb = Read Byte ( 8 bit) */
/* iSeries_Readw = Read Word (16 bit) */
/* iSeries_Readl = Read Long (32 bit) */
/************************************************************************/
u8 iSeries_Readb(u32* IoAddress) {
iSeries_Device Device;
u8 IoData;
u8 Data = -1;
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
do {
Device.RCode = HvCallPci_barLoad8 (Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, &IoData);
Data = IoData;
if(Device.RCode != 0) CheckReturnCode("ReadB",&Device);
} while(Device.RCode != 0);
}
return Data;
}
u16 iSeries_Readw(u32* IoAddress) {
iSeries_Device Device;
u16 IoData;
u16 Data = -1;
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
do {
Device.RCode = HvCallPci_barLoad16(Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, &IoData);
Data = swab16(IoData);
if(Device.RCode != 0) CheckReturnCode("ReadW",&Device);
} while(Device.RCode != 0);
}
return Data;
}
u32 iSeries_Readl(u32* IoAddress) {
iSeries_Device Device;
u32 Data = -1;
u32 IoData;
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
IoRetry = 0;
do {
Device.RCode = HvCallPci_barLoad32(Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, &IoData);
Data = swab32(IoData);
if(Device.RCode != 0) CheckReturnCode("ReadL",&Device);
} while(Device.RCode != 0);
}
return Data;
}
/************************************************************************/
/* Write MM I/O Instructions for the iSeries */
/* On MM I/O error, iSeries_pci_IoError is called */
/* Data is in big Endian format. */
/************************************************************************/
/* iSeries_Writeb = Write Byte (8 bit) */
/* iSeries_Writew = Write Word(16 bit) */
/* iSeries_Writel = Write Long(32 bit) */
/************************************************************************/
void iSeries_Writeb(u8 Data,u32* IoAddress) {
iSeries_Device Device;
u8 IoData = Data;
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
do {
Device.RCode = HvCallPci_barStore8 (Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, IoData);
if(Device.RCode != 0) CheckReturnCode("WrteB",&Device);
} while(Device.RCode != 0);
}
}
void iSeries_Writew(u16 Data,u32* IoAddress) {
iSeries_Device Device;
u16 IoData = swab16(Data);
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
do {
Device.RCode = HvCallPci_barStore16(Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, IoData);
if(Device.RCode != 0) CheckReturnCode("WrteW",&Device);
} while(Device.RCode != 0);
}
}
void iSeries_Writel(u32 Data,u32* IoAddress) {
iSeries_Device Device;
u32 IoData = swab32(Data);
IoRetry = 0;
if(build_iSeries_Device_From_IoAddress(&Device, IoAddress) == 0) {
do {
Device.RCode = HvCallPci_barStore32(Device.BusNumber, Device.SubBus,Device.DevFn,
Device.BarNumber, Device.BarOffset, IoData);
if(Device.RCode != 0) CheckReturnCode("WrteL",&Device);
} while(Device.RCode != 0);
}
}
/************************************************************************/
/* iSeries I/O Remap */
/* -> Check to see if one of ours */
/* -> If not, return null to help find the bug. */
/************************************************************************/
void* iSeries_ioremap (unsigned long offset, unsigned long size) {
if(iSeries_xlateIoMmAddress((u32*)offset) != 0) {
return (void*)offset;
}
else {
return NULL;
}
}
/************************************************************************/
/* Routine to build the iSeries_Device for the pci device. */
/************************************************************************/
void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr) {
Device->PciDevPtr = DevPtr;
Device->BusNumber = ISERIES_GET_LPAR_BUS(DevPtr->bus->number);
if(ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number) == 0) {
Device->SubBus = ISERIES_DEVFN_DECODE_SUBBUS(DevPtr->devfn);
Device->DevFn = 0x10 | ISERIES_DECODE_FUNCTION(DevPtr->devfn);
}
else {
Device->SubBus = ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number);
Device->DevFn = ISERIES_44_FORMAT(DevPtr->devfn);
}
Device->BarNumber = 0xFF;
Device->BarOffset = 0;
Device->RCode = 0;
}
/************************************************************************/
/* Routine to build the iSeries_Device for the IoAddress. */
/************************************************************************/
int build_iSeries_Device_From_IoAddress(iSeries_Device* Device, u32* IoAddress) {
Device->PciDevPtr = iSeries_xlateIoMmAddress(IoAddress);
if(Device->PciDevPtr != 0) { /* Valid pci_dev? */
build_iSeries_Device(Device,Device->PciDevPtr);
Device->BarNumber = iSeries_IoMmTable_Bar(IoAddress);
if( Device->BarNumber != 0xFF) {
Device->BarOffset = iSeries_IoMmTable_BarOffset(IoAddress);
return 0;
}
else {
sprintf(PciFrBuffer,"I/O BAR Address: 0x%08X",(int)IoAddress);
ISERIES_PCI_FR(PciFrBuffer);
printk("PCI: Invalid I/O Address 0x%08X\n",(int)IoAddress);
return -1;
}
}
printk("PCI: Invalid I/O Address 0x%08X\n",(int)IoAddress);
return -1;
}
/************************************************************************/
/* Returns the iSeries bus value */
/************************************************************************/
u8 iSeries_Get_Bus(struct pci_dev* DevPtr) {
return iSeries_GlobalBusMap[DevPtr->bus->number][_HVBUSNUMBER_];
}
/************************************************************************/
/* Returns the iSeries subbus value */
/************************************************************************/
u8 iSeries_Get_SubBus(struct pci_dev* DevPtr) {
u8 SubBus = iSeries_GlobalBusMap[DevPtr->bus->number][_HVSUBBUSNUMBER_];
if (SubBus == 0xFF) SubBus = 0xFF;
else if(SubBus == 0) SubBus = ISERIES_DEVFN_DECODE_SUBBUS(DevPtr->devfn);
else SubBus = ISERIES_GET_LPAR_SUBBUS(DevPtr->bus->number);
return SubBus;
}
/************************************************************************/
/* Returns the iSeries Device and Function Number */
/************************************************************************/
u8 iSeries_Get_DevFn(struct pci_dev* DevPtr) {
u8 SubBus = iSeries_GlobalBusMap[DevPtr->bus->number][_HVSUBBUSNUMBER_];
u8 DevFn;
if (SubBus == 0xFF) DevFn = 0;
else if(SubBus == 0) DevFn = 0x10 | ISERIES_DECODE_FUNCTION(DevPtr->devfn);
else DevFn = ISERIES_44_FORMAT(DevPtr->devfn);
return DevFn;
}
/************************************************************************/
/* This is provides the mapping from the Linux bus and devfn to ISeries */
/* Bus and Subbus. */
/* Initialize to all FFs, translations will fail if FF entry found. */
/************************************************************************/
u8 iSeries_GlobalBusMap[256][2]; /* Global Bus Mapping */
void __init iSeries_Initialize_GlobalBusMap(void) {
int Index;
for(Index = 0; Index < 256; ++Index) {
iSeries_GlobalBusMap[Index][_HVBUSNUMBER_] = 0xFF;
iSeries_GlobalBusMap[Index][_HVSUBBUSNUMBER_] = 0xFF;
}
ISERIES_PCI_FR("IntGlobalBusMap");
}
/************************************************************************/
/* Create the Flight Recorder */
/************************************************************************/
FlightRecorder PciFlightRecorder; /* Pci Flight Recorder */
FlightRecorder* PciFr; /* Pointer to Fr */
char PciFrBufferData[128]; /* Working buffer */
char* PciFrBuffer; /* Pointer to buffer */
int PciTraceFlag = 0; /* Trace Level Flag */
struct pci_dev* PciDeviceTrace; /* Device Tracing */
void __init iSeries_Initialize_FlightRecorder(void) {
PciFr = &PciFlightRecorder;
PciFrBuffer = &PciFrBufferData[0];
iSeries_Fr_Initialize(PciFr, "PciFlightRecord");
ISERIES_PCI_FR("August 10,2001.");
PciTraceFlag = 0;
}
/************************************************************************/
/* Function to turn on and off the Flight Recorder Trace Flag */
/************************************************************************/
int iSeries_Set_PciTraceFlag(int TraceFlag) {
int TempFlag = PciTraceFlag;
PciTraceFlag = TraceFlag;
return TempFlag;
}
int iSeries_Get_PciTraceFlag(void) {
return PciTraceFlag;
}
void iSeries_Set_PciFilter(struct pci_dev* PciDevice) {
PciDeviceTrace = PciDevice;
}
/************************************************************************/
/* Initialize the I/O Tables and maps */
/************************************************************************/
void __init iSeries_pci_Initialize(void) {
iSeries_Initialize_FlightRecorder(); /* Flight Recorder 1st. */
iSeries_Initialize_GlobalBusMap(); /* Global Bus Map */
iSeries_IoMmTable_Initialize(); /* Io Translate table. */
iSeries_proc_callback(&iSeries_pci_proc_init);
iSeries_Set_PciErpFlag(4); /* Erp Level set to 4 */
}
/************************************************************************/
/* Erp level when PCI I/O Errors are detected. */
/* 0 = Be quiet about the errors. */
/* 1 >= Log error information and continue on. */
/* 2 = Printk the error information and continue on. */
/* 3 = Printk the error information and put task to sleep. */
/* 4 = Printk the error information and panic the kernel with no reboo*/
/************************************************************************/
int iSeries_pci_ErpLevel = 0;
/************************************************************************/
/* Allows clients to set the Erp State */
/************************************************************************/
int iSeries_Set_PciErpFlag(int ErpFlag) {
int SavedState = iSeries_pci_ErpLevel;
printk("PCI: PciERPFlag set to %d.\n", ErpFlag);
iSeries_pci_ErpLevel = ErpFlag;
return SavedState;
}
extern int panic_timeout; /* Panic Timeout reference */
/************************************************************************/
/* Fatal I/O Error, crash the system. */
/* PCI: Fatal I/O Error, Device 00/00, Error 0x0000, Status Reg 0x0000 */
/* PCI: Kernel Panic called with reboot disabled. */
/************************************************************************/
void iSeries_pci_IoError(char* OpCode,iSeries_Device* Device) {
char DeviceInfo[128];
char ErrorInfo[128];
struct pci_dev* PciDev = Device->PciDevPtr;
sprintf(ErrorInfo,"I/O Error Detected. Op: %s, Bus%3d, Device%3d Error: 0x%04X\n",
OpCode,PciDev->bus->number, PCI_SLOT(PciDev->devfn),Device->RCode);
iSeries_Device_Information(PciDev,DeviceInfo,128);
/* Log the error in the flight recorder */
if(iSeries_pci_ErpLevel > 0) {
if( Device->RCode != 0x0102) { /* Previous error */
ISERIES_PCI_FR(ErrorInfo);
ISERIES_PCI_FR(DeviceInfo);
}
}
if(iSeries_pci_ErpLevel > 1) {
printk("PCI: %s",ErrorInfo);
printk("PCI: %s\n",DeviceInfo);
}
if(iSeries_pci_ErpLevel == 3) {
printk("PCI: Current process 0x%08X put to sleep for debug.\n",current->pid);
{
DECLARE_WAITQUEUE(WaitQueue, current);
add_wait_queue(&current->wait_chldexit,&WaitQueue);
current->state = TASK_INTERRUPTIBLE;
schedule();
current->state = TASK_RUNNING;
remove_wait_queue(&current->wait_chldexit,&WaitQueue);
}
}
else if(iSeries_pci_ErpLevel == 4) {
mf_displaySrc(0xB6000103);
panic_timeout = 0; /* Don't reboot. */
/* printk("PCI: Hardware I/O Error, SRC B6000103, Kernel Panic called.\n");*/
/* panic("Automatic Reboot Disabled.") */
panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled.");
}
}
/************************************************************************/
/* I/0 Memory copy MUST use mmio commands on iSeries */
/************************************************************************/
void* iSeries_memcpy_toio(void *dest, void *source, int n)
{
char *dst = dest;
char *src = source;
while (n--) {
writeb (*src++, dst++);
}
return dest;
}
void* iSeries_memcpy_fromio(void *dest, void *source, int n)
{
char *dst = dest;
char *src = source;
while (n--)
*dst++ = readb (src++);
return dest;
}
#ifdef CONFIG_PPC_ISERIES
#ifndef _ISERIES_PCI_H
#define _ISERIES_PCI_H
/************************************************************************/
/* File iSeries_pci.h created by Allan Trautman on Tue Jan 9 2001. */
/************************************************************************/
/* Define some useful macros for the iseries pci routines. */
/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created December 28, 2000 */
/* Converted to iseries_pci.h Jan 25, 2001 */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <asm/iSeries/iSeries_pci.h>
/************************************************************************************/
/* Define some useful macros. */
/* These macros started with Wayne, Al renamed and refined. */
/************************************************************************************/
/* Encodes SubBus address(seddddfff), Works only for bridges under EADS 1 and 2. */
/************************************************************************************/
/* #define ISERIES_ENCODE_SUBBUS(eads, bridge, device) \
(0x80 | ((eads-1 & 0x01) << 6) | ((bridge & 0x0F) << 3) | (device & 0x07)) */
#define ISERIES_ENCODE_SUBBUS(e, ef, df) (((0x80 | ((e&0x02)<<5) | ((ef & 0x07)<<3)) & 0xF8) | (df & 0x03)) // Al - Please Review
/************************************************************************************/
/* Combines IdSel and Function into Iseries 4.4 format */
/* For Linux, see PCI_DEVFN(slot,func) in include/linux/pci.h */
/************************************************************************************/
// #define ISERIES_PCI_AGENTID(idsel,func) ((idsel & 0x0F) << 4) | (func & 0x07)
#define ISERIES_PCI_AGENTID(idsel,func) (((idsel & 0x0F) << 4) | (func & 0x0F)) // Al - Please Review
/************************************************************************************/
/* Converts DeviceFunction from Linux 5.3(dddddfff) to Iseries 4.4(dddd0fff) */
/* Converts DeviceFunction from Iseries 4.4(dddd0fff) to Linux 5.3(dddddfff) */
/************************************************************************************/
#define ISERIES_44_FORMAT(devfn53) (((devfn53 & 0xF8) << 1) | (devfn53 & 0x07))
#define ISERIES_53_FORMAT(devfn44) (((devfn44 & 0xF0) >> 1) | (devfn44 & 0x07))
/************************************************************************************/
/* Tests for encoded subbus. */
/************************************************************************************/
#define ISERIES_IS_SUBBUS_ENCODED_IN_DEVFN(devfn) ((devfn & 0x80) == 0x80)
/************************************************************************************/
/* Decodes the Iseries subbus to devfn, ONLY Works for bus 0!! Use Table lookup. */
/************************************************************************************/
/* #define ISERIES_DEVFN_DECODE_SUBBUS(devfn) \
((((devfn & 0x40) >> 1) + 0x20) | ((devfn >> 1) & 0x1C)) */
#define ISERIES_DEVFN_DECODE_SUBBUS(devfn) (((((devfn >> 6 ) & 0x1) + 1) << 5) | (((devfn >> 3) & 0x7) << 2)) // Al - Please Review
/************************************************************************************/
/* Decodes Linux DevFn to Iseries DevFn, bridge device, or function. */
/* For Linux, see PCI_SLOT and PCI_FUNC in include/linux/pci.h */
/************************************************************************************/
#define ISERIES_DECODE_DEVFN(linuxdevfn) (((linuxdevfn & 0x71) << 1) | (linuxdevfn & 0x07))
#define ISERIES_DECODE_DEVICE(linuxdevfn) (((linuxdevfn & 0x38) >> 3) |(((linuxdevfn & 0x40) >> 2) + 0x10))
#define ISERIES_DECODE_FUNCTION(linuxdevfn) (linuxdevfn & 0x07)
#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7)
#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7)
#define ISERIES_GET_HOSE_HV_BUSNUM(hose) (((struct iSeries_hose_arch_data *)(hose->arch_data))->hvBusNumber)
/************************************************************************************/
/* Retreives Iseries Bus and SubBus from GlobalBusMap */
/************************************************************************************/
#define ISERIES_GET_LPAR_BUS(linux_bus) iSeries_GlobalBusMap[linux_bus][_HVBUSNUMBER_]
#define ISERIES_GET_LPAR_SUBBUS(linux_bus) iSeries_GlobalBusMap[linux_bus][_HVSUBBUSNUMBER_]
#define ISERIES_ADD_BUS_GLOBALBUSMAP(linuxbus, iseriesbus, iseriessubbus) \
iSeries_GlobalBusMap[linuxbus][_HVBUSNUMBER_] = iseriesbus; \
iSeries_GlobalBusMap[linuxbus][_HVSUBBUSNUMBER_] = iseriessubbus;
/************************************************************************************/
/* Global Bus map */
/* Bus and Subbus index values into the global bus number map array. */
/************************************************************************************/
#define ISERIES_GLOBALBUSMAP_SIZE 256
#define _HVBUSNUMBER_ 0
#define _HVSUBBUSNUMBER_ 1
extern u8 iSeries_GlobalBusMap[ISERIES_GLOBALBUSMAP_SIZE][2];
void iSeries_Initialize_GlobalBusMap(void);
#define pci_assign_all_buses() 1 // Al - NEW
/************************************************************************************/
/* Converts Virtual Address to Real Address for Hypervisor calls */
/************************************************************************************/
#define REALADDR(virtaddr) (0x8000000000000000 | (virt_to_absolute((u32)virtaddr) ))
/************************************************************************************/
/* Define TRUE and FALSE Values for Al */
/************************************************************************************/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef struct pci_dev pciDev;
/************************************************************************/
/* Routines to build the iSeries_Device for the pci device. */
/************************************************************************/
extern void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr);
extern int build_iSeries_Device_From_IoAddress(iSeries_Device* Device, u32* IoAddress);
extern void iSeries_pci_Initialize(void);
/************************************************************************/
/* Flight Recorder Debug Support */
/************************************************************************/
extern int PciTraceFlag; /* Conditional Trace */
void iSeries_Initialize_FlightRecorder(void);
int iSeries_Set_PciTraceFlag(int Flag); /* Sets flag, return old*/
int iSeries_Get_PciTraceFlag(void); /* Gets Flag. */
void iSeries_DumpDevice(char* Text, iSeries_Device* );
#endif /* _ISERIES_PCI_H */
#endif /*CONFIG_PPC_ISERIES */
/************************************************************************/
/* File iSeries pci_proc.c created by Allan Trautman on Feb 27 2001. */
/************************************************************************/
/* Create /proc/iSeries/pci file that contains iSeries card location. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Feb 27, 2001 */
/* End Change Activity */
/************************************************************************/
#include <asm/uaccess.h>
#include <asm/iSeries/iSeries_VpdInfo.h>
#include <asm/iSeries/iSeries_proc.h>
#include <asm/iSeries/iSeries_pci.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
static struct proc_dir_entry *pci_proc_root = NULL;
static struct proc_dir_entry *pciFr_proc_root = NULL;
int iSeries_proc_pci_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
int iSeries_proc_pci_write_proc(struct file *file, const char *buffer, unsigned long count, void *data);
int iSeries_proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
int iSeries_proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data);
void iSeries_pci_proc_init(struct proc_dir_entry *iSeries_proc) {
printk("PCI: Creating /proc/iSeries/pci\n");
/* Read = User,Group,Other, Write User */
pci_proc_root = create_proc_entry("pci", S_IFREG | S_IRUGO | S_IWUSR, iSeries_proc);
if (!pci_proc_root) return;
pci_proc_root->nlink = 1;
pci_proc_root->data = (void *)0;
pci_proc_root->read_proc = iSeries_proc_pci_read_proc;
pci_proc_root->write_proc = iSeries_proc_pci_write_proc;
/* Read = User,Group,Other, Write User */
printk("PCI: Creating /proc/iSeries/pciFr\n");
pciFr_proc_root = create_proc_entry("pciFr", S_IFREG | S_IRUGO | S_IWUSR, iSeries_proc);
if (!pciFr_proc_root) return;
pciFr_proc_root->nlink = 1;
pciFr_proc_root->data = (void *)0;
pciFr_proc_root->read_proc = iSeries_proc_pciFr_read_proc;
pciFr_proc_root->write_proc = iSeries_proc_pciFr_write_proc;
}
/*******************************************************************************/
/* Get called when client reads the /proc/iSeries/pci file. The data returned */
/* is the iSeries card locations for service. */
/*******************************************************************************/
int iSeries_proc_pci_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) {
int LineLen; /* Size of new Data */
struct pci_dev* PciDev; /* Device pointer */
struct net_device *dev; /* net_device pointer */
int DeviceCount; /* Device Number */
/***************************************************************************/
/* ### Bus Device Bus Dev Frm Card */
/* 1. Linux: 0/ 28 iSeries: 24/ 36/ 1/C14 */
/* 2. Linux: 0/ 30 iSeries: 24/ 38/ 2/C14 */
/***************************************************************************/
DeviceCount = 1; /* Count the devices listed. */
LineLen = 0; /* Reset Length */
/***************************************************************************/
/* List the devices */
/***************************************************************************/
pci_for_each_dev(PciDev) {
LineLen += sprintf(page+LineLen,"%3d. ",DeviceCount);
LineLen += iSeries_Device_Information(PciDev,page+LineLen,count-LineLen);
for (dev = dev_base; dev != NULL; dev = dev->next)
{
if (dev->base_addr == PciDev->resource[0].start ) { /* Yep, a net_device */
LineLen += sprintf(page+LineLen, ", Net device: %s", dev->name);
} /* if */
} /* for */
LineLen += sprintf(page+LineLen,"\n");
++DeviceCount; /* Add for the list. */
/************************************************************************/
/* Run out of room in system buffer. */
/************************************************************************/
if(LineLen+80 >= count) { /* Room for another line? No. bail out */
LineLen +=sprintf(page+LineLen,"/proc/pci file full!\n");
break;
}
}
/***************************************************************************/
/* If no devices, tell user that instead */
/***************************************************************************/
if(DeviceCount == 1) {
LineLen +=sprintf(page+LineLen,"No PCI devices found\n");
}
/***************************************************************************/
/* Update counts and return */
/***************************************************************************/
*eof = LineLen;
return LineLen;
}
/*******************************************************************************/
/* Do nothing, Nothing to support for the write */
/*******************************************************************************/
int iSeries_proc_pci_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) {
return count;
}
/*******************************************************************************/
/* Get called when client reads the /proc/iSeries/pci file. The data returned */
/* is the iSeries card locations for service. */
/*******************************************************************************/
int iSeries_proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) {
struct pci_dev* PciDev; /* Device pointer */
int DeviceCount = 1; /* Device Number */
int LineLen = 0; /* Size of new Data */
printk("PCI: Dump Flight Recorder!\n");
/***************************************************************************/
/* List the devices */
/***************************************************************************/
pci_for_each_dev(PciDev) {
LineLen += sprintf(page+LineLen,"%3d. 0x%08X ",DeviceCount,(int)PciDev);
LineLen += sprintf(page+LineLen,"Bus: %02X, Device: %02X ",PciDev->bus->number,PciDev->devfn);
LineLen += sprintf(page+LineLen,"\n");
++DeviceCount; /* Add for the list. */
}
LineLen += sprintf(page+LineLen,"--\n");
/***************************************************************************/
/* The Flight Recorder */
/* Someday handle wrap */
/***************************************************************************/
if (PciFr->StartingPointer != NULL) {
char* StartEntry = (char*)PciFr->StartingPointer;
char* EndEntry = (char*)PciFr->CurrentPointer;
while(EndEntry > StartEntry && LineLen+40 < count) {
LineLen += sprintf(page+LineLen,"%s\n",StartEntry);
StartEntry += strlen(StartEntry) + 1;
}
if(LineLen+40 >= count) {
printk("PCI: Max Count Hit %d and %d\n",LineLen,count);
}
}
/***************************************************************************/
/* Update counts and return */
/***************************************************************************/
printk("PCI: End of File at %d\n",LineLen);
*eof = LineLen;
return LineLen;
}
/*******************************************************************************/
/* Flight Recorder Controls */
/*******************************************************************************/
int iSeries_proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) {
if(buffer != 0 && strlen(buffer) > 0) {
if( strstr(buffer,"trace on") != NULL) {
iSeries_Set_PciTraceFlag(1);
ISERIES_PCI_FR_DATE("PCI Trace turned on!");
}
else if(strstr(buffer,"trace off") != NULL) {
iSeries_Set_PciTraceFlag(0);
ISERIES_PCI_FR_DATE("PCI Trace turned off!");
}
else {
ISERIES_PCI_FR_TIME("PCI Trace Option Invalid!");
readl(0x00000000);
}
}
return count;
}
/*
* iSeries_proc.c
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#ifndef _ISERIES_PROC_H
#include <asm/iSeries/iSeries_proc.h>
#endif
static struct proc_dir_entry * iSeries_proc_root = NULL;
static int iSeries_proc_initializationDone = 0;
static spinlock_t iSeries_proc_lock;
struct iSeries_proc_registration
{
struct iSeries_proc_registration *next;
iSeriesProcFunction functionMember;
};
struct iSeries_proc_registration preallocated[16];
#define MYQUEUETYPE(T) struct MYQueue##T
#define MYQUEUE(T) \
MYQUEUETYPE(T) \
{ \
struct T *head; \
struct T *tail; \
}
#define MYQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; } while(0)
#define MYQUEUEENQ(q, p) \
do { \
(p)->next = NULL; \
if ((q)->head != NULL) \
{ \
(q)->head->next = (p); \
(q)->head = (p); \
} \
else \
{ \
(q)->tail = (q)->head = (p); \
} \
} while(0)
#define MYQUEUEDEQ(q,p) \
do { \
(p) = (q)->tail; \
if ((p) != NULL) \
{ \
(q)->tail = (p)->next; \
(p)->next = NULL; \
} \
if ((q)->tail == NULL) \
(q)->head = NULL; \
} while(0)
MYQUEUE(iSeries_proc_registration);
typedef MYQUEUETYPE(iSeries_proc_registration) aQueue;
aQueue iSeries_free;
aQueue iSeries_queued;
void iSeries_proc_early_init(void)
{
int i = 0;
unsigned long flags;
iSeries_proc_initializationDone = 0;
spin_lock_init(&iSeries_proc_lock);
MYQUEUECTOR(&iSeries_free);
MYQUEUECTOR(&iSeries_queued);
spin_lock_irqsave(&iSeries_proc_lock, flags);
for (i = 0; i < 16; ++i)
{
MYQUEUEENQ(&iSeries_free, preallocated+i);
}
spin_unlock_irqrestore(&iSeries_proc_lock, flags);
}
void iSeries_proc_create(void)
{
unsigned long flags;
struct iSeries_proc_registration *reg = NULL;
spin_lock_irqsave(&iSeries_proc_lock, flags);
printk("iSeries_proc: Creating /proc/iSeries\n");
iSeries_proc_root = proc_mkdir("iSeries", 0);
if (!iSeries_proc_root) return;
MYQUEUEDEQ(&iSeries_queued, reg);
while (reg != NULL)
{
(*(reg->functionMember))(iSeries_proc_root);
MYQUEUEDEQ(&iSeries_queued, reg);
}
iSeries_proc_initializationDone = 1;
spin_unlock_irqrestore(&iSeries_proc_lock, flags);
}
void iSeries_proc_callback(iSeriesProcFunction initFunction)
{
unsigned long flags;
spin_lock_irqsave(&iSeries_proc_lock, flags);
if (iSeries_proc_initializationDone)
{
(*initFunction)(iSeries_proc_root);
}
else
{
struct iSeries_proc_registration *reg = NULL;
MYQUEUEDEQ(&iSeries_free, reg);
if (reg != NULL)
{
// printk("Registering %p in reg %p\n", initFunction, reg);
reg->functionMember = initFunction;
MYQUEUEENQ(&iSeries_queued, reg);
}
else
{
printk("Couldn't get a queue entry\n");
}
}
spin_unlock_irqrestore(&iSeries_proc_lock, flags);
}
/************************************************************************/
/* File iSeries_reset_device.c created by Allan Trautman on Mar 21 2001.*/
/************************************************************************/
/* This code supports the pci interface on the IBM iSeries systems. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, March 20, 2001 */
/* April 30, 2001, Added return codes on functions. */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/irq.h>
/************************************************************************/
/* Arch specific's */
/************************************************************************/
#include <asm/io.h>
#include <asm/iSeries/HvCallPci.h>
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/mf.h>
#include <asm/iSeries/iSeries_FlightRecorder.h>
#include <asm/iSeries/iSeries_pci.h>
#include "iSeries_pci.h"
/************************************************************************/
/* Interface to Reset Device, see .h for parms and flavors. */
/************************************************************************/
int iSeries_Device_Reset_NoIrq(struct pci_dev* PciDev) {
return iSeries_Device_Reset(PciDev,0,0,1);
}
int iSeries_Device_Reset_Generic(struct pci_dev* PciDev) {
return iSeries_Device_Reset(PciDev,0,0,0);
}
int iSeries_Device_Reset(struct pci_dev* PciDev, int AssertTime, int DelayTime, int IrqState) {
int RCode = 0;
PciReqsSaveArea* RegSaveArea = NULL;
if(PciDev != 0) {
if(IrqState == 0) disable_irq(PciDev->irq);
RegSaveArea = iSeries_Device_SaveConfigRegs(PciDev);
if(RegSaveArea != NULL) {
RCode = iSeries_Device_ToggleReset(PciDev, AssertTime, DelayTime);
if(RCode == 0) {
RCode = iSeries_Device_RestoreConfigRegs(RegSaveArea);
}
}
else {
RCode = -1;
}
if(IrqState == 0) enable_irq(PciDev->irq);
}
return RCode;
}
/************************************************************************/
/* Interface to toggle the reset line */
/* Time is in .1 seconds, need for seconds. */
/************************************************************************/
int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime) {
unsigned long AssertDelay = AssertTime;
unsigned long WaitDelay = DelayTime;
u16 Bus = ISERIES_GET_LPAR_BUS(PciDev->bus->number);
u8 Slot = ISERIES_DECODE_DEVICE(PciDev->devfn);
int RCode = 0;
/* Set defaults */
if(AssertTime < 5) AssertDelay = 5; /* Default is .5 second */
if(WaitDelay < 30) WaitDelay = 30; /* Default is 3 seconds */
/* Assert reset for time specified */
AssertDelay *= HZ; /* Convert to ticks. */
AssertDelay /= 10; /* Adjust to whole count */
RCode = HvCallPci_setSlotReset(Bus, 0x00, Slot, 1);
set_current_state(TASK_UNINTERRUPTIBLE); /* Only Wait. */
schedule_timeout(AssertDelay); /* Sleep for the time */
RCode += HvCallPci_setSlotReset(Bus, 0x00, Slot, 0);
/* Wait for device to reset */
WaitDelay *= HZ; /* Ticks */
WaitDelay /= 10; /* Whole count */
set_current_state(TASK_UNINTERRUPTIBLE); /* Only Wait. */
schedule_timeout(WaitDelay); /* Sleep for the time */
if(RCode == 0) {
sprintf(PciFrBuffer,"Slot Reset on Bus%3d, Device%3d!\n",Bus, Slot);
}
else {
sprintf(PciFrBuffer,"Slot Reset on Bus%3d, Device%3d Failed! RCode: %04X\n",Bus, Slot, RCode);
}
ISERIES_PCI_FR_TIME(PciFrBuffer);
printk("PCI: %s\n",PciFrBuffer);
return RCode;
}
/************************************************************************/
/* Allocates space and save the config registers for a device. */
/************************************************************************/
/* Note: This does byte reads so the data may appear byte swapped */
/* when compared to read word or dword. */
/* The data returned is a structure and will be freed automatically on */
/* the restore of the data. The is checking so if the save fails, the */
/* data will not be restore. Yes I know, you are most likey toast. */
/************************************************************************/
PciReqsSaveArea* iSeries_Device_SaveConfigRegs(struct pci_dev* DevPtr) {
int Register = 0;
struct pci_dev* PciDev = DevPtr;
PciReqsSaveArea* RegSaveArea = (PciReqsSaveArea*)kmalloc(sizeof(PciReqsSaveArea), GFP_KERNEL);
/*printk("PCI: Save Configuration Registers. 0x%08X\n",(int)RegSaveArea); */
if(RegSaveArea == 0) {
printk("PCI: Allocation failure in Save Configuration Registers.\n");
}
/********************************************************************/
/* Initialize Area. */
/********************************************************************/
else {
RegSaveArea->PciDev = DevPtr;
RegSaveArea->Flags = 0x01;
RegSaveArea->ByteCount = PCI_MAX_LAT+1; /* Number of Bytes */
RegSaveArea->RCode = 0;
RegSaveArea->FailReg = 0;
/****************************************************************/
/* Save All the Regs, NOTE: restore skips the first 16 bytes. */
/****************************************************************/
for(Register = 0;Register < RegSaveArea->ByteCount && RegSaveArea->RCode == 0; ++Register) {
RegSaveArea->RCode = pci_read_config_byte(PciDev, Register, &RegSaveArea->Regs[Register]);
}
/* Check for error during the save. */
if(RegSaveArea->RCode != 0) {
printk("PCI: I/O Failure in Save Configuration Registers. 0x%02X, 0x%04X\n",
Register,RegSaveArea->RCode);
RegSaveArea->Flags |= 0x80; /* Ouch Flag. */
RegSaveArea->FailReg = Register; /* Stuff this way */
}
}
return RegSaveArea;
}
/************************************************************************/
/* Restores the registers saved via the save function. See the save */
/* function for details. */
/************************************************************************/
int iSeries_Device_RestoreConfigRegs(PciReqsSaveArea* SaveArea) {
int RCode = 0;
if(SaveArea == 0 || SaveArea->PciDev == 0 ||
(SaveArea->Flags & 0x80) == 0x80 || SaveArea->RCode != 0) {
printk("PCI: Invalid SaveArea passed to Restore Configuration Registers. 0x%08X\n",(int)SaveArea);
RCode = -1;
}
else {
int Register;
struct pci_dev* PciDev = SaveArea->PciDev;
/***************************************************************/
/* Don't touch the Cmd or BIST regs, user must restore those. */
/* Restore PCI_CACHE_LINE_SIZE & PCI_LATENCY_TIMER */
/* Restore Saved Regs from 0x10 to 0x3F */
/***************************************************************/
pci_write_config_byte(PciDev, PCI_CACHE_LINE_SIZE, SaveArea->Regs[PCI_CACHE_LINE_SIZE]);
pci_write_config_byte(PciDev, PCI_LATENCY_TIMER, SaveArea->Regs[PCI_LATENCY_TIMER]);
for(Register = PCI_BASE_ADDRESS_0; Register < SaveArea->ByteCount && SaveArea->RCode == 0; ++Register) {
SaveArea->RCode = pci_write_config_byte(PciDev, Register, SaveArea->Regs[Register]);
}
if(SaveArea->RCode != 0) {
printk("PCI: I/O Failure in Restore Configuration Registers %d, %02X\n",Register,SaveArea->RCode);
SaveArea->FailReg = Register;
RCode = SaveArea->RCode;
}
else {
RCode = 0;
}
/***************************************************************/
/* Is the Auto Free Flag on */
/***************************************************************/
if(SaveArea->Flags && 0x01 == 0x01 ) {
/* printk("PCI: Auto Free Register Save Area. 0x%08X\n",(int)SaveArea); */
kfree(SaveArea);
}
}
return RCode;
}
/*
* mf.c
* Copyright (C) 2001 Troy D. Armstrong IBM Corporation
*
* This modules exists as an interface between a Linux secondary partition
* running on an iSeries and the primary partition's Virtual Service
* Processor (VSP) object. The VSP has final authority over powering on/off
* all partitions in the iSeries. It also provides miscellaneous low-level
* machine facility type operations.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/iSeries/mf.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/iSeries/HvLpConfig.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/nvram.h>
#include <asm/time.h>
#include <asm/iSeries/ItSpCommArea.h>
#include <asm/iSeries/mf_proc.h>
#include <asm/iSeries/iSeries_proc.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/bcd.h>
/*
* This is the structure layout for the Machine Facilites LPAR event
* flows.
*/
struct VspCmdData;
struct CeMsgData;
union SafeCast
{
u64 ptrAsU64;
void *ptr;
};
typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp );
struct CeMsgCompleteData
{
CeMsgCompleteHandler xHdlr;
void *xToken;
};
struct VspRspData
{
struct semaphore *xSemaphore;
struct VspCmdData *xResponse;
};
struct IoMFLpEvent
{
struct HvLpEvent xHvLpEvent;
u16 xSubtypeRc;
u16 xRsvd1;
u32 xRsvd2;
union
{
struct AllocData
{
u16 xSize;
u16 xType;
u32 xCount;
u16 xRsvd3;
u8 xRsvd4;
HvLpIndex xTargetLp;
} xAllocData;
struct CeMsgData
{
u8 xCEMsg[12];
char xReserved[4];
struct CeMsgCompleteData *xToken;
} xCEMsgData;
struct VspCmdData
{
union SafeCast xTokenUnion;
u16 xCmd;
HvLpIndex xLpIndex;
u8 xRc;
u32 xReserved1;
union VspCmdSubData
{
struct
{
u64 xState;
} xGetStateOut;
struct
{
u64 xIplType;
} xGetIplTypeOut, xFunction02SelectIplTypeIn;
struct
{
u64 xIplMode;
} xGetIplModeOut, xFunction02SelectIplModeIn;
struct
{
u64 xPage[4];
} xGetSrcHistoryIn;
struct
{
u64 xFlag;
} xGetAutoIplWhenPrimaryIplsOut, xSetAutoIplWhenPrimaryIplsIn, xWhiteButtonPowerOffIn, xFunction08FastPowerOffIn, xIsSpcnRackPowerIncompleteOut;
struct
{
u64 xToken;
u64 xAddressType;
u64 xSide;
u32 xTransferLength;
u32 xOffset;
} xSetKernelImageIn, xGetKernelImageIn, xSetKernelCmdLineIn, xGetKernelCmdLineIn;
struct
{
u32 xTransferLength;
} xGetKernelImageOut,xGetKernelCmdLineOut;
u8 xReserved2[80];
} xSubData;
} xVspCmd;
} xUnion;
};
/*
* All outgoing event traffic is kept on a FIFO queue. The first
* pointer points to the one that is outstanding, and all new
* requests get stuck on the end. Also, we keep a certain number of
* preallocated stack elements so that we can operate very early in
* the boot up sequence (before kmalloc is ready).
*/
struct StackElement
{
struct StackElement * next;
struct IoMFLpEvent event;
MFCompleteHandler hdlr;
char dmaData[72];
unsigned dmaDataLength;
unsigned remoteAddress;
};
static spinlock_t spinlock;
static struct StackElement * head = NULL;
static struct StackElement * tail = NULL;
static struct StackElement * avail = NULL;
static struct StackElement prealloc[16];
/*
* Put a stack element onto the available queue, so it can get reused.
* Attention! You must have the spinlock before calling!
*/
void free( struct StackElement * element )
{
if( element != NULL )
{
element->next = avail;
avail = element;
}
}
/*
* Enqueue the outbound event onto the stack. If the queue was
* empty to begin with, we must also issue it via the Hypervisor
* interface. There is a section of code below that will touch
* the first stack pointer without the protection of the spinlock.
* This is OK, because we know that nobody else will be modifying
* the first pointer when we do this.
*/
static int signalEvent( struct StackElement * newElement )
{
int rc = 0;
unsigned long flags;
int go = 1;
struct StackElement * element;
HvLpEvent_Rc hvRc;
/* enqueue the event */
if( newElement != NULL )
{
spin_lock_irqsave( &spinlock, flags );
if( head == NULL )
head = newElement;
else
{
go = 0;
tail->next = newElement;
}
newElement->next = NULL;
tail = newElement;
spin_unlock_irqrestore( &spinlock, flags );
}
/* send the event */
while( go )
{
go = 0;
/* any DMA data to send beforehand? */
if( head->dmaDataLength > 0 )
HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote );
hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent);
if( hvRc != HvLpEvent_Rc_Good )
{
printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc );
spin_lock_irqsave( &spinlock, flags );
element = head;
head = head->next;
if( head != NULL )
go = 1;
spin_unlock_irqrestore( &spinlock, flags );
if( element == newElement )
rc = -EIO;
else
{
if( element->hdlr != NULL )
{
union SafeCast mySafeCast;
mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken;
(*element->hdlr)( mySafeCast.ptr, -EIO );
}
}
spin_lock_irqsave( &spinlock, flags );
free( element );
spin_unlock_irqrestore( &spinlock, flags );
}
}
return rc;
}
/*
* Allocate a new StackElement structure, and initialize it.
*/
static struct StackElement * newStackElement( void )
{
struct StackElement * newElement = NULL;
HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex();
unsigned long flags;
if( newElement == NULL )
{
spin_lock_irqsave( &spinlock, flags );
if( avail != NULL )
{
newElement = avail;
avail = avail->next;
}
spin_unlock_irqrestore( &spinlock, flags );
}
if( newElement == NULL )
newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC);
if( newElement == NULL )
{
printk( KERN_ERR "mf.c: unable to kmalloc %d bytes\n", sizeof(struct StackElement) );
return NULL;
}
memset( newElement, 0, sizeof(struct StackElement) );
newElement->event.xHvLpEvent.xFlags.xValid = 1;
newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck;
newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck;
newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int;
newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac;
newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex();
newElement->event.xHvLpEvent.xTargetLp = primaryLp;
newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1;
newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good;
newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac);
newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac);
return newElement;
}
static int signalVspInstruction( struct VspCmdData *vspCmd )
{
struct StackElement * newElement = newStackElement();
int rc = 0;
struct VspRspData response;
DECLARE_MUTEX_LOCKED(Semaphore);
response.xSemaphore = &Semaphore;
response.xResponse = vspCmd;
if( newElement == NULL )
rc = -ENOMEM;
else
{
newElement->event.xHvLpEvent.xSubtype = 6;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0);
newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response;
newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd;
newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex();
newElement->event.xUnion.xVspCmd.xRc = 0xFF;
newElement->event.xUnion.xVspCmd.xReserved1 = 0;
memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData));
mb();
rc = signalEvent(newElement);
}
if (rc == 0)
{
down(&Semaphore);
}
return rc;
}
/*
* Send a 12-byte CE message to the primary partition VSP object
*/
static int signalCEMsg( char * ceMsg, void * token )
{
struct StackElement * newElement = newStackElement();
int rc = 0;
if( newElement == NULL )
rc = -ENOMEM;
else
{
newElement->event.xHvLpEvent.xSubtype = 0;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0);
memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 );
newElement->event.xUnion.xCEMsgData.xToken = token;
rc = signalEvent(newElement);
}
return rc;
}
/*
* Send a 12-byte CE message and DMA data to the primary partition VSP object
*/
static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress )
{
struct StackElement * newElement = newStackElement();
int rc = 0;
if( newElement == NULL )
rc = -ENOMEM;
else
{
newElement->event.xHvLpEvent.xSubtype = 0;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0);
memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 );
newElement->event.xUnion.xCEMsgData.xToken = token;
memcpy( newElement->dmaData, dmaData, dmaDataLength );
newElement->dmaDataLength = dmaDataLength;
newElement->remoteAddress = remoteAddress;
rc = signalEvent(newElement);
}
return rc;
}
/*
* Initiate a nice (hopefully) shutdown of Linux. We simply are
* going to try and send the init process a SIGINT signal. If
* this fails (why?), we'll simply force it off in a not-so-nice
* manner.
*/
static int shutdown( void )
{
int rc = kill_proc(1,SIGINT,1);
if( rc )
{
printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc );
mf_powerOff();
}
else
printk( KERN_ALERT "mf.c: init has been successfully notified to proceed with shutdown\n" );
return rc;
}
/*
* The primary partition VSP object is sending us a new
* event flow. Handle it...
*/
static void intReceived( struct IoMFLpEvent * event )
{
int freeIt = 0;
struct StackElement * two = NULL;
/* ack the interrupt */
event->xHvLpEvent.xRc = HvLpEvent_Rc_Good;
HvCallEvent_ackLpEvent( &event->xHvLpEvent );
/* process interrupt */
switch( event->xHvLpEvent.xSubtype )
{
case 0: /* CE message */
switch( event->xUnion.xCEMsgData.xCEMsg[3] )
{
case 0x5B: /* power control notification */
if( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 )
{
printk( KERN_ALERT "mf.c: Commencing partition shutdown\n" );
if( shutdown() == 0 )
signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
}
break;
case 0xC0: /* get time */
{
if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) )
{
freeIt = 1;
if ( head->event.xUnion.xCEMsgData.xToken != 0 )
{
CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr;
void * token = head->event.xUnion.xCEMsgData.xToken->xToken;
if (xHdlr != NULL)
(*xHdlr)( token, &(event->xUnion.xCEMsgData) );
}
}
}
break;
}
/* remove from queue */
if ( freeIt == 1 )
{
unsigned long flags;
spin_lock_irqsave( &spinlock, flags );
if( head != NULL )
{
struct StackElement *oldHead = head;
head = head->next;
two = head;
free( oldHead );
}
spin_unlock_irqrestore( &spinlock, flags );
}
/* send next waiting event */
if( two != NULL )
signalEvent( NULL );
break;
case 1: /* IT sys shutdown */
printk( KERN_ALERT "mf.c: Commencing system shutdown\n" );
shutdown();
break;
}
}
/*
* The primary partition VSP object is acknowledging the receipt
* of a flow we sent to them. If there are other flows queued
* up, we must send another one now...
*/
static void ackReceived( struct IoMFLpEvent * event )
{
unsigned long flags;
struct StackElement * two = NULL;
unsigned long freeIt = 0;
/* handle current event */
if( head != NULL )
{
switch( event->xHvLpEvent.xSubtype )
{
case 0: /* CE msg */
if( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 )
{
if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 )
{
freeIt = 1;
if ( head->event.xUnion.xCEMsgData.xToken != 0 )
{
CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr;
void * token = head->event.xUnion.xCEMsgData.xToken->xToken;
if (xHdlr != NULL)
(*xHdlr)( token, &(event->xUnion.xCEMsgData) );
}
}
}
else
{
freeIt = 1;
}
break;
case 4: /* allocate */
case 5: /* deallocate */
if( head->hdlr != NULL )
{
union SafeCast mySafeCast;
mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken;
(*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount );
}
freeIt = 1;
break;
case 6:
{
struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr;
if (rsp != NULL)
{
if (rsp->xResponse != NULL)
memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd));
if (rsp->xSemaphore != NULL)
up(rsp->xSemaphore);
}
else
{
printk( KERN_ERR "mf.c: no rsp\n");
}
freeIt = 1;
}
break;
}
}
else
printk( KERN_ERR "mf.c: stack empty for receiving ack\n" );
/* remove from queue */
spin_lock_irqsave( &spinlock, flags );
if(( head != NULL ) && ( freeIt == 1 ))
{
struct StackElement *oldHead = head;
head = head->next;
two = head;
free( oldHead );
}
spin_unlock_irqrestore( &spinlock, flags );
/* send next waiting event */
if( two != NULL )
signalEvent( NULL );
}
/*
* This is the generic event handler we are registering with
* the Hypervisor. Ensure the flows are for us, and then
* parse it enough to know if it is an interrupt or an
* acknowledge.
*/
static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs )
{
if( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) )
{
switch( event->xFlags.xFunction )
{
case HvLpEvent_Function_Ack:
ackReceived( (struct IoMFLpEvent *)event );
break;
case HvLpEvent_Function_Int:
intReceived( (struct IoMFLpEvent *)event );
break;
default:
printk( KERN_ERR "mf.c: non ack/int event received\n" );
break;
}
}
else
printk( KERN_ERR "mf.c: alien event received\n" );
}
/*
* Global kernel interface to allocate and seed events into the
* Hypervisor.
*/
void mf_allocateLpEvents( HvLpIndex targetLp,
HvLpEvent_Type type,
unsigned size,
unsigned count,
MFCompleteHandler hdlr,
void * userToken )
{
struct StackElement * newElement = newStackElement();
int rc = 0;
if( newElement == NULL )
rc = -ENOMEM;
else
{
union SafeCast mine;
mine.ptr = userToken;
newElement->event.xHvLpEvent.xSubtype = 4;
newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0);
newElement->event.xUnion.xAllocData.xTargetLp = targetLp;
newElement->event.xUnion.xAllocData.xType = type;
newElement->event.xUnion.xAllocData.xSize = size;
newElement->event.xUnion.xAllocData.xCount = count;
newElement->hdlr = hdlr;
rc = signalEvent(newElement);
}
if( (rc != 0) && (hdlr != NULL) )
(*hdlr)( userToken, rc );
}
/*
* Global kernel interface to unseed and deallocate events already in
* Hypervisor.
*/
void mf_deallocateLpEvents( HvLpIndex targetLp,
HvLpEvent_Type type,
unsigned count,
MFCompleteHandler hdlr,
void * userToken )
{
struct StackElement * newElement = newStackElement();
int rc = 0;
if( newElement == NULL )
rc = -ENOMEM;
else
{
union SafeCast mine;
mine.ptr = userToken;
newElement->event.xHvLpEvent.xSubtype = 5;
newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0);
newElement->event.xUnion.xAllocData.xTargetLp = targetLp;
newElement->event.xUnion.xAllocData.xType = type;
newElement->event.xUnion.xAllocData.xCount = count;
newElement->hdlr = hdlr;
rc = signalEvent(newElement);
}
if( (rc != 0) && (hdlr != NULL) )
(*hdlr)( userToken, rc );
}
/*
* Global kernel interface to tell the VSP object in the primary
* partition to power this partition off.
*/
void mf_powerOff( void )
{
printk( KERN_ALERT "mf.c: Down it goes...\n" );
signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
for(;;);
}
/*
* Global kernel interface to tell the VSP object in the primary
* partition to reboot this partition.
*/
void mf_reboot( void )
{
printk( KERN_ALERT "mf.c: Preparing to bounce...\n" );
signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
for(;;);
}
/*
* Display a single word SRC onto the VSP control panel.
*/
void mf_displaySrc( u32 word )
{
u8 ce[12];
memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 );
ce[8] = word>>24;
ce[9] = word>>16;
ce[10] = word>>8;
ce[11] = word;
signalCEMsg( ce, NULL );
}
/*
* Display a single word SRC of the form "PROGXXXX" on the VSP control panel.
*/
void mf_displayProgress( u16 value )
{
u8 ce[12];
u8 src[72];
memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 );
memcpy( src,
"\x01\x00\x00\x01"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"\x00\x00\x00\x00"
"PROGxxxx"
" ",
72 );
src[6] = value>>8;
src[7] = value&255;
src[44] = "0123456789ABCDEF"[(value>>12)&15];
src[45] = "0123456789ABCDEF"[(value>>8)&15];
src[46] = "0123456789ABCDEF"[(value>>4)&15];
src[47] = "0123456789ABCDEF"[value&15];
dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 );
}
/*
* Clear the VSP control panel. Used to "erase" an SRC that was
* previously displayed.
*/
void mf_clearSrc( void )
{
signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
}
/*
* Initialization code here.
*/
void mf_init( void )
{
int i;
/* initialize */
spin_lock_init( &spinlock );
for( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i )
free( &prealloc[i] );
HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler );
/* virtual continue ack */
signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL );
/* initialization complete */
printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" );
iSeries_proc_callback(&mf_proc_init);
}
void mf_setSide(char side)
{
int rc = 0;
u64 newSide = 0;
struct VspCmdData myVspCmd;
memset(&myVspCmd, 0, sizeof(myVspCmd));
if (side == 'A')
newSide = 0;
else if (side == 'B')
newSide = 1;
else if (side == 'C')
newSide = 2;
else
newSide = 3;
myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide;
myVspCmd.xCmd = 10;
rc = signalVspInstruction(&myVspCmd);
}
char mf_getSide(void)
{
char returnValue = ' ';
int rc = 0;
struct VspCmdData myVspCmd;
memset(&myVspCmd, 0, sizeof(myVspCmd));
myVspCmd.xCmd = 2;
myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0;
mb();
rc = signalVspInstruction(&myVspCmd);
if(rc != 0)
{
return returnValue;
}
else
{
if (myVspCmd.xRc == 0)
{
if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0)
returnValue = 'A';
else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1)
returnValue = 'B';
else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2)
returnValue = 'C';
else
returnValue = 'D';
}
}
return returnValue;
}
void mf_getSrcHistory(char *buffer, int size)
{
/* struct IplTypeReturnStuff returnStuff;
struct StackElement * newElement = newStackElement();
int rc = 0;
char *pages[4];
pages[0] = kmalloc(4096, GFP_ATOMIC);
pages[1] = kmalloc(4096, GFP_ATOMIC);
pages[2] = kmalloc(4096, GFP_ATOMIC);
pages[3] = kmalloc(4096, GFP_ATOMIC);
if(( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL))
rc = -ENOMEM;
else
{
returnStuff.xType = 0;
returnStuff.xRc = 0;
returnStuff.xDone = 0;
newElement->event.xHvLpEvent.xSubtype = 6;
newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0);
newElement->event.xUnion.xVspCmd.xEvent = &returnStuff;
newElement->event.xUnion.xVspCmd.xCmd = 4;
newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex();
newElement->event.xUnion.xVspCmd.xRc = 0xFF;
newElement->event.xUnion.xVspCmd.xReserved1 = 0;
newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0]));
newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1]));
newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2]));
newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3]));
mb();
rc = signalEvent(newElement);
}
if(rc != 0)
{
return;
}
else
{
while (returnStuff.xDone != 1)
{
udelay(10);
}
if (returnStuff.xRc == 0)
{
memcpy(buffer, pages[0], size);
}
}
kfree(pages[0]);
kfree(pages[1]);
kfree(pages[2]);
kfree(pages[3]);*/
}
void mf_setCmdLine(const char *cmdline, int size, u64 side)
{
struct VspCmdData myVspCmd;
int rc = 0;
dma_addr_t dma_addr = 0;
char *page = pci_alloc_consistent(NULL, size, &dma_addr);
if (page == NULL) {
printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n");
return;
}
copy_from_user(page, cmdline, size);
memset(&myVspCmd, 0, sizeof(myVspCmd));
myVspCmd.xCmd = 31;
myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr;
myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex;
myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side;
myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size;
mb();
rc = signalVspInstruction(&myVspCmd);
pci_free_consistent(NULL, size, page, dma_addr);
}
int mf_getCmdLine(char *cmdline, int *size, u64 side)
{
struct VspCmdData myVspCmd;
int rc = 0;
int len = *size;
dma_addr_t dma_addr = pci_map_single(NULL, cmdline, *size, PCI_DMA_FROMDEVICE);
memset(cmdline, 0, *size);
memset(&myVspCmd, 0, sizeof(myVspCmd));
myVspCmd.xCmd = 33;
myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr;
myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex;
myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side;
myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size;
mb();
rc = signalVspInstruction(&myVspCmd);
if ( ! rc ) {
if (myVspCmd.xRc == 0)
{
len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength;
}
// else
// {
// memcpy(cmdline, "Bad cmdline", 11);
// }
}
pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE);
return len;
}
int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side)
{
struct VspCmdData myVspCmd;
int rc = 0;
dma_addr_t dma_addr = 0;
char *page = pci_alloc_consistent(NULL, size, &dma_addr);
if (page == NULL) {
printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n");
return -ENOMEM;
}
if (copy_from_user(page, buffer, size)) {
rc = -EFAULT;
goto out;
}
memset(&myVspCmd, 0, sizeof(myVspCmd));
myVspCmd.xCmd = 30;
myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr;
myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex;
myVspCmd.xSubData.xGetKernelImageIn.xSide = side;
myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset;
myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size;
mb();
rc = signalVspInstruction(&myVspCmd);
if(rc == 0)
{
if (myVspCmd.xRc == 0)
{
rc = 0;
}
else
{
rc = -ENOMEM;
}
}
out:
pci_free_consistent(NULL, size, page, dma_addr);
return rc;
}
int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side)
{
struct VspCmdData myVspCmd;
int rc = 0;
int len = *size;
dma_addr_t dma_addr = pci_map_single(NULL, buffer, *size, PCI_DMA_FROMDEVICE);
memset(buffer, 0, len);
memset(&myVspCmd, 0, sizeof(myVspCmd));
myVspCmd.xCmd = 32;
myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr;
myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex;
myVspCmd.xSubData.xGetKernelImageIn.xSide = side;
myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset;
myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len;
mb();
rc = signalVspInstruction(&myVspCmd);
if(rc == 0)
{
if (myVspCmd.xRc == 0)
{
*size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength;
}
else
{
rc = -ENOMEM;
}
}
pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE);
return rc;
}
int mf_setRtcTime(unsigned long time)
{
struct rtc_time tm;
to_tm(time, &tm);
return mf_setRtc( &tm );
}
struct RtcTimeData
{
struct semaphore *xSemaphore;
struct CeMsgData xCeMsg;
int xRc;
};
void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg)
{
struct RtcTimeData *rtc = (struct RtcTimeData *)token;
memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg));
rtc->xRc = 0;
up(rtc->xSemaphore);
}
static unsigned long lastsec = 1;
int mf_getRtcTime(unsigned long *time)
{
// unsigned long usec, tsec;
u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart));
u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1);
int year = 1970;
int year1 = ( dataWord1 >> 24 ) & 0x000000FF;
int year2 = ( dataWord1 >> 16 ) & 0x000000FF;
int sec = ( dataWord1 >> 8 ) & 0x000000FF;
int min = dataWord1 & 0x000000FF;
int hour = ( dataWord2 >> 24 ) & 0x000000FF;
int day = ( dataWord2 >> 8 ) & 0x000000FF;
int mon = dataWord2 & 0x000000FF;
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year1);
BCD_TO_BIN(year2);
year = year1 * 100 + year2;
*time = mktime(year, mon, day, hour, min, sec);
*time += ( jiffies / HZ );
// Now THIS is a nasty hack!
// It ensures that the first two calls to mf_getRtcTime get different
// answers. That way the loop in init_time (time.c) will not think
// the clock is stuck.
if ( lastsec ) {
*time -= lastsec;
--lastsec;
}
return 0;
}
int mf_getRtc( struct rtc_time * tm )
{
struct CeMsgCompleteData ceComplete;
struct RtcTimeData rtcData;
int rc = 0;
DECLARE_MUTEX_LOCKED(Semaphore);
memset(&ceComplete, 0, sizeof(ceComplete));
memset(&rtcData, 0, sizeof(rtcData));
rtcData.xSemaphore = &Semaphore;
ceComplete.xHdlr = &getRtcTimeComplete;
ceComplete.xToken = (void *)&rtcData;
rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete );
if ( rc == 0 )
{
down(&Semaphore);
if ( rtcData.xRc == 0)
{
if ( ( rtcData.xCeMsg.xCEMsg[2] == 0xa9 ) ||
( rtcData.xCeMsg.xCEMsg[2] == 0xaf ) ) {
/* TOD clock is not set */
tm->tm_sec = 1;
tm->tm_min = 1;
tm->tm_hour = 1;
tm->tm_mday = 10;
tm->tm_mon = 8;
tm->tm_year = 71;
mf_setRtc( tm );
}
{
u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4));
u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8));
u8 year = (dataWord1 >> 16 ) & 0x000000FF;
u8 sec = ( dataWord1 >> 8 ) & 0x000000FF;
u8 min = dataWord1 & 0x000000FF;
u8 hour = ( dataWord2 >> 24 ) & 0x000000FF;
u8 day = ( dataWord2 >> 8 ) & 0x000000FF;
u8 mon = dataWord2 & 0x000000FF;
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
if ( year <= 69 )
year += 100;
tm->tm_sec = sec;
tm->tm_min = min;
tm->tm_hour = hour;
tm->tm_mday = day;
tm->tm_mon = mon;
tm->tm_year = year;
}
}
else
{
rc = rtcData.xRc;
tm->tm_sec = 0;
tm->tm_min = 0;
tm->tm_hour = 0;
tm->tm_mday = 15;
tm->tm_mon = 5;
tm->tm_year = 52;
}
tm->tm_wday = 0;
tm->tm_yday = 0;
tm->tm_isdst = 0;
}
return rc;
}
int mf_setRtc(struct rtc_time * tm)
{
char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00";
int rc = 0;
u8 day, mon, hour, min, sec, y1, y2;
unsigned year;
year = 1900 + tm->tm_year;
y1 = year / 100;
y2 = year % 100;
sec = tm->tm_sec;
min = tm->tm_min;
hour = tm->tm_hour;
day = tm->tm_mday;
mon = tm->tm_mon + 1;
BIN_TO_BCD(sec);
BIN_TO_BCD(min);
BIN_TO_BCD(hour);
BIN_TO_BCD(mon);
BIN_TO_BCD(day);
BIN_TO_BCD(y1);
BIN_TO_BCD(y2);
ceTime[4] = y1;
ceTime[5] = y2;
ceTime[6] = sec;
ceTime[7] = min;
ceTime[8] = hour;
ceTime[10] = day;
ceTime[11] = mon;
rc = signalCEMsg( ceTime, NULL );
return rc;
}
/*
* mf_proc.c
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _MF_PROC_H
#include <asm/iSeries/mf_proc.h>
#endif
#ifndef MF_H_INCLUDED
#include <asm/iSeries/mf.h>
#endif
#include <asm/uaccess.h>
static struct proc_dir_entry *mf_proc_root = NULL;
int proc_mf_dump_cmdline
(char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_mf_dump_vmlinux
(char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_mf_dump_side
(char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_mf_change_side
(struct file *file, const char *buffer, unsigned long count, void *data);
int proc_mf_dump_src
(char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_mf_change_src (struct file *file, const char *buffer, unsigned long count, void *data);
int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data);
int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data);
void mf_proc_init(struct proc_dir_entry *iSeries_proc)
{
struct proc_dir_entry *ent = NULL;
struct proc_dir_entry *mf_a = NULL;
struct proc_dir_entry *mf_b = NULL;
struct proc_dir_entry *mf_c = NULL;
struct proc_dir_entry *mf_d = NULL;
mf_proc_root = proc_mkdir("mf", iSeries_proc);
if (!mf_proc_root) return;
mf_a = proc_mkdir("A", mf_proc_root);
if (!mf_a) return;
ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_a);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_mf_dump_cmdline;
ent->write_proc = proc_mf_change_cmdline;
ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_a);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_mf_dump_vmlinux;
ent->write_proc = proc_mf_change_vmlinux;
mf_b = proc_mkdir("B", mf_proc_root);
if (!mf_b) return;
ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_b);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)1;
ent->read_proc = proc_mf_dump_cmdline;
ent->write_proc = proc_mf_change_cmdline;
ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_b);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)1;
ent->read_proc = proc_mf_dump_vmlinux;
ent->write_proc = proc_mf_change_vmlinux;
mf_c = proc_mkdir("C", mf_proc_root);
if (!mf_c) return;
ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_c);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)2;
ent->read_proc = proc_mf_dump_cmdline;
ent->write_proc = proc_mf_change_cmdline;
ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_c);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)2;
ent->read_proc = proc_mf_dump_vmlinux;
ent->write_proc = proc_mf_change_vmlinux;
mf_d = proc_mkdir("D", mf_proc_root);
if (!mf_d) return;
ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_d);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)3;
ent->read_proc = proc_mf_dump_cmdline;
ent->write_proc = proc_mf_change_cmdline;
ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR, mf_d);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)3;
ent->read_proc = proc_mf_dump_vmlinux;
ent->write_proc = NULL;
ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_mf_dump_side;
ent->write_proc = proc_mf_change_side;
ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_mf_dump_src;
ent->write_proc = proc_mf_change_src;
}
int proc_mf_dump_cmdline
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = count;
char *p;
len = mf_getCmdLine(page, &len, (u64)(int)data);
p = page + len - 1;
while ( p > page ) {
if ( (*p == 0) || (*p == ' ') )
--p;
else
break;
}
if ( *p != '\n' ) {
++p;
*p = '\n';
}
++p;
*p = 0;
len = p - page;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else
len = count;
*start = page + off;
return len;
}
int proc_mf_dump_vmlinux
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int sizeToGet = count;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)(int)data) == 0)
{
if (sizeToGet != 0)
{
*start = page + off;
printk("mf_proc.c: got count %d off %d\n", sizeToGet, (int)off);
return sizeToGet;
}
else
{
printk("mf_proc.c: eof\n");
*eof = 1;
return 0;
}
}
else
{
printk("mf_proc.c: eof\n");
*eof = 1;
return 0;
}
}
int proc_mf_dump_side
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = 0;
char mf_current_side = mf_getSide();
len = sprintf(page, "%c\n", mf_current_side);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if ((*buffer != 'A') &&
(*buffer != 'B') &&
(*buffer != 'C') &&
(*buffer != 'D'))
{
printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n");
return -EINVAL;
}
mf_setSide(*buffer);
return count;
}
int proc_mf_dump_src
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = 0;
mf_getSrcHistory(page, count);
len = count;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else
len = count;
*start = page + off;
return len;
}
int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if ((count < 4) && (count != 1))
{
printk(KERN_ERR "mf_proc: invalid src\n");
return -EINVAL;
}
if ((count == 1) && ((*buffer) == '\0'))
{
mf_clearSrc();
}
else
{
mf_displaySrc(*(u32 *)buffer);
}
return count;
}
int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
mf_setCmdLine(buffer, count, (u64)(int)data);
return count;
}
int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
mf_setVmlinuxChunk(buffer, count, file->f_pos, (u64)(int)data);
file->f_pos += count;
return count;
}
/*
* pmc_proc.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _PMC_PROC_H
#include <asm/iSeries/pmc_proc.h>
#endif
#include <asm/iSeries/Paca.h>
#include <asm/iSeries/ItLpPaca.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/processor.h>
#define MMCR0 795
#define MMCR1 798
#define MMCRA 786
#define PMC1 787
#define PMC2 788
#define PMC3 789
#define PMC4 790
#define PMC5 791
#define PMC6 792
#define PMC7 793
#define PMC8 794
static int proc_pmc_control_mode = 0;
#define PMC_CONTROL_CPI 1
#define PMC_CONTROL_TLB 2
static struct proc_dir_entry *pmc_proc_root = NULL;
int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_mmcr0( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_mmcr1( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_mmcra( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc1( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc2( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc3( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc4( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc5( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc6( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc7( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_get_pmc8( char *page, char **start, off_t off, int count, int *eof, void *data);
int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data);
int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data);
void pmc_proc_init(struct proc_dir_entry *iSeries_proc)
{
struct proc_dir_entry *ent = NULL;
ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_get_lpevents;
ent->write_proc = proc_reset_lpevents;
pmc_proc_root = proc_mkdir("pmc", iSeries_proc);
if (!pmc_proc_root) return;
ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_control;
ent->write_proc = proc_pmc_set_control;
ent = create_proc_entry("mmcr0", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_mmcr0;
ent->write_proc = proc_pmc_set_mmcr0;
ent = create_proc_entry("mmcr1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_mmcr1;
ent->write_proc = proc_pmc_set_mmcr1;
ent = create_proc_entry("mmcra", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_mmcra;
ent->write_proc = proc_pmc_set_mmcra;
ent = create_proc_entry("pmc1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc1;
ent->write_proc = proc_pmc_set_pmc1;
ent = create_proc_entry("pmc2", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc2;
ent->write_proc = proc_pmc_set_pmc2;
ent = create_proc_entry("pmc3", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc3;
ent->write_proc = proc_pmc_set_pmc3;
ent = create_proc_entry("pmc4", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc4;
ent->write_proc = proc_pmc_set_pmc4;
ent = create_proc_entry("pmc5", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc5;
ent->write_proc = proc_pmc_set_pmc5;
ent = create_proc_entry("pmc6", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc6;
ent->write_proc = proc_pmc_set_pmc6;
ent = create_proc_entry("pmc7", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc7;
ent->write_proc = proc_pmc_set_pmc7;
ent = create_proc_entry("pmc8", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root);
if (!ent) return;
ent->nlink = 1;
ent->data = (void *)0;
ent->read_proc = proc_pmc_get_pmc8;
ent->write_proc = proc_pmc_set_pmc8;
}
static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len)
{
if ( len <= off+count)
*eof = 1;
*start = page+off;
len -= off;
if ( len > count )
len = count;
if ( len < 0 )
len = 0;
return len;
}
static char * lpEventTypes[9] = {
"Hypervisor\t\t",
"Machine Facilities\t",
"Session Manager\t",
"SPD I/O\t\t",
"Virtual Bus\t\t",
"PCI I/O\t\t",
"RIO I/O\t\t",
"Virtual Lan\t\t",
"Virtual I/O\t\t"
};
int proc_get_lpevents
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
unsigned i;
int len = 0;
len += sprintf( page+len, "LpEventQueue 0\n" );
len += sprintf( page+len, " events processed:\t%lu\n",
(unsigned long)xItLpQueue.xLpIntCount );
for (i=0; i<9; ++i) {
len += sprintf( page+len, " %s %10lu\n",
lpEventTypes[i],
(unsigned long)xItLpQueue.xLpIntCountByType[i] );
}
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data )
{
return count;
}
int proc_pmc_get_control
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = 0;
if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) {
unsigned long mach_cycles = mfspr( PMC5 );
unsigned long inst_complete = mfspr( PMC4 );
unsigned long inst_dispatch = mfspr( PMC3 );
unsigned long thread_active_run = mfspr( PMC1 );
unsigned long thread_active = mfspr( PMC2 );
unsigned long cpi = 0;
unsigned long cpithou = 0;
unsigned long remain;
if ( inst_complete ) {
cpi = thread_active_run / inst_complete;
remain = thread_active_run % inst_complete;
if ( inst_complete > 1000000 )
cpithou = remain / ( inst_complete / 1000 );
else
cpithou = ( remain * 1000 ) / inst_complete;
}
len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" );
len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles );
len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active );
len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete );
len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch );
len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run );
len += sprintf( page+len, "thread active run cycles/instructions completed\n" );
len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou );
}
else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) {
len += sprintf( page+len, "PMC TLB Mode\n" );
len += sprintf( page+len, "I-miss count : %12u\n", mfspr( PMC1 ) );
len += sprintf( page+len, "I-miss latency : %12u\n", mfspr( PMC2 ) );
len += sprintf( page+len, "D-miss count : %12u\n", mfspr( PMC3 ) );
len += sprintf( page+len, "D-miss latency : %12u\n", mfspr( PMC4 ) );
len += sprintf( page+len, "IERAT miss count : %12u\n", mfspr( PMC5 ) );
len += sprintf( page+len, "D-reference count : %12u\n", mfspr( PMC6 ) );
len += sprintf( page+len, "miss PTEs searched : %12u\n", mfspr( PMC7 ) );
len += sprintf( page+len, "miss >8 PTEs searched : %12u\n", mfspr( PMC8 ) );
}
/* IMPLEMENT ME */
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_mmcr0
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(MMCR0) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_mmcr1
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(MMCR1) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_mmcra
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(MMCRA) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc1
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC1) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc2
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC2) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc3
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC3) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc4
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC4) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc5
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC5) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc6
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC6) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc7
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC7) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
int proc_pmc_get_pmc8
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len = sprintf( page, "0x%08x", mfspr(PMC8) );
return pmc_calc_metrics( page, start, off, count, eof, len );
}
unsigned long proc_pmc_conv_int( const char *buf, unsigned count )
{
const char * p;
char b0, b1;
unsigned v, multiplier, mult, i;
unsigned long val;
multiplier = 10;
p = buf;
if ( count >= 3 ) {
b0 = buf[0];
b1 = buf[1];
if ( ( b0 == '0' ) &&
( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) {
p = buf + 2;
count -= 2;
multiplier = 16;
}
}
val = 0;
for ( i=0; i<count; ++i ) {
b0 = *p++;
v = 0;
mult = multiplier;
if ( ( b0 >= '0' ) && ( b0 <= '9' ) )
v = b0 - '0';
else if ( multiplier == 16 ) {
if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) )
v = b0 - 'a' + 10;
else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) )
v = b0 - 'A' + 10;
else
mult = 1;
}
else
mult = 1;
val *= mult;
val += v;
}
return val;
}
static inline void proc_pmc_stop(void)
{
// Freeze all counters, leave everything else alone
mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 );
}
static inline void proc_pmc_start(void)
{
// Unfreeze all counters, leave everything else alone
mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 );
}
static inline void proc_pmc_reset(void)
{
// Clear all the PMCs to zero
// Assume a "stop" has already frozen the counters
// Clear all the PMCs
mtspr( PMC1, 0 );
mtspr( PMC2, 0 );
mtspr( PMC3, 0 );
mtspr( PMC4, 0 );
mtspr( PMC5, 0 );
mtspr( PMC6, 0 );
mtspr( PMC7, 0 );
mtspr( PMC8, 0 );
}
static inline void proc_pmc_cpi(void)
{
/* Configure the PMC registers to count cycles and instructions */
/* so we can compute cpi */
/*
* MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0)
* MMCR0[6] = 1 Freeze counters when any overflow
* MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles
* MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles
* MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched
* MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed
* MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles
*
*/
proc_pmc_control_mode = PMC_CONTROL_CPI;
// Indicate to hypervisor that we are using the PMCs
((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1;
// Freeze all counters
mtspr( MMCR0, 0x80000000 );
mtspr( MMCR1, 0x00000000 );
// Clear all the PMCs
mtspr( PMC1, 0 );
mtspr( PMC2, 0 );
mtspr( PMC3, 0 );
mtspr( PMC4, 0 );
mtspr( PMC5, 0 );
mtspr( PMC6, 0 );
mtspr( PMC7, 0 );
mtspr( PMC8, 0 );
// Freeze counters in Wait State (CTRL[31]=0)
mtspr( MMCRA, 0x00000002 );
// PMC3<-0x07, PMC4<-0x03, PMC5<-0x06
mtspr( MMCR1, 0x38cc0000 );
mb();
// PMC1<-0x01, PMC2<-0x05
// Start all counters
mtspr( MMCR0, 0x02000045 );
}
static inline void proc_pmc_tlb(void)
{
/* Configure the PMC registers to count tlb misses */
/*
* MMCR0[6] = 1 Freeze counters when any overflow
* MMCR0[19:25] = 0x55 Group count
* PMC1 counts I misses
* PMC2 counts I miss duration (latency)
* PMC3 counts D misses
* PMC4 counts D miss duration (latency)
* PMC5 counts IERAT misses
* PMC6 counts D references (including PMC7)
* PMC7 counts miss PTEs searched
* PMC8 counts miss >8 PTEs searched
*
*/
proc_pmc_control_mode = PMC_CONTROL_TLB;
// Indicate to hypervisor that we are using the PMCs
((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1;
// Freeze all counters
mtspr( MMCR0, 0x80000000 );
mtspr( MMCR1, 0x00000000 );
// Clear all the PMCs
mtspr( PMC1, 0 );
mtspr( PMC2, 0 );
mtspr( PMC3, 0 );
mtspr( PMC4, 0 );
mtspr( PMC5, 0 );
mtspr( PMC6, 0 );
mtspr( PMC7, 0 );
mtspr( PMC8, 0 );
mtspr( MMCRA, 0x00000000 );
mb();
// PMC1<-0x55
// Start all counters
mtspr( MMCR0, 0x02001540 );
}
int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data )
{
if ( ! strncmp( buffer, "stop", 4 ) )
proc_pmc_stop();
else if ( ! strncmp( buffer, "start", 5 ) )
proc_pmc_start();
else if ( ! strncmp( buffer, "reset", 5 ) )
proc_pmc_reset();
else if ( ! strncmp( buffer, "cpi", 3 ) )
proc_pmc_cpi();
else if ( ! strncmp( buffer, "tlb", 3 ) )
proc_pmc_tlb();
/* IMPLEMENT ME */
return count;
}
int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
v = v & ~0x04000000; /* Don't allow interrupts for now */
if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */
((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 1;
else
((struct Paca *)mfspr(SPRG1))->xLpPacaPtr->xPMCRegsInUse = 0;
mtspr( MMCR0, v );
return count;
}
int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( MMCR1, v );
return count;
}
int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
v = v & ~0x00008000; /* Don't allow interrupts for now */
mtspr( MMCRA, v );
return count;
}
int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC1, v );
return count;
}
int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC2, v );
return count;
}
int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC3, v );
return count;
}
int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC4, v );
return count;
}
int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC5, v );
return count;
}
int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC6, v );
return count;
}
int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC7, v );
return count;
}
int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data )
{
unsigned long v;
v = proc_pmc_conv_int( buffer, count );
mtspr( PMC8, v );
return count;
}
/*
* Real Time Clock interface for IBM iSeries
*
* Based on rtc.c by Paul Gortmaker
*
* This driver allows use of the real time clock
* from user space. It exports the /dev/rtc
* interface supporting various ioctl() and also the
* /proc/driver/rtc pseudo-file for status information.
*
* iSeries does not support RTC interrupts nor an alarm.
*
* 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.
*
* 1.0 Mike Corrigan: IBM iSeries rtc support
*/
#define RTC_VERSION "1.0"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/mc146818rtc.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/iSeries/mf.h>
/*
* We sponge a minor off of the misc major. No need slurping
* up another valuable major dev number for this. If you add
* an ioctl, make sure you don't conflict with SPARC's RTC
* ioctls.
*/
static loff_t rtc_llseek(struct file *file, loff_t offset, int origin);
static ssize_t rtc_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static void get_rtc_time (struct rtc_time *rtc_tm);
static int rtc_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data);
/*
* If this driver ever becomes modularised, it will be really nice
* to make the epoch retain its value across module reload...
*/
static unsigned long epoch = 1900; /* year corresponding to 0x00 */
static const unsigned char days_in_mo[] =
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/*
* Now all the various file operations that we export.
*/
static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
static ssize_t rtc_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
return -EIO;
}
static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct rtc_time wtime;
switch (cmd) {
case RTC_RD_TIME: /* Read the time/date from RTC */
{
get_rtc_time(&wtime);
break;
}
case RTC_SET_TIME: /* Set the RTC */
{
struct rtc_time rtc_tm;
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned int yrs;
if (!capable(CAP_SYS_TIME))
return -EACCES;
if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
sizeof(struct rtc_time)))
return -EFAULT;
yrs = rtc_tm.tm_year;
mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
day = rtc_tm.tm_mday;
hrs = rtc_tm.tm_hour;
min = rtc_tm.tm_min;
sec = rtc_tm.tm_sec;
if (yrs < 70)
return -EINVAL;
leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
if ((mon > 12) || (day == 0))
return -EINVAL;
if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
return -EINVAL;
if ((hrs >= 24) || (min >= 60) || (sec >= 60))
return -EINVAL;
if ( yrs > 169 )
return -EINVAL;
mf_setRtc( &rtc_tm );
return 0;
}
case RTC_EPOCH_READ: /* Read the epoch. */
{
return put_user (epoch, (unsigned long *)arg);
}
case RTC_EPOCH_SET: /* Set the epoch. */
{
/*
* There were no RTC clocks before 1900.
*/
if (arg < 1900)
return -EINVAL;
if (!capable(CAP_SYS_TIME))
return -EACCES;
epoch = arg;
return 0;
}
default:
return -EINVAL;
}
return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
}
static int rtc_open(struct inode *inode, struct file *file)
{
return 0;
}
static int rtc_release(struct inode *inode, struct file *file)
{
return 0;
}
/*
* The various file operations we support.
*/
static struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.llseek = rtc_llseek,
.read = rtc_read,
.ioctl = rtc_ioctl,
.open = rtc_open,
.release = rtc_release,
};
static struct miscdevice rtc_dev=
{
RTC_MINOR,
"rtc",
&rtc_fops
};
static int __init rtc_init(void)
{
if (misc_register(&rtc_dev))
return -ENODEV;
create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n");
return 0;
}
static void __exit rtc_exit (void)
{
remove_proc_entry ("driver/rtc", NULL);
misc_deregister(&rtc_dev);
}
module_init(rtc_init);
module_exit(rtc_exit);
/*
* Info exported via "/proc/driver/rtc".
*/
static int rtc_proc_output (char *buf)
{
char *p;
struct rtc_time tm;
p = buf;
get_rtc_time(&tm);
/*
* There is no way to tell if the luser has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
p += sprintf(p,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n"
"rtc_epoch\t: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
p += sprintf(p,
"DST_enable\t: no\n"
"BCD\t\t: yes\n"
"24hr\t\t: yes\n" );
return p - buf;
}
static int rtc_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = rtc_proc_output (page);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static void get_rtc_time(struct rtc_time *rtc_tm)
{
mf_getRtc( rtc_tm );
rtc_tm->tm_mon--;
}
...@@ -13,7 +13,6 @@ endif ...@@ -13,7 +13,6 @@ endif
HEAD-y := head.o HEAD-y := head.o
HEAD-$(CONFIG_40x) := head_4xx.o HEAD-$(CONFIG_40x) := head_4xx.o
HEAD-$(CONFIG_8xx) := head_8xx.o HEAD-$(CONFIG_8xx) := head_8xx.o
HEAD-$(CONFIG_PPC_ISERIES) := iSeries_head.o
HEAD-$(CONFIG_6xx) += idle_6xx.o HEAD-$(CONFIG_6xx) += idle_6xx.o
EXTRA_TARGETS := $(HEAD-y) EXTRA_TARGETS := $(HEAD-y)
...@@ -27,9 +26,7 @@ obj-y := entry.o traps.o irq.o idle.o time.o misc.o \ ...@@ -27,9 +26,7 @@ obj-y := entry.o traps.o irq.o idle.o time.o misc.o \
obj-$(CONFIG_6xx) += l2cr.o obj-$(CONFIG_6xx) += l2cr.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_PCI) += pci.o
ifneq ($(CONFIG_PPC_ISERIES),y)
obj-$(CONFIG_PCI) += pci-dma.o obj-$(CONFIG_PCI) += pci-dma.o
endif
obj-$(CONFIG_KGDB) += ppc-stub.o obj-$(CONFIG_KGDB) += ppc-stub.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_TAU) += temp.o obj-$(CONFIG_TAU) += temp.o
...@@ -38,7 +35,6 @@ ifneq ($(CONFIG_MATH_EMULATION),n) ...@@ -38,7 +35,6 @@ ifneq ($(CONFIG_MATH_EMULATION),n)
obj-y += softemu8xx.o obj-y += softemu8xx.o
endif endif
endif endif
obj-$(CONFIG_PPC_ISERIES) += iSeries_misc.o
find_name : find_name.c find_name : find_name.c
$(HOSTCC) $(HOSTCFLAGS) -o find_name find_name.c $(HOSTCC) $(HOSTCFLAGS) -o find_name find_name.c
...@@ -25,13 +25,6 @@ ...@@ -25,13 +25,6 @@
#include <asm/cputable.h> #include <asm/cputable.h>
#include <asm/thread_info.h> #include <asm/thread_info.h>
#ifdef CONFIG_PPC_ISERIES
#include <asm/iSeries/Paca.h>
#include <asm/iSeries/ItLpPaca.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/HvLpEvent.h>
#endif /* CONFIG_PPC_ISERIES */
#define DEFINE(sym, val) \ #define DEFINE(sym, val) \
asm volatile("\n->" #sym " %0 " #val : : "i" (val)) asm volatile("\n->" #sym " %0 " #val : : "i" (val))
...@@ -124,35 +117,6 @@ main(void) ...@@ -124,35 +117,6 @@ main(void)
DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features)); DEFINE(CPU_SPEC_FEATURES, offsetof(struct cpu_spec, cpu_features));
DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup));
#ifdef CONFIG_PPC_ISERIES
DEFINE(PACAPROCENABLED, offsetof(struct Paca, xProcEnabled));
DEFINE(PACAPACAINDEX, offsetof(struct Paca, xPacaIndex));
DEFINE(PACAPROCSTART, offsetof(struct Paca, xProcStart));
DEFINE(PACAKSAVE, offsetof(struct Paca, xKsave));
DEFINE(PACASAVEDMSR, offsetof(struct Paca, xSavedMsr));
DEFINE(PACASAVEDLR, offsetof(struct Paca, xSavedLr));
DEFINE(PACACONTEXTOVERFLOW, offsetof(struct Paca, xContextOverflow));
DEFINE(PACAR21, offsetof(struct Paca, xR21));
DEFINE(PACAR22, offsetof(struct Paca, xR22));
DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr));
DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca));
DEFINE(PACA_STRUCT_SIZE, sizeof(struct Paca));
DEFINE(LPREGSAV, offsetof(struct Paca, xRegSav));
DEFINE(PACADEFAULTDECR, offsetof(struct Paca, default_decr));
DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xRsvd));
DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0));
DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1));
DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xDecrInt));
DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIpiCnt));
DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr));
DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending));
DEFINE(LPQINUSEWORD, offsetof(struct ItLpQueue, xInUseWord));
DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags));
DEFINE(CONTEXT, offsetof(struct mm_struct, context));
DEFINE(_SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, mq));
DEFINE(PACA_EXT_INTS, offsetof(struct Paca, ext_ints));
#endif /* CONFIG_PPC_ISERIES */
DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28);
return 0; return 0;
} }
...@@ -33,7 +33,7 @@ extern void __setup_cpu_8xx(unsigned long offset, int cpu_nr, struct cpu_spec* s ...@@ -33,7 +33,7 @@ extern void __setup_cpu_8xx(unsigned long offset, int cpu_nr, struct cpu_spec* s
extern void __setup_cpu_generic(unsigned long offset, int cpu_nr, struct cpu_spec* spec); extern void __setup_cpu_generic(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
#define CLASSIC_PPC (!defined(CONFIG_8xx) && !defined(CONFIG_4xx) && \ #define CLASSIC_PPC (!defined(CONFIG_8xx) && !defined(CONFIG_4xx) && \
!defined(CONFIG_POWER3) && !defined(CONFIG_PPC_ISERIES)) !defined(CONFIG_POWER3))
/* This table only contains "desktop" CPUs, it need to be filled with embedded /* This table only contains "desktop" CPUs, it need to be filled with embedded
* ones as well... * ones as well...
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
* rewritten by Paul Mackerras. * rewritten by Paul Mackerras.
* Copyright (C) 1996 Paul Mackerras. * Copyright (C) 1996 Paul Mackerras.
* MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
* Adaptations for iSeries Lpar by Mike Corrigan & Dave Boutcher
* *
* This file contains the system call entry code, context switch * This file contains the system call entry code, context switch
* code, and exception/interrupt return code for PowerPC. * code, and exception/interrupt return code for PowerPC.
...@@ -31,14 +30,10 @@ ...@@ -31,14 +30,10 @@
#include <asm/thread_info.h> #include <asm/thread_info.h>
#include <asm/ppc_asm.h> #include <asm/ppc_asm.h>
#include <asm/offsets.h> #include <asm/offsets.h>
#ifdef CONFIG_PPC_ISERIES
#include "iSeries_asm.h"
#endif /* CONFIG_PPC_ISERIES */
#undef SHOW_SYSCALLS #undef SHOW_SYSCALLS
#undef SHOW_SYSCALLS_TASK #undef SHOW_SYSCALLS_TASK
#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_head.S */
/* /*
* This code finishes saving the registers to the exception frame * This code finishes saving the registers to the exception frame
* and jumps to the appropriate handler for the exception, turning * and jumps to the appropriate handler for the exception, turning
...@@ -115,7 +110,6 @@ stack_ovf: ...@@ -115,7 +110,6 @@ stack_ovf:
mtspr SRR1,r10 mtspr SRR1,r10
SYNC SYNC
RFI RFI
#endif /* CONFIG_PPC_ISERIES */
/* /*
* Handle a system call. * Handle a system call.
...@@ -384,8 +378,7 @@ ppc_clone: ...@@ -384,8 +378,7 @@ ppc_clone:
* On entry, r3 points to the THREAD for the current task, r4 * On entry, r3 points to the THREAD for the current task, r4
* points to the THREAD for the new task. * points to the THREAD for the new task.
* *
* This routine is always called with interrupts disabled * This routine is always called with interrupts disabled.
* (soft disabled for iSeries).
* *
* Note: there are two ways to get to the "going out" portion * Note: there are two ways to get to the "going out" portion
* of this code; either by coming in via the entry (_switch) * of this code; either by coming in via the entry (_switch)
......
/*
* arch/ppc/kernel/iSeries_asm.h
*
* Definitions used by various bits of low-level assembly code on iSeries.
*
* Copyright (C) 2001 IBM Corp.
*
* 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.
*/
#define CHECKLPQUEUE(ra,rb,rc) \
mfspr rb,SPRG1; /* Get Paca address */\
lbz ra,PACALPPACA+LPPACAIPIINT(rb); /* Get IPI int flag */\
cmpi 0,ra,0; /* IPI occurred in hypervisor ? */\
bne 99f; /* If so, skip rest */\
lwz ra,PACALPQUEUE(rb); /* Get LpQueue address */\
cmpi 0,ra,0; /* Does LpQueue exist? */\
beq 99f; /* If not skip rest */\
lbz rb,LPQINUSEWORD(ra); /* Test for LpQueue recursion */\
cmpi 0,rb,1; /* If we are about to recurse */\
beq 99f; /* If so, skip rest */\
lwz rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\
lbz rb,LPEVENTFLAGS(rb); /* Get Valid bit */\
lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\
andi. ra,rb,0x0080; /* Isolate Valid bit */\
or. ra,ra,rc; /* 0 == no pending events */\
99:
#define CHECKDECR(ra,rb) \
mfspr rb,SPRG1; /* Get Paca address */\
lbz ra,PACALPPACA+LPPACADECRINT(rb); /* Get DECR int flag */\
cmpi 0,ra,0; /* DECR occurred in hypervisor ? */\
beq 99f; /* If not, skip rest */\
xor ra,ra,ra; \
stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\
99:
#define CHECKANYINT(ra,rb,rc) \
mfspr rb,SPRG1; /* Get Paca address */\
ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get all interrupt flags */\
/* Note use of ld, protected by soft/hard disabled */\
cmpldi 0,ra,0; /* Any interrupt occurred while soft disabled? */\
bne 99f; /* If so, skip rest */\
lwz ra,PACALPQUEUE(rb); /* Get LpQueue address */\
cmpi 0,ra,0; /* Does LpQueue exist? */\
beq 99f; /* If not skip rest */\
lwz rb,LPQINUSEWORD(ra); /* Test for LpQueue recursion */\
cmpi 0,rb,1; /* If we are about to recurse */\
beq 99f; /* If so, skip rest */\
lwz rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\
lbz rb,LPEVENTFLAGS(rb); /* Get Valid bit */\
lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\
andi. ra,rb,0x0080; /* Isolate Valid bit */\
or. ra,ra,rc; /* 0 == no pending events */\
99:
/*
* arch/ppc/kernel/iSeries_head.S
*
* Adapted from arch/ppc/kernel/head.S
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP
* Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu>
* Adapted for Power Macintosh by Paul Mackerras.
* Low-level exception handlers and MMU support
* rewritten by Paul Mackerras.
* Copyright (C) 1996 Paul Mackerras.
* Adapted for iSeries by Mike Corrigan
* Updated by Dave Boutcher
*
* This file contains the low-level support and setup for the
* iSeries LPAR platform.
*
* 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 <asm/processor.h>
#include <asm/page.h>
#include <asm/mmu.h>
#include <asm/pgtable.h>
#include <asm/ppc_asm.h>
#include <asm/thread_info.h>
#include <asm/offsets.h>
#include "iSeries_asm.h"
.text
.globl _stext
_stext:
/* iSeries LPAR
*
* In an iSeries partition, the operating system has no direct access
* to the hashed page table. The iSeries hypervisor manages the
* hashed page table, and is directed by the operating system in the
* partition. The partition, Linux in this case, always runs with
* MSR.IR and MSR.DR equal to 1. The hypervisor establishes
* addressibility for the first 64 MB of memory at 0xC0000000 by
* building a hashed page table and setting segment register 12.
*
* The partition memory is not physically contiguous, nor necessarily
* addressable with a 32-bit address. The hypervisor provides functions
* which the kernel can use to discover the layout of memory. The
* iSeries LPAR specific code in the kernel will build a table that maps
* contiguous pseudo-real addresses starting at zero to the actual
* physical addresses owned by this partition. In 32-bit mode we will
* restrict ourselves to no more than 768 MB (or maybe 1 GB)
*
* When Linux interrupt handlers get control, the hypervisor has
* already saved SRR0 and SRR1 into a control block shared between
* the hypervisor and Linux. This is know as the ItLpPaca. The values
* in the actual SRR0 and SRR1 are not valid. This requires a change in
* the way the SPRG registers are used. The definitions are:
*
* Register old definition new definition
*
* SPRG0 temp - used to save gpr reserved for hypervisor
* SPRG1 temp - used to save gpr addr of Paca
* SPRG2 0 or kernel stack frame temp - used to save gpr
* SPRG3 Linux thread Linux thread
*
* The Paca contains the address of the ItLpPaca. The Paca is known only
* to Linux, while the ItLpPaca is shared between Linux and the
* hypervisor.
*
* The value that used to be in SPRG2 will now be saved in the Paca,
* as will at least one GPR.
*/
.globl __start
__start:
b start_here
. = 0x020
/* iSeries LPAR hypervisor expects a 64-bit offset of
the hvReleaseData structure (see HvReleaseData.h)
at offset 0x20. This is the base for all common
control blocks between the hypervisor and the kernel
*/
.long 0
.long hvReleaseData-KERNELBASE
.long 0
.long msChunks-KERNELBASE
.long 0
.long pidhash-KERNELBASE
/* Pointer to start of embedded System.map */
.long 0
.globl embedded_sysmap_start
embedded_sysmap_start:
.long 0
/* Pointer to end of embedded System.map */
.long 0
.globl embedded_sysmap_end
embedded_sysmap_end:
.long 0
. = 0x060
.globl ste_fault_count
ste_fault_count:
.long 0
.globl set_context_count
set_context_count:
.long 0
.globl yield_count
yield_count:
.long 0
.globl update_times_count
update_times_count:
.long 0
.globl update_wall_jiffies_count
update_wall_jiffies_count:
.long 0
.globl update_wall_jiffies_ticks
update_wall_jiffies_ticks:
.long 0
/*
* We assume SPRG1 has the address of the Paca and SPRG3
* has the address of the task's thread_struct.
* SPRG2 is used as a scratch register (as required by the
* hypervisor). SPRG0 is reserved for the hypervisor.
*
* The ItLpPaca has the values of SRR0 and SRR1 that the
* hypervisor saved at the point of the actual interrupt.
*
* The Paca contains the value that the non-LPAR PPC Linux Kernel
* keeps in SPRG2, which is either zero (if the interrupt
* occurred in the kernel) or the address of the available
* space on the kernel stack (if the interrupt occurred
* in user code).
*/
#define EXCEPTION_PROLOG_1 \
mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\
mfspr r20,SPRG1; /* get Paca */\
/* must do std not stw because soft disable protects \
* 64-bit register use (in HvCall, maybe others) \
*/\
std r21,PACAR21(r20); /* Save GPR21 in Paca */\
std r22,PACAR22(r20); /* Save GPR22 in Paca */\
mfcr r22 /* Get CR */
#define EXCEPTION_PROLOG_2 \
lwz r21,PACAKSAVE(r20); /* exception stack to use */\
cmpwi 0,r21,0; /* user mode or kernel */\
bne 1f; /* 0 -> r1, else use PACAKSAVE */\
subi r21,r1,INT_FRAME_SIZE; /* alloc exc. frame */\
1: stw r1,GPR1(r21); \
mr r1,r21; \
stw r22,_CCR(r1); /* save CR in stackframe */ \
mflr r22; \
stw r22,_LINK(r1); /* Save LR in stackframe */ \
bl save_regs; /* now save everything else */ \
ld r22,PACALPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\
ld r23,PACALPPACA+LPPACASRR1(r20) /* Get SRR1 from ItLpPaca */
#define EXCEPTION_PROLOG_EXIT \
mtcrf 0xff,r22; \
ld r22,PACALPPACA+LPPACASRR0(r20); \
ld r21,PACALPPACA+LPPACASRR1(r20); \
mtspr SRR0,r22; \
mtspr SRR1,r21; \
ld r22,PACAR22(r20); \
ld r21,PACAR21(r20); \
mfspr r20,SPRG2; \
RFI
#define EXCEPTION_PROLOG \
EXCEPTION_PROLOG_1; \
EXCEPTION_PROLOG_2
/*
* Note: code which follows this uses cr0.eq (set if from kernel),
* r21, r22 (SRR0), and r23 (SRR1).
*/
/*
* Exception vectors.
*/
#define STD_EXCEPTION(n, label, hdlr) \
. = n; \
label: \
EXCEPTION_PROLOG; \
addi r3,r1,STACK_FRAME_OVERHEAD; \
li r20,0; /* soft disabled */\
bl transfer_to_handler; \
.long hdlr; \
.long ret_from_except
/* System reset */
. = 0x100
SystemReset:
mfspr r3,SPRG3 /* Get Paca address */
mtspr SPRG1,r3 /* Set Linux SPRG1 -> Paca */
lhz r24,PACAPACAINDEX(r3) /* Get processor # */
cmpi 0,r24,0 /* Are we processor 0? */
beq start_here /* Start up the first processor */
mfspr r4,CTRLF
li r5,RUNLATCH
andc r4,r4,r5 /* Turn off the run light */
mtspr CTRLT,r4
1:
HMT_LOW
#ifdef CONFIG_SMP
lbz r23,PACAPROCSTART(r3) /* Test if this processor
* should start */
cmpi 0,r23,0
bne secondary_start
secondary_smp_loop:
/* Let the Hypervisor know we are alive */
/* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
lis r3,0x8002
rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */
or r3,r3,r0 /* r3 = r3 | r0 */
#else /* CONFIG_SMP */
/* Yield the processor. This is required for non-SMP kernels
which are running on multi-threaded machines. */
lis r3,0x8000
rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */
addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */
li r4,0 /* "yield timed" */
li r5,-1 /* "yield forever" */
#endif /* CONFIG_SMP */
li r0,-1 /* r0=-1 indicates a Hypervisor call */
sc /* Invoke the hypervisor via a system call */
mfspr r3,SPRG1 /* Put r3 back */
b 1b /* If SMP not configured, secondaries
* loop forever */
/* Machine check */
STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
/* Data access exception. */
. = 0x300
DataAccess:
EXCEPTION_PROLOG
mfspr r4,DAR
stw r4,_DAR(r1)
mfspr r5,DSISR
stw r5,_DSISR(r1)
andis. r0,r5,0x0020 /* Is this a segment fault? */
bne ste_fault /* Yes - go reload segment regs */
/* This should and with 0xd7ff */
andis. r0,r5,0xa470 /* Can we handle as little fault? */
bne 1f /* */
rlwinm r3,r5,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */
/*
* r3 contains the required access permissions
* r4 contains the faulting address
*/
stw r22,_NIP(r1) /* Help with debug if dsi loop */
bl hash_page /* Try to handle as hpte fault */
lwz r4,_DAR(r1) /* Get original DAR */
lwz r5,_DSISR(r1) /* and original DSISR */
1: addi r3,r1,STACK_FRAME_OVERHEAD
lwz r20,_SOFTE(r1)
bl transfer_to_handler
.long do_page_fault
.long ret_from_except
/* Instruction access exception. */
. = 0x400
InstructionAccess:
EXCEPTION_PROLOG
mr r4,r22
mr r5,r23
andis. r0,r23,0x0020 /* Is this a segment fault? */
bne ste_fault /* Yes - go reload segment regs */
andis. r0,r23,0x4000 /* no pte found? */
beq 1f /* if so, try to put a PTE */
li r3,0
bl hash_page /* Try to handle as hpte fault */
mr r4,r22
mr r5,r23
1: addi r3,r1,STACK_FRAME_OVERHEAD
lwz r20,_SOFTE(r1)
bl transfer_to_handler
.long do_page_fault
.long ret_from_except
/* External interrupt */
. = 0x500;
HardwareInterrupt:
EXCEPTION_PROLOG_1
lbz r21,PACAPROCENABLED(r20)
cmpi 0,r21,0
bne 1f
EXCEPTION_PROLOG_EXIT
1: EXCEPTION_PROLOG_2
do_pending_int:
addi r3,r1,STACK_FRAME_OVERHEAD
li r4,0
li r20,0 /* Soft disabled */
bl transfer_to_handler
.globl do_IRQ_intercept
do_IRQ_intercept:
.long do_IRQ;
.long ret_from_intercept
/* Alignment exception */
. = 0x600
Alignment:
EXCEPTION_PROLOG
mfspr r4,DAR
stw r4,_DAR(r1)
mfspr r5,DSISR
stw r5,_DSISR(r1)
addi r3,r1,STACK_FRAME_OVERHEAD
lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */
bl transfer_to_handler
.long AlignmentException
.long ret_from_except
/* Program check exception */
. = 0x700
ProgramCheck:
EXCEPTION_PROLOG
addi r3,r1,STACK_FRAME_OVERHEAD
lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */
bl transfer_to_handler
.long ProgramCheckException
.long ret_from_except
/* Floating-point unavailable */
. = 0x800
FPUnavailable:
EXCEPTION_PROLOG
lwz r3,PACAKSAVE(r20)
cmpwi 0,r3,0
beq 1f
b load_up_fpu
1:
li r20,0 /* soft disabled */
bl transfer_to_handler /* if from kernel, take a trap */
.long KernelFP
.long ret_from_except
. = 0x900
Decrementer:
EXCEPTION_PROLOG_1
lbz r21,PACAPROCENABLED(r20)
cmpi 0,r21,0
bne 1f
li r21,1
stb r21,PACALPPACA+LPPACADECRINT(r20)
lwz r21,PACADEFAULTDECR(r20)
mtspr DEC,r21
EXCEPTION_PROLOG_EXIT
1: EXCEPTION_PROLOG_2
addi r3,r1,STACK_FRAME_OVERHEAD
li r20,0 /* Soft disabled */
bl transfer_to_handler
.globl timer_interrupt_intercept
timer_interrupt_intercept:
.long timer_interrupt
.long ret_from_intercept
STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
STD_EXCEPTION(0xb00, Trap_0b, UnknownException)
/* System call */
. = 0xc00
SystemCall:
EXCEPTION_PROLOG
/* Store r3 to the kernel stack */
stw r3,ORIG_GPR3(r1)
lbz r20,PACAPROCENABLED(r20) /* preserve soft en/disabled */
bl transfer_to_handler
.long DoSyscall
.long ret_from_except
/* Single step - not used on 601 */
STD_EXCEPTION(0xd00, SingleStep, SingleStepException)
/*
STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
*/
STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint)
/*
STD_EXCEPTION(0x1400, SMI, SMIException)
STD_EXCEPTION(0x1500, Trap_15, UnknownException)
STD_EXCEPTION(0x1600, Trap_16, UnknownException)
STD_EXCEPTION(0x1700, Trap_17, TAUException)
STD_EXCEPTION(0x1800, Trap_18, UnknownException)
STD_EXCEPTION(0x1900, Trap_19, UnknownException)
STD_EXCEPTION(0x1a00, Trap_1a, UnknownException)
STD_EXCEPTION(0x1b00, Trap_1b, UnknownException)
STD_EXCEPTION(0x1c00, Trap_1c, UnknownException)
STD_EXCEPTION(0x1d00, Trap_1d, UnknownException)
STD_EXCEPTION(0x1e00, Trap_1e, UnknownException)
STD_EXCEPTION(0x1f00, Trap_1f, UnknownException)
STD_EXCEPTION(0x2000, RunMode, RunModeException)
STD_EXCEPTION(0x2100, Trap_21, UnknownException)
STD_EXCEPTION(0x2200, Trap_22, UnknownException)
STD_EXCEPTION(0x2300, Trap_23, UnknownException)
STD_EXCEPTION(0x2400, Trap_24, UnknownException)
STD_EXCEPTION(0x2500, Trap_25, UnknownException)
STD_EXCEPTION(0x2600, Trap_26, UnknownException)
STD_EXCEPTION(0x2700, Trap_27, UnknownException)
STD_EXCEPTION(0x2800, Trap_28, UnknownException)
STD_EXCEPTION(0x2900, Trap_29, UnknownException)
STD_EXCEPTION(0x2a00, Trap_2a, UnknownException)
STD_EXCEPTION(0x2b00, Trap_2b, UnknownException)
STD_EXCEPTION(0x2c00, Trap_2c, UnknownException)
STD_EXCEPTION(0x2d00, Trap_2d, UnknownException)
STD_EXCEPTION(0x2e00, Trap_2e, UnknownException)
STD_EXCEPTION(0x2f00, Trap_2f, UnknownException)
*/
. = 0x3000
/* This code saves: CTR, XER, DAR, DSISR, SRR0, SRR1, */
/* r0, r2-r13, r20-r24 */
/* It uses R22 as a scratch register */
save_regs:
ld r22,PACAR21(r20) /* Get GPR21 from Paca */
stw r22,GPR21(r1) /* Save GPR21 in stackframe */
ld r22,PACAR22(r20) /* Get GPR22 from Paca */
stw r22,GPR22(r1) /* Save GPR22 in stackframe */
stw r23,GPR23(r1) /* Save GPR23 in stackframe */
stw r24,GPR24(r1) /* Save GPR24 in stackframe */
mfspr r22,SPRG2 /* Get GPR20 from SPRG2 */
stw r22,GPR20(r1) /* Save GPR20 in stackframe */
mfctr r22
stw r22,_CTR(r1)
mfspr r22,XER
stw r22,_XER(r1)
lbz r22,PACAPROCENABLED(r20)/* Get soft enabled/disabled */
stw r22,_SOFTE(r1)
stw r0,GPR0(r1)
SAVE_8GPRS(2, r1)
SAVE_4GPRS(10, r1)
blr
ste_fault:
bl set_kernel_segregs
mfspr r3,SPRG1
li r4,0
stb r4,PACAPROCENABLED(r3) /* Soft disable prevents going to */
/* do_pending_int on recursive fault */
lis r3,ste_fault_count@ha
lwz r4,ste_fault_count@l(r3)
addi r4,r4,1
stw r4,ste_fault_count@l(r3)
mfspr r3,SPRG3 /* get thread */
addi r3,r3,-THREAD /* get 'current' */
lwz r3,MM(r3) /* get mm */
cmpi 0,r3,0 /* if no mm */
beq 1f /* then use context 0 (kernel) */
lwz r3,CONTEXT(r3) /* get context */
1:
/* set_context kills r0, r3, r4 and CTR */
bl set_context
lwz r3,_SOFTE(r1)
cmpi 0,r3,0
beq 5f /* skip checks if restoring disabled */
CHECKANYINT(r4,r5,r6) /* if pending interrupts, process them */
bne- do_pending_int
5:
mfspr r4,SPRG1
stb r3,PACAPROCENABLED(r4) /* Restore enabled/disabled */
b fault_exit
/*
* This code finishes saving the registers to the exception frame
* and jumps to the appropriate handler for the exception, turning
* on address translation.
*
* At this point r0-r13, r20-r24, CCR, CTR, LINK, XER, DAR and DSISR
* are saved on a stack. SRR0 is in r22, SRR1 is in r23
* r1 points to the stackframe, r1 points to the kernel stackframe
* We no longer have any dependency on data saved in the PACA, SRR0, SRR1
* DAR or DSISR. We now copy the registers to the kernel stack (which
* might cause little faults). Any little fault will be handled without
* saving state. Thus when the little fault is completed, it will rfi
* back to the original faulting instruction.
*/
.globl transfer_to_handler
transfer_to_handler:
mfspr r6,SPRG1
li r7,0
stw r7,PACAKSAVE(r6) /* Force new frame for recursive fault */
/* Restore the regs used above -- parameters to syscall */
lwz r6,GPR6(r1)
lwz r7,GPR7(r1)
stw r22,_NIP(r1)
stw r23,_MSR(r1)
SAVE_4GPRS(14, r1)
SAVE_2GPRS(18, r1)
SAVE_4GPRS(25, r1)
SAVE_2GPRS(29, r1)
SAVE_GPR(31, r1)
andi. r23,r23,MSR_PR
mfspr r23,SPRG3
addi r2,r23,-THREAD /* set r2 to current */
beq 2f /* if from user, fix up THREAD.regs */
addi r24,r1,STACK_FRAME_OVERHEAD
stw r24,PT_REGS(r23)
b 3f
2: /* if from kernel, check for stack overflow */
lwz r22,THREAD_INFO(r2)
cmplw r1,r22 /* if r1 <= current->thread_info */
ble- stack_ovf /* then the kernel stack overflowed */
3:
li r22,0
stw r22,RESULT(r1)
mfspr r23,SPRG1 /* Get Paca address */
stb r20,PACAPROCENABLED(r23) /* soft enable or disabled */
mflr r23
andi. r24,r23,0x3f00 /* get vector offset */
stw r24,TRAP(r1)
lwz r24,0(r23) /* virtual address of handler */
lwz r23,4(r23) /* where to go when done */
li r20,MSR_KERNEL
ori r20,r20,MSR_EE /* Always hard enabled */
FIX_SRR1(r20,r22)
mtspr SRR0,r24
mtspr SRR1,r20
mtlr r23
RFI /* jump to handler, enable MMU */
/*
* On kernel stack overflow, load up an initial stack pointer
* and call StackOverflow(regs), which should not return.
*/
stack_ovf:
addi r3,r1,STACK_FRAME_OVERHEAD
lis r1,init_thread_union@ha
addi r1,r1,init_thread_union@l
addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD
mfspr r24,SPRG1
li r20,0
stb r20,PACAPROCENABLED(r24) /* soft disable */
lis r24,StackOverflow@ha
addi r24,r24,StackOverflow@l
li r20,MSR_KERNEL
ori r20,r20,MSR_EE /* Always hard enabled */
FIX_SRR1(r20,r22)
mtspr SRR0,r24
mtspr SRR1,r20
RFI
/*
* Disable FP for the task which had the FPU previously,
* and save its floating-point registers in its thread_struct.
* Enables the FPU for use in the kernel on return.
* On SMP we know the fpu is free, since we give it up every
* switch. -- Cort
* Assume r20 points to PACA on entry
*/
load_up_fpu:
mfmsr r5
ori r5,r5,MSR_FP
SYNC
MTMSRD(r5) /* enable use of fpu now */
isync
/*
* For SMP, we don't do lazy FPU switching because it just gets too
* horrendously complex, especially when a task switches from one CPU
* to another. Instead we call giveup_fpu in switch_to.
*/
#ifndef CONFIG_SMP
lis r3,last_task_used_math@ha
lwz r4,last_task_used_math@l(r3)
cmpi 0,r4,0
beq 1f
addi r4,r4,THREAD /* want last_task_used_math->thread */
SAVE_32FPRS(0, r4)
mffs fr0
stfd fr0,THREAD_FPSCR-4(r4)
lwz r5,PT_REGS(r4)
lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5)
li r20,MSR_FP|MSR_FE0|MSR_FE1
andc r4,r4,r20 /* disable FP for previous task */
stw r4,_MSR-STACK_FRAME_OVERHEAD(r5)
1:
#endif /* CONFIG_SMP */
/* enable use of FP after return */
ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1
mfspr r5,SPRG3 /* current task's THREAD (phys) */
lfd fr0,THREAD_FPSCR-4(r5)
mtfsf 0xff,fr0
REST_32FPRS(0, r5)
#ifndef CONFIG_SMP
subi r4,r5,THREAD
stw r4,last_task_used_math@l(r3)
#endif /* CONFIG_SMP */
/* restore registers and return */
lwz r3,_CCR(r21)
lwz r4,_LINK(r21)
mtcrf 0xff,r3
mtlr r4
REST_GPR(1, r21)
REST_4GPRS(3, r21)
/* we haven't used ctr or xer */
mtspr SRR1,r23
mtspr SRR0,r22
REST_GPR(20, r21)
REST_2GPRS(22, r21)
lwz r21,GPR21(r21)
SYNC
RFI
/*
* FP unavailable trap from kernel - print a message, but let
* the task use FP in the kernel until it returns to user mode.
*/
KernelFP:
lwz r3,_MSR(r1)
ori r3,r3,MSR_FP
stw r3,_MSR(r1) /* enable use of FP after return */
lis r3,86f@h
ori r3,r3,86f@l
mr r4,r2 /* current */
lwz r5,_NIP(r1)
bl printk
b ret_from_except
86: .string "floating point used in kernel (task=%p, pc=%x)\n"
.align 4
/*
* giveup_fpu(tsk)
* Disable FP for the task given as the argument,
* and save the floating-point registers in its thread_struct.
* Enables the FPU for use in the kernel on return.
*/
.globl giveup_fpu
giveup_fpu:
mfmsr r5
ori r5,r5,MSR_FP
mtmsr r5 /* enable use of fpu now */
cmpi 0,r3,0
beqlr- /* if no previous owner, done */
addi r3,r3,THREAD /* want THREAD of task */
lwz r5,PT_REGS(r3)
cmpi 0,r5,0
SAVE_32FPRS(0, r3)
mffs fr0
stfd fr0,THREAD_FPSCR-4(r3)
beq 1f
lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5)
li r3,MSR_FP|MSR_FE0|MSR_FE1
andc r4,r4,r3 /* disable FP for previous task */
stw r4,_MSR-STACK_FRAME_OVERHEAD(r5)
1:
#ifndef CONFIG_SMP
li r5,0
lis r4,last_task_used_math@ha
stw r5,last_task_used_math@l(r4)
#endif /* CONFIG_SMP */
blr
#ifdef CONFIG_SMP
secondary_start:
lis r3,0,
mr r4,r24
bl identify_cpu
bl call_setup_cpu /* Call setup_cpu for this CPU */
/* get current */
HMT_MEDIUM /* Set thread priority to MEDIUM */
lis r2,current_set@h
ori r2,r2,current_set@l
slwi r24,r24,2 /* get current_set[cpu#] */
lwzx r2,r2,r24
/* stack */
addi r1,r2,THREAD_SIZE-STACK_FRAME_OVERHEAD
li r0,0
stw r0,0(r1)
/* load up the MMU */
bl load_up_mmu
/* ptr to phys current thread */
addi r4,r2,THREAD /* phys address of our thread_struct */
rlwinm r4,r4,0,0,31
mtspr SPRG3,r4
/* Set up address of Paca in current thread */
lis r23,xPaca@ha
addi r23,r23,xPaca@l
/* r24 has CPU # * 4 at this point. The Paca is 2048 bytes
long so multiply r24 by 512 to index into the array of Pacas */
slwi r24,r24,9
add r23,r23,r24
rlwinm r23,r23,0,0,31
mtspr SPRG1,r23
li r3,0
stw r3,PACAKSAVE(r23) /* 0 => r1 has kernel sp */
stb r3,PACAPROCENABLED(r23) /* Soft disabled */
/* enable MMU and jump to start_secondary */
li r4,MSR_KERNEL
ori r4,r4,MSR_EE /* Hard enabled */
lis r3,start_secondary@h
ori r3,r3,start_secondary@l
mtspr SRR0,r3
FIX_SRR1(r4,r3)
mtspr SRR1,r4
RFI
#endif /* CONFIG_SMP */
/*
* Load stuff into the MMU. Intended to be called with
* IR=0 and DR=0.
*/
load_up_mmu:
li r0,16 /* load up segment register values */
mtctr r0 /* for context 0 */
lis r3,0x2000 /* Ku = 1, VSID = 0 */
li r4,0
3: mtsrin r3,r4
addi r3,r3,0x111 /* increment VSID */
addis r4,r4,0x1000 /* address of next segment */
bdnz 3b
blr
/*
* This is where the main kernel code starts.
*/
start_here:
/* ptr to current */
lis r2,init_task@h
ori r2,r2,init_task@l
/* Set up for using our exception vectors */
addi r4,r2,THREAD /* init task's THREAD */
rlwinm r4,r4,0,0,31
mtspr SPRG3,r4
/* Get address of Paca for processor 0 */
lis r11,xPaca@ha
addi r11,r11,xPaca@l
rlwinm r11,r11,0,0,31
mtspr SPRG1,r11
li r3,0
stw r3,PACAKSAVE(r11) /* 0 => r1 has kernel sp */
stb r3,PACAPROCENABLED(r11) /* Soft disabled */
lis r1,init_thread_union@ha
addi r1,r1,init_thread_union@l
li r0,0
stwu r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
/* fix klimit for system map */
lis r6,embedded_sysmap_end@ha
lwz r7,embedded_sysmap_end@l(r6)
cmpi 0,r7,0
beq 5f
lis r6, KERNELBASE@h
add r7,r7,r6
addi r7,r7,4095
li r6,0x0FFF
andc r7,r7,r6
lis r6,klimit@ha
stw r7,klimit@l(r6)
5:
/*
* Decide what sort of machine this is and initialize the MMU.
*/
bl early_init /* We have to do this with MMU on */
mr r3,r31
mr r4,r30
mr r5,r29
mr r6,r28
mr r7,r27
li r6,0 /* No cmdline parameters */
bl platform_init
bl MMU_init
bl load_up_mmu
li r4,MSR_KERNEL
ori r4,r4,MSR_EE /* Hard enabled */
FIX_SRR1(r4,r5)
lis r3,start_kernel@h
ori r3,r3,start_kernel@l
mtspr SRR0,r3
mtspr SRR1,r4
RFI /* ensure correct MSR and jump to
start_kernel */
hash_page:
mflr r21 /* Save LR in r21 */
/*
* We hard enable here (but first soft disable) so that the hash_page
* code can spin on the mmu_hash_lock without problem on a shared
* processor
*/
li r0,0
stb r0,PACAPROCENABLED(r20) /* Soft disable */
mfmsr r0
ori r0,r0,MSR_EE
mtmsr r0 /* Hard enable */
bl create_hpte /* add the hash table entry */
/*
* Now go back to hard disabled
*/
mfmsr r0
li r4,0
ori r4,r4,MSR_EE
andc r0,r0,r4
mtmsr r0 /* Hard disable */
lwz r0,_SOFTE(r1)
mtlr r21 /* restore LR */
mr r21,r1 /* restore r21 */
cmpi 0,r0,0 /* See if we will soft enable in */
/* fault_exit */
beq 5f /* if not, skip checks */
CHECKANYINT(r4,r5,r6) /* if pending interrupts, process them */
bne- do_pending_int
5: stb r0,PACAPROCENABLED(r20) /* Restore soft enable/disable */
cmpi 0,r3,0 /* check return code form create_hpte */
bnelr
/*
* htab_reloads counts the number of times we have to fault an
* HPTE into the hash table. This should only happen after a
* fork (because fork does a flush_tlb_mm) or a vmalloc or ioremap.
* Where a page is faulted into a process's address space,
* update_mmu_cache gets called to put the HPTE into the hash table
* and those are counted as preloads rather than reloads.
*/
lis r2,htab_reloads@ha
lwz r3,htab_reloads@l(r2)
addi r3,r3,1
stw r3,htab_reloads@l(r2)
fault_exit:
lwz r3,_CCR(r1)
lwz r4,_LINK(r1)
lwz r5,_CTR(r1)
lwz r6,_XER(r1)
mtcrf 0xff,r3
mtlr r4
mtctr r5
mtspr XER,r6
lwz r0,GPR0(r1)
REST_8GPRS(2, r1)
REST_4GPRS(10, r1)
FIX_SRR1(r23,r20)
mtspr SRR1,r23
mtspr SRR0,r22
REST_4GPRS(20, r1)
lwz r1,GPR1(r1)
RFI
/*
* Set up the segment registers for a new context.
* context in r3
*/
_GLOBAL(set_context)
mulli r3,r3,897 /* multiply context by skew factor */
rlwinm r3,r3,4,8,27 /* VSID = (context & 0xfffff) << 4 */
addis r3,r3,0x6000 /* Set Ks, Ku bits */
li r0,NUM_USER_SEGMENTS
mtctr r0
li r4,0
3:
mtsrin r3,r4
addi r3,r3,0x111 /* next VSID */
rlwinm r3,r3,0,8,3 /* clear out any overflow from VSID field */
addis r4,r4,0x1000 /* address of next segment */
bdnz 3b
isync
set_kernel_segregs:
/*
* Reload the last four segment registers because they
* might have been clobbered by the hypervisor if we
* are running on a shared processor
*/
lis r3,0x2000 /* Set Ku = 1 */
addi r3,r3,0xCCC /* Set VSID = CCC */
lis r4,0xC000 /* Set SR = C */
li r0,4 /* Load regs C, D, E and F */
mtctr r0
4: mtsrin r3,r4
addi r3,r3,0x111 /* increment VSID */
addis r4,r4,0x1000 /* address of next segment */
bdnz 4b
isync
blr
/* Hypervisor call
*
* Invoke the iSeries hypervisor (PLIC) via the System Call instruction.
* Parameters are passed to this routine in registers r3 - r10 and are
* converted to 64-bit by combining registers. eg. r3 <- r3
* r4 <- r5,r6, r5 <- r7,r8, r6 <- r9,r10
*
* r3 contains the HV function to be called
* r5,r6 contain the first 64-bit operand
* r7,r8 contain the second 64-bit operand
* r9,r10 contain the third 64-bit operand
* caller's stack frame +8 contains the fourth 64-bit operand
* caller's stack frame +16 contains the fifth 64-bit operand
* caller's stack frame +24 contains the sixth 64-bit operand
* caller's stack frame +32 contains the seventh 64-bit operand
*
*/
_GLOBAL(HvCall)
_GLOBAL(HvCall0)
_GLOBAL(HvCall1)
_GLOBAL(HvCall2)
_GLOBAL(HvCall3)
_GLOBAL(HvCall4)
_GLOBAL(HvCall5)
_GLOBAL(HvCall6)
_GLOBAL(HvCall7)
/*
* Stack a frame and save one reg so we can hang on to
* the old MSR
*/
stwu r1,-64(r1)
stw r31,60(r1)
stw r22,24(r1)
stw r23,28(r1)
stw r24,32(r1)
stw r25,36(r1)
stw r26,40(r1)
stw r27,44(r1)
stw r28,48(r1)
stw r29,52(r1)
/*
* The hypervisor assumes CR fields 0-4 are volatile, but
* gcc assumes CR fields 2-7 are non-volatile.
* We must save and restore the CR here
*/
mfcr r31
stw r31,20(r1)
/* Before we can convert to using 64-bit registers we must
* soft disable external interrupts as the interrupt handlers
* don't preserve the high half of the registers
*/
mfspr r11,SPRG1 /* Get the Paca pointer */
lbz r31,PACAPROCENABLED(r11) /* Get soft enable/disable flag */
li r0,0
stb r0,PACAPROCENABLED(r11) /* Soft disable */
/* Get parameters four through seven */
lwz r22,72(r1)
lwz r23,76(r1)
lwz r24,80(r1)
lwz r25,84(r1)
lwz r26,88(r1)
lwz r27,92(r1)
lwz r28,96(r1)
lwz r29,100(r1)
/* Now it is safe to operate on 64-bit registers
*
* Format the operands into the 64-bit registers
*
*/
rldicr r5,r5,32,31 /* r5 = r5 << 32 */
rldicl r6,r6,0,32 /* r6 = r6 & 0x00000000ffffffff */
or r4,r5,r6 /* r4 = r5 | r6 */
rldicr r7,r7,32,31 /* r7 = r7 << 32 */
rldicl r8,r8,0,32 /* r8 = r8 & 0x00000000ffffffff */
or r5,r7,r8 /* r5 = r7 | r8 */
rldicr r9,r9,32,31 /* r9 = r9 << 32 */
rldicl r10,r10,0,32 /* r10 = r10 & 0x00000000ffffffff */
or r6,r9,r10 /* r6 = r9 | r10 */
rldicr r22,r22,32,31 /* r22 = r22 << 32 */
rldicl r23,r23,0,32 /* r23 = r23 & 0x00000000ffffffff */
or r7,r22,r23 /* r7 = r22 | r23 */
rldicr r24,r24,32,31 /* r24 = r24 << 32 */
rldicl r25,r25,0,32 /* r25 = r25 & 0x00000000ffffffff */
or r8,r24,r25 /* r8 = r24 | r25 */
rldicr r26,r26,32,31 /* r26 = r26 << 32 */
rldicl r27,r27,0,32 /* r27 = r27 & 0x00000000ffffffff */
or r9,r26,r27 /* r9 = r26 | r27 */
rldicr r28,r28,32,31 /* r28 = r28 << 32 */
rldicl r29,r29,0,32 /* r29 = r29 & 0x00000000ffffffff */
or r10,r28,r29 /* r10 = r28 | r29 */
/*
* Extract the hypervisor function call number from R3
* and format it into the 64-bit R3.
*/
rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */
or r3,r3,r0 /* r3 = r3 | r0 */
/*
* r0 = 0xffffffffffffffff indicates a hypervisor call
*/
li r0,-1 /* r1 = 0xffffffffffffffff */
/* Invoke the hypervisor via the System Call instruction */
sc
HMT_MEDIUM
/* Return value in 64-bit R3
* format it into R3 and R4
*/
rldicl r4,r3,0,32 /* r4 = r3 & 0x00000000ffffffff */
rldicl r3,r3,32,32 /* r3 = (r3 >> 32) & 0x00000000ffffffff */
/* We are now done with 64-bit registers it is safe to touch
* the stack again.
*/
lwz r22,24(r1)
lwz r23,28(r1)
lwz r24,32(r1)
lwz r25,36(r1)
lwz r26,40(r1)
lwz r27,44(r1)
lwz r28,48(r1)
lwz r29,52(r1)
/* While we were running in the hypervisor, a decrementer or
* external interrupt may have occured. If we are about to
* enable here, we must check for these and process them
*/
cmpi 0,r31,0 /* check if going to enable */
beq 1f /* skip checks if staying disabled */
/* Save r3, r4 and LR */
stw r3,52(r1)
stw r4,48(r1)
mflr r3
stw r3,44(r1)
/* enable and check for decrementers/lpEvents */
mr r3,r31
bl local_irq_restore
/* Restore r3, r4 and LR */
lwz r3,44(r1)
mtlr r3
lwz r3,52(r1)
lwz r4,48(r1)
1:
/*
* Unstack the frame and restore r31 and the CR
*/
lwz r31,20(r1)
mtcrf 0xff,r31
lwz r31,60(r1)
lwz r1,0(r1)
blr
/* Hypervisor call with return data
*
* Invoke the iSeries hypervisor (PLIC) via the System Call instruction.
* The Hv function ID is passed in r3
* The address of the return data structure is passed in r4
* Parameters are passed to this routine in registers r5 - r10 and are
* converted to 64-bit by combining registers. eg. r3 <- r3
* r4 <- r5,r6, r5 <- r7,r8, r6 <- r9,r10
*
* r3 contains the HV function to be called
* r4 contains the address of the return data structure
* r5,r6 contain the first 64-bit operand
* r7,r8 contain the second 64-bit operand
* r9,r10 contain the third 64-bit operand
* caller's stack frame +8 contains the fourth 64-bit operand
* caller's stack frame +16 contains the fifth 64-bit operand
* caller's stack frame +24 contains the sixth 64-bit operand
* caller's stack frame +32 contains the seventh 64-bit operand
*
*/
_GLOBAL(HvCallRet16)
_GLOBAL(HvCall0Ret16)
_GLOBAL(HvCall1Ret16)
_GLOBAL(HvCall2Ret16)
_GLOBAL(HvCall3Ret16)
_GLOBAL(HvCall4Ret16)
_GLOBAL(HvCall5Ret16)
_GLOBAL(HvCall6Ret16)
_GLOBAL(HvCall7Ret16)
/*
* Stack a frame and save some regs
*/
stwu r1,-64(r1)
stw r31,60(r1)
stw r30,56(r1)
stw r22,24(r1)
stw r23,28(r1)
stw r24,32(r1)
stw r25,36(r1)
stw r26,40(r1)
stw r27,44(r1)
stw r28,48(r1)
stw r29,52(r1)
mr r30,r4 /* Save return data address */
/*
* The hypervisor assumes CR fields 0-4 are volatile, but
* gcc assumes CR fields 2-7 are non-volatile.
* We must save and restore the CR here
*/
mfcr r31
stw r31,20(r1)
/* Before we can convert to using 64-bit registers we must
* soft disable external interrupts as the interrupt handlers
* don't preserve the high half of the registers
*/
mfspr r11,SPRG1 /* Get the Paca pointer */
lbz r31,PACAPROCENABLED(r11) /* Get soft enable/disable flag */
li r0,0
stb r0,PACAPROCENABLED(r11) /* Soft disable */
/* Get parameters four through seven */
lwz r22,76(r1)
lwz r23,80(r1)
lwz r24,84(r1)
lwz r25,88(r1)
lwz r26,92(r1)
lwz r27,96(r1)
lwz r28,100(r1)
lwz r29,104(r1)
/* Now it is safe to operate on 64-bit registers
*
*/
/*
* Format the operands into the 64-bit registers
*/
rldicr r5,r5,32,31 /* r5 = r5 << 32 */
rldicl r6,r6,0,32 /* r6 = r6 & 0x00000000ffffffff */
or r4,r5,r6 /* r4 = r5 | r6 */
rldicr r7,r7,32,31 /* r7 = r7 << 32 */
rldicl r8,r8,0,32 /* r8 = r8 & 0x00000000ffffffff */
or r5,r7,r8 /* r5 = r7 | r8 */
rldicr r9,r9,32,31 /* r9 = r9 << 32 */
rldicl r10,r10,0,32 /* r10 = r10 & 0x00000000ffffffff */
or r6,r9,r10 /* r6 = r9 | r10 */
rldicr r22,r22,32,31 /* r22 = r22 << 32 */
rldicl r23,r23,0,32 /* r23 = r23 & 0x00000000ffffffff */
or r7,r22,r23 /* r7 = r22 | r23 */
rldicr r24,r24,32,31 /* r24 = r24 << 32 */
rldicl r25,r25,0,32 /* r25 = r25 & 0x00000000ffffffff */
or r8,r24,r25 /* r8 = r24 | r25 */
rldicr r26,r26,32,31 /* r26 = r26 << 32 */
rldicl r27,r27,0,32 /* r27 = r27 & 0x00000000ffffffff */
or r9,r26,r27 /* r9 = r26 | r27 */
rldicr r28,r28,32,31 /* r28 = r28 << 32 */
rldicl r29,r29,0,32 /* r29 = r29 & 0x00000000ffffffff */
or r10,r28,r29 /* r10 = r28 | r29 */
/*
* Extract the hypervisor function call number from R3
* and format it into the 64-bit R3.
*/
rldicr r0,r3,32,15 /* r4 = (r3 << 32) & 0xffff000000000000 */
rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */
or r3,r3,r0 /* r3 = r3 | r4 */
/*
* r0 = 0xffffffffffffffff indicates a hypervisor call
*/
li r0,-1 /* r1 = 0xffffffffffffffff */
/* Invoke the hypervisor via the System Call instruction */
sc
HMT_MEDIUM
/* Return values in 64-bit R3, R4, R5 and R6
* place R3 and R4 into data structure, R5 into R3,R4
*/
rldicl r6,r3,32,32 /* r6 = (r3 >> 32) & 0x00000000ffffffff */
rldicl r7,r3,0,32 /* r7 = r3 & 0x00000000ffffffff */
rldicl r8,r4,32,32 /* r8 = (r4 >> 32) & 0x00000000ffffffff */
rldicl r9,r4,0,32 /* r9 = r4 & 0x00000000ffffffff */
rldicl r4,r5,0,32 /* r4 = r5 & 0x00000000ffffffff */
rldicl r3,r5,32,32 /* r3 = (r5 >> 32) & 0x00000000ffffffff */
/* We are now done with 64-bit registers it is safe to touch
* the stack again.
*/
stw r6,0(r30) /* Save returned data */
stw r7,4(r30)
stw r8,8(r30)
stw r9,12(r30)
lwz r22,24(r1)
lwz r23,28(r1)
lwz r24,32(r1)
lwz r25,36(r1)
lwz r26,40(r1)
lwz r27,44(r1)
lwz r28,48(r1)
lwz r29,52(r1)
/* While we were running in the hypervisor, a decrementer or
* external interrupt may have occured. If we are about to
* enable here, we must check for these and process them
*/
cmpi 0,r31,0 /* check if going to enable */
beq 1f /* skip checks if staying disabled */
/* Save r3, r4 and LR */
stw r3,48(r1)
stw r4,44(r1)
mflr r3
stw r3,40(r1)
/* enable and check for decrementers/lpEvents */
mr r3,r31
bl local_irq_restore
lwz r3,40(r1)
mtlr r3
lwz r3,48(r1)
lwz r4,44(r1)
1:
/*
* Unstack the frame and restore r30, r31 and CR
*/
lwz r31,20(r1)
mtcrf 0xff,r31
lwz r30,56(r1)
lwz r31,60(r1)
lwz r1,0(r1)
blr
/* Hypervisor call (use no stack)
*
* These functions must be called with interrupts soft disabled.
* The caller is responsible for saving the non-volatile CR
* The operands should already be formatted into the 64-bit registers
*
* Invoke the iSeries hypervisor (PLIC) via the System Call instruction.
*
* r3 contains the HV function to be called
* r4 contains the first 64-bit operand
* r5 contains the second 64-bit operand
* r6 contains the third 64-bit operand
* r7 contains the fourth 64-bit operand
* r8 contains the fifth 64-bit operand
* r9 contains the sixth 64-bit operand
* r10 contains the seventh 64-bit operand
*
* data is returned in 64-bit registers r3-r6
*
*/
_GLOBAL(HvXCall)
_GLOBAL(HvXCall0)
_GLOBAL(HvXCall1)
_GLOBAL(HvXCall2)
_GLOBAL(HvXCall3)
/*
* Extract the hypervisor function call number from R3
* and format it into the 64-bit R3.
*/
rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */
or r3,r3,r0 /* r3 = r3 | r0 */
/*
* r0 = 0xffffffffffffffff indicates a hypervisor call
*/
li r0,-1 /* r1 = 0xffffffffffffffff */
/* Invoke the hypervisor via the System Call instruction */
sc
blr
_GLOBAL(__setup_cpu_power3)
blr
_GLOBAL(__setup_cpu_power4)
blr
_GLOBAL(__setup_cpu_generic)
blr
_GLOBAL(iSeries_check_intr)
mflr r31
lwz r5,_SOFTE(r1)
cmpi 0,r5,0
beqlr
mfspr r5,SPRG1
lbz r5,PACAPROCENABLED(r5)
cmpi 0,r5,0
bnelr
/* Check for lost decrementer interrupts.
* (If decrementer popped while we were in the hypervisor)
* (calls timer_interrupt if so)
*/
3: CHECKDECR(r4,r5)
/* Check for pending interrupts. If no interrupts pending,
* then CR0 = "eq" and r4 == 0
* (kills registers r5 and r6)
*/
beq+ 1f
addi r3,r1,STACK_FRAME_OVERHEAD
bl timer_interrupt
1:
CHECKLPQUEUE(r4,r5,r6)
beq+ 2f
addi r3,r1,STACK_FRAME_OVERHEAD
bl do_IRQ
b 3b
2: mtlr r31
blr
/*
* Fake an interrupt from kernel mode.
* This is used when enable_irq loses an interrupt.
* We only fill in the stack frame minimally.
*/
_GLOBAL(fake_interrupt)
mflr r0
stw r0,4(r1)
stwu r1,-INT_FRAME_SIZE(r1)
stw r0,_NIP(r1)
stw r0,_LINK(r1)
mfmsr r3
stw r3,_MSR(r1)
li r0,0x0fac
stw r0,TRAP(r1)
addi r3,r1,STACK_FRAME_OVERHEAD
li r4,1
bl do_IRQ
addi r1,r1,INT_FRAME_SIZE
lwz r0,4(r1)
mtlr r0
blr
/*
* Fake a decrementer from kernel mode.
* This is used when the decrementer pops in
* the hypervisor. We only fill in the stack
* frame minimally
*/
_GLOBAL(fake_decrementer)
mflr r0
stw r0,4(r1)
stwu r1,-INT_FRAME_SIZE(r1)
stw r0,_NIP(r1)
stw r0,_LINK(r1)
mfmsr r3
stw r3,_MSR(r1)
li r0,0x0fac
stw r0,TRAP(r1)
addi r3,r1,STACK_FRAME_OVERHEAD
bl timer_interrupt
addi r1,r1,INT_FRAME_SIZE
lwz r0,4(r1)
mtlr r0
blr
_GLOBAL(create_hpte)
stwu r1,-INT_FRAME_SIZE(r1)
stw r0,GPR0(r1)
lwz r0,0(r1)
stw r0,GPR1(r1)
/* r3-r13 are caller saved */
stw r2,GPR2(r1)
SAVE_8GPRS(4, r1)
SAVE_2GPRS(12, r1)
SAVE_4GPRS(20,r1)
mfspr r20,XER
mfctr r22
stw r20,_XER(r1)
stw r22,_CTR(r1)
mflr r20
mfmsr r22
stw r20,_NIP(r1)
stw r22,_MSR(r1)
stw r20,_LINK(r1)
bl iSeries_create_hpte
lwz r0,GPR0(r1)
lwz r2,GPR2(r1)
REST_8GPRS(4, r1)
REST_2GPRS(12, r1)
lwz r20,_NIP(r1)
lwz r22,_XER(r1)
mtlr r20
mtspr XER,r22
lwz r20,_CTR(r1)
mtctr r20
REST_4GPRS(20,r1)
addi r1,r1,INT_FRAME_SIZE
blr
###
### extern void abort(void)
###
### Invoke the hypervisor to kill the partition.
###
_GLOBAL(abort)
/*
* We put a few things here that have to be page-aligned.
* This stuff goes at the beginning of the data segment,
* which is page-aligned.
*/
.data
.globl sdata
sdata:
.globl empty_zero_page
empty_zero_page:
.space 4096
.globl swapper_pg_dir
swapper_pg_dir:
.space 4096
/*
* This space gets a copy of optional info passed to us by the bootstrap
* Used to pass parameters into the kernel like root=/dev/sda1, etc.
*/
.globl cmd_line
cmd_line:
.space 512
/*
* BK Id: %F% %I% %G% %U% %#%
*/
/*
* Idle task for iSeries.
*
* 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/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/cache.h>
#include <asm/cputable.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCall.h>
#include <asm/hardirq.h>
static void yield_shared_processor(void);
static void run_light_on(int on);
extern unsigned long yield_count;
void iSeries_idle(void)
{
if (!need_resched()) {
/* Turn off the run light */
run_light_on(0);
yield_shared_processor();
HMT_low();
#ifdef CONFIG_SMP
set_thread_flag(TIF_POLLING_NRFLAG);
while (!need_resched())
barrier();
clear_thread_flag(TIF_POLLING_NRFLAG);
#endif
}
if (need_resched()) {
run_light_on(1);
schedule();
return;
}
}
extern void fake_interrupt(void);
extern u64 get_tb64(void);
static void run_light_on(int on)
{
unsigned long CTRL;
CTRL = mfspr(CTRLF);
CTRL = on? (CTRL | RUNLATCH): (CTRL & ~RUNLATCH);
mtspr(CTRLT, CTRL);
}
static void yield_shared_processor(void)
{
struct Paca *paca;
u64 tb;
/* Poll for I/O events */
local_irq_disable();
local_irq_enable();
paca = (struct Paca *)mfspr(SPRG1);
if ( paca->xLpPaca.xSharedProc ) {
HvCall_setEnabledInterrupts( HvCall_MaskIPI |
HvCall_MaskLpEvent |
HvCall_MaskLpProd |
HvCall_MaskTimeout );
/*
* Check here for any of the above pending...
* IPI and Decrementers are indicated in ItLpPaca
* LpEvents are indicated on the LpQueue
*
* Disabling/enabling will check for LpEvents, IPIs
* and decrementers
*/
local_irq_disable();
local_irq_enable();
++yield_count;
/* Get current tb value */
tb = get_tb64();
/* Compute future tb value when yield will expire */
tb += tb_ticks_per_jiffy;
HvCall_yieldProcessor( HvCall_YieldTimed, tb );
/* Check here for any of the above pending or timeout expired*/
local_irq_disable();
/*
* The decrementer stops during the yield. Just force
* a fake decrementer now and the timer_interrupt
* code will straighten it all out
*/
paca->xLpPaca.xDecrInt = 1;
local_irq_enable();
}
}
/*
* This file contains miscellaneous low-level functions.
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
* and Paul Mackerras.
* Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com)
* updated by Dave Boutcher (boutcher@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 of the License, or (at your option) any later version.
*
*/
#include <linux/config.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <asm/errno.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/cache.h>
#include <asm/ppc_asm.h>
#include <asm/offsets.h>
#include "iSeries_asm.h"
.text
.align 5
#ifdef CONFIG_SMP
.comm mmu_hash_lock,4
#endif /* CONFIG_SMP */
_GLOBAL(is_msr_enabled)
mfmsr r3
andi. r3,r3,MSR_EE
beqlr /* Return r3=0 indicating disabled */
li r3,1
blr /* Return r3=1 indicating enabled */
_GLOBAL(is_soft_enabled)
mfspr r3,SPRG1
lbz r3,PACAPROCENABLED(r3)
blr
_GLOBAL(get_tb64)
/* hard disable because we are using a 64-bit register
* and we don't want it to get trashed in an interrupt
* handler
*/
mfmsr r5
rlwinm r0,r5,0,17,15 /* clear MSR_EE in r0 */
mtmsr r0 /* hard disable */
mftb r3
rldicl r4,r3,0,32
rldicl r3,r3,32,32
mtmsr r5 /* restore MSR_EE */
blr
/* void local_save_flags(unsigned long *flags) */
_GLOBAL(local_save_flags_ptr)
mfspr r4,SPRG1 /* Get Paca pointer */
lbz r4,PACAPROCENABLED(r4)
stw r4,0(r3)
blr
_GLOBAL(local_save_flags_ptr_end)
/* void local_irq_restore(unsigned long flags) */
_GLOBAL(local_irq_restore)
cmpi 0,r3,0 /* Are we enabling? */
beq 0f /* No - then skip interrupt checks */
3:
mfspr r4,SPRG1
lbz r4,PACAPROCENABLED(r4)
cmpi 0,r4,0 /* Are we already enabled? */
bne 0f /* Yes - then skip interrupt checks */
CHECKDECR(r4,r5)
bne- do_fake_decrementer
CHECKLPQUEUE(r4,r5,r6)
bne- do_lost_interrupts
2:
mfmsr r0
rlwinm r0,r0,0,17,15 /* hard disable */
mtmsr r0
CHECKANYINT(r4,r5,r6)
ori r0,r0,MSR_EE
beq 1f
mtmsr r0 /* hard enable */
b 3b /* process more interrupts */
1:
mfspr r4,SPRG1
stb r3,PACAPROCENABLED(r4)
mtmsr r0 /* hard enable */
blr
0:
mfspr r4,SPRG1
stb r3,PACAPROCENABLED(r4)
blr
_GLOBAL(local_irq_restore_end)
_GLOBAL(local_irq_disable)
mfspr r4,SPRG1
li r3,0
stb r3,PACAPROCENABLED(r4)
blr /* Done */
_GLOBAL(local_irq_disable_end)
_GLOBAL(local_irq_enable)
li r3,1
b local_irq_restore
_GLOBAL(local_irq_enable_end)
/*
* We were about to enable interrupts but we have to simulate
* some interrupts that were lost by enable_irq first.
*/
_GLOBAL(do_lost_interrupts)
stwu r1,-32(r1)
mflr r0
stw r0,36(r1)
stw r3,28(r1)
1: bl fake_interrupt
bl check_softirqs
mfmsr r0
rlwinm r0,r0,0,17,15 /* hard disable */
mtmsr r0
CHECKANYINT(r4,r5,r6)
ori r0,r0,MSR_EE
beq 2f
mtmsr r0 /* hard enable */
b 1b /* process more interrupts */
2: lwz r3,28(r1)
mfspr r4,SPRG1
stb r3,PACAPROCENABLED(r4) /* restore soft interrupt state */
mtmsr r0 /* hard enable */
lwz r0,36(r1)
mtlr r0
addi r1,r1,32
blr
/*
* Simulate a decrementer interrupt
*/
do_fake_decrementer:
stwu r1,-32(r1)
mflr r0
stw r0,36(r1)
stw r3,28(r1)
bl fake_decrementer
bl check_softirqs
lwz r0,36(r1)
mtlr r0
lwz r3,28(r1)
addi r1,r1,32
mfmsr r0 /* hard disable */
rlwinm r0,r0,0,17,15
mtmsr r0
CHECKANYINT(r4,r5,r6) /* Check for any interrupts pending */
ori r0,r0,MSR_EE
beq 1f
mtmsr r0 /* hard enable */
b do_lost_interrupts /* Handle more interrupts */
1: mfspr r4,SPRG1
stb r3,PACAPROCENABLED(r4) /* soft enable */
mtmsr r0 /* hard enable */
blr
/*
* do softirqs if necessary
*/
check_softirqs:
stwu r1,-32(r1)
mflr r0
stw r0,36(r1)
lis r4,irq_stat@ha
addi r4,r4,irq_stat@l
#ifdef CONFIG_SMP
lwz r3,CPU(r2)
slwi r3,r3,7
add r4,r4,r3
#endif
lwz r5,0(r4)
lwz r4,4(r4)
and. r5,r5,r4
beq+ 2f
bl do_softirq
2:
lwz r0,36(r1)
mtlr r0
addi r1,r1,32
blr
/*
* Write any modified data cache blocks out to memory
* and invalidate the corresponding instruction cache blocks.
*
* flush_icache_range(unsigned long start, unsigned long stop)
*/
_GLOBAL(flush_icache_range)
/*
* Flush the data cache to memory
*
* Different models of iSeries's have different cache line sizes
* and in some cases i-cache and d-cache line sizes differ from
* each other.
*/
lis r7,iSeries_dcache_line_size@ha
lhz r7,iSeries_dcache_line_size@l(r7)
addi r5,r7,-1
andc r6,r3,r5 /* round low to line bdy */
subf r8,r6,r4 /* compute length */
add r8,r8,r5 /* ensure we get enough */
lis r9,iSeries_log_dcache_line_size@ha
lhz r9,iSeries_log_dcache_line_size@l(r9)
srw. r8,r8,r9 /* compute line count */
beqlr /* nothing to do? */
mtctr r8
1: dcbst 0,r6
add r6,r6,r7
bdnz 1b
sync
/* Now invalidate the instruction cache */
lis r7,iSeries_icache_line_size@ha
lhz r7,iSeries_icache_line_size@l(r7)
addi r5,r7,-1
andc r6,r3,r5 /* round low to line bdy */
subf r8,r6,r4 /* compute length */
add r8,r8,r5
lis r9,iSeries_log_icache_line_size@ha
lhz r9,iSeries_log_icache_line_size@l(r9)
srw. r8,r8,r9 /* compute line count */
beqlr /* nothing to do? */
mtctr r8
2: icbi 0,r6
add r6,r6,r7
bdnz 2b
sync
isync
blr
/*
* Like above, but only do the D-cache.
*
* flush_dcache_range(unsigned long start, unsigned long stop)
*/
_GLOBAL(flush_dcache_range)
/*
* Flush the data cache to memory
*
* Different models of iSeries's have different cache line sizes
*/
lis r7,iSeries_dcache_line_size@ha
lhz r7,iSeries_dcache_line_size@l(r7)
addi r5,r7,-1
andc r6,r3,r5 /* round low to line bdy */
subf r8,r6,r4 /* compute length */
add r8,r8,r5 /* ensure we get enough */
lis r9,iSeries_log_dcache_line_size@ha
lhz r9,iSeries_log_dcache_line_size@l(r9)
srw. r8,r8,r9 /* compute line count */
beqlr /* nothing to do? */
mtctr r8
0: dcbst 0,r6
add r6,r6,r7
bdnz 0b
sync
blr
/*
* Flush a particular page from the data cache to RAM.
* Note: this is necessary because the instruction cache does *not*
* snoop from the data cache.
*
* void __flush_page_to_ram(void *page)
* void __flush_dcache_icache(void *page)
*/
_GLOBAL(__flush_page_to_ram)
_GLOBAL(__flush_dcache_icache)
/*
* Flush the data cache to memory
*
* Different models of iSeries's have different cache line sizes
*/
/* Flush the dcache */
rlwinm r3,r3,0,0,19 /* Page align */
lis r4,iSeries_dcache_lines_per_page@ha
lhz r4,iSeries_dcache_lines_per_page@l(r4)
lis r5,iSeries_dcache_line_size@ha
lhz r5,iSeries_dcache_line_size@l(r5)
mr r6,r3
mtctr r4
0: dcbst 0,r6
add r6,r6,r5
bdnz 0b
sync
/* Now invalidate the icache */
lis r4,iSeries_icache_lines_per_page@ha
lhz r4,iSeries_icache_lines_per_page@l(r4)
lis r5,iSeries_icache_line_size@ha
lhz r5,iSeries_icache_line_size@l(r5)
mtctr r4
1: icbi 0,r3
add r3,r3,r5
bdnz 1b
sync
isync
blr
/*
* Flush a particular page from the instruction cache.
* Note: this is necessary because the instruction cache does *not*
* snoop from the data cache.
*
* void __flush_icache_page(void *page)
*/
_GLOBAL(__flush_icache_page)
/*
* Different models of iSeries's have different cache line sizes
*/
/* Invalidate the icache */
rlwinm r3,r3,0,0,19 /* Page align */
lis r4,iSeries_icache_lines_per_page@ha
lhz r4,iSeries_icache_lines_per_page@l(r4)
lis r5,iSeries_icache_line_size@ha
lhz r5,iSeries_icache_line_size@l(r5)
mtctr r4
1: icbi 0,r3
add r3,r3,r5
bdnz 1b
sync
isync
blr
/*
* Clear a page using the dcbz instruction, which doesn't cause any
* memory traffic (except to write out any cache lines which get
* displaced). This only works on cacheable memory.
*/
_GLOBAL(clear_page)
rlwinm r3,r3,0,0,19 /* Page align */
lis r4,iSeries_dcache_lines_per_page@ha
lhz r4,iSeries_dcache_lines_per_page@l(r4)
lis r5,iSeries_dcache_line_size@ha
lhz r5,iSeries_dcache_line_size@l(r5)
mtctr r4
0: dcbz 0,r3
add r3,r3,r5
bdnz 0b
blr
/*
* Copy a whole page. We use the dcbz instruction on the destination
* to reduce memory traffic (it eliminates the unnecessary reads of
* the destination into cache). This requires that the destination
* is cacheable.
*/
#define COPY_16_BYTES \
lwz r6,4(r4); \
lwz r7,8(r4); \
lwz r8,12(r4); \
lwzu r9,16(r4); \
stw r6,4(r3); \
stw r7,8(r3); \
stw r8,12(r3); \
stwu r9,16(r3)
_GLOBAL(copy_page)
rlwinm r3,r3,0,0,19 /* Page align */
rlwinm r4,r4,0,0,19
lis r5,iSeries_dcache_lines_per_page@ha
lhz r5,iSeries_dcache_lines_per_page@l(r5)
lis r6,iSeries_dcache_line_size@ha
lhz r0,iSeries_dcache_line_size@l(r6)
mtctr r5
addi r3,r3,-4
addi r4,r4,-4
li r10,4
cmpi 0,r0,32
beq do_32_byte_line
cmpi 0,r0,64
beq do_64_byte_line
cmpi 0,r0,128
beq do_128_byte_line
/* We don't have code specifically for this cache line size */
/* Assume that the cache line size is at least 16 (and of */
/* course a multiple of 16) */
/* This code will work for all power-of-2 cache line sizes */
/* from 16 to 4096 */
1: mr r5,r0
dcbz r10,r3
0: COPY_16_BYTES
addi r5,r5,-16
or. r5,r5,r5
bne 0b
bdnz 1b
blr
do_32_byte_line:
dcbz r10,r3
COPY_16_BYTES
COPY_16_BYTES
bdnz do_32_byte_line
blr
do_64_byte_line:
dcbz r10,r3
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
bdnz do_64_byte_line
blr
do_128_byte_line:
dcbz r10,r3
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
COPY_16_BYTES
bdnz do_128_byte_line
blr
...@@ -502,7 +502,6 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) ...@@ -502,7 +502,6 @@ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq)
spin_unlock(&desc->lock); spin_unlock(&desc->lock);
} }
#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_pic.c */
void do_IRQ(struct pt_regs *regs) void do_IRQ(struct pt_regs *regs)
{ {
int irq, first = 1; int irq, first = 1;
...@@ -525,7 +524,6 @@ void do_IRQ(struct pt_regs *regs) ...@@ -525,7 +524,6 @@ void do_IRQ(struct pt_regs *regs)
ppc_spurious_interrupts++; ppc_spurious_interrupts++;
irq_exit(); irq_exit();
} }
#endif /* CONFIG_PPC_ISERIES */
unsigned long probe_irq_on (void) unsigned long probe_irq_on (void)
{ {
......
...@@ -136,7 +136,6 @@ _GLOBAL(identify_cpu) ...@@ -136,7 +136,6 @@ _GLOBAL(identify_cpu)
* r3 = data offset (not changed) * r3 = data offset (not changed)
*/ */
_GLOBAL(do_cpu_ftr_fixups) _GLOBAL(do_cpu_ftr_fixups)
#ifndef CONFIG_PPC_ISERIES
/* Get CPU 0 features */ /* Get CPU 0 features */
addis r6,r3,cur_cpu_spec@ha addis r6,r3,cur_cpu_spec@ha
addi r6,r6,cur_cpu_spec@l addi r6,r6,cur_cpu_spec@l
...@@ -180,9 +179,6 @@ _GLOBAL(do_cpu_ftr_fixups) ...@@ -180,9 +179,6 @@ _GLOBAL(do_cpu_ftr_fixups)
sync /* additional sync needed on g4 */ sync /* additional sync needed on g4 */
isync isync
b 1b b 1b
#else /* CONFIG_PPC_ISERIES */
blr
#endif /* CONFIG_PPC_ISERIES */
/* /*
* call_setup_cpu - call the setup_cpu function for this cpu * call_setup_cpu - call the setup_cpu function for this cpu
...@@ -205,7 +201,6 @@ _GLOBAL(call_setup_cpu) ...@@ -205,7 +201,6 @@ _GLOBAL(call_setup_cpu)
mr r4,r24 mr r4,r24
bctr bctr
#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_misc.S */
/* void local_save_flags_ptr(unsigned long *flags) */ /* void local_save_flags_ptr(unsigned long *flags) */
_GLOBAL(local_save_flags_ptr) _GLOBAL(local_save_flags_ptr)
mfmsr r4 mfmsr r4
...@@ -333,7 +328,6 @@ _GLOBAL(local_irq_enable) ...@@ -333,7 +328,6 @@ _GLOBAL(local_irq_enable)
nop nop
nop nop
_GLOBAL(local_irq_enable_end) _GLOBAL(local_irq_enable_end)
#endif /* CONFIG_PPC_ISERIES */
/* /*
* complement mask on the msr then "or" some values on. * complement mask on the msr then "or" some values on.
...@@ -487,7 +481,6 @@ _GLOBAL(flush_instruction_cache) ...@@ -487,7 +481,6 @@ _GLOBAL(flush_instruction_cache)
isync isync
blr blr
#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_misc.S */
/* /*
* Write any modified data cache blocks out to memory * Write any modified data cache blocks out to memory
* and invalidate the corresponding instruction cache blocks. * and invalidate the corresponding instruction cache blocks.
...@@ -751,7 +744,6 @@ _GLOBAL(copy_page) ...@@ -751,7 +744,6 @@ _GLOBAL(copy_page)
#endif #endif
bdnz 1b bdnz 1b
blr blr
#endif /* CONFIG_PPC_ISERIES */
/* /*
* void atomic_clear_mask(atomic_t mask, atomic_t *addr) * void atomic_clear_mask(atomic_t mask, atomic_t *addr)
......
...@@ -866,7 +866,7 @@ kgdb_output_string (const char* s, unsigned int count) ...@@ -866,7 +866,7 @@ kgdb_output_string (const char* s, unsigned int count)
return 1; return 1;
} }
#if defined(CONFIG_6xx) || defined(CONFIG_POWER3) || defined(CONFIG_ISERIES) #if defined(CONFIG_6xx) || defined(CONFIG_POWER3)
/* This is used on arches which don't have a serial driver that maps /* This is used on arches which don't have a serial driver that maps
* the ports for us */ * the ports for us */
......
...@@ -153,11 +153,9 @@ EXPORT_SYMBOL(_insl_ns); ...@@ -153,11 +153,9 @@ EXPORT_SYMBOL(_insl_ns);
EXPORT_SYMBOL(_outsl_ns); EXPORT_SYMBOL(_outsl_ns);
EXPORT_SYMBOL(iopa); EXPORT_SYMBOL(iopa);
EXPORT_SYMBOL(mm_ptov); EXPORT_SYMBOL(mm_ptov);
#ifndef CONFIG_PPC_ISERIES
EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(ioremap);
EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(__ioremap);
EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(iounmap);
#endif
#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
EXPORT_SYMBOL(ppc_ide_md); EXPORT_SYMBOL(ppc_ide_md);
......
...@@ -44,9 +44,6 @@ ...@@ -44,9 +44,6 @@
#include <asm/mmu.h> #include <asm/mmu.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/hardirq.h> #include <asm/hardirq.h>
#ifdef CONFIG_PPC_ISERIES
#include <asm/iSeries/Paca.h>
#endif
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs);
extern unsigned long _get_SP(void); extern unsigned long _get_SP(void);
...@@ -365,9 +362,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, ...@@ -365,9 +362,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
sp -= STACK_FRAME_OVERHEAD; sp -= STACK_FRAME_OVERHEAD;
p->thread.ksp = sp; p->thread.ksp = sp;
kregs->nip = (unsigned long)ret_from_fork; kregs->nip = (unsigned long)ret_from_fork;
#ifdef CONFIG_PPC_ISERIES
kregs->softEnable = ((struct Paca *)mfspr(SPRG1))->xProcEnabled;
#endif
/* /*
* copy fpu info - assume lazy fpu switch now always * copy fpu info - assume lazy fpu switch now always
...@@ -408,6 +402,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) ...@@ -408,6 +402,7 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
regs->link = 0; regs->link = 0;
regs->xer = 0; regs->xer = 0;
regs->ccr = 0; regs->ccr = 0;
regs->mq = 0;
regs->nip = nip; regs->nip = nip;
regs->gpr[1] = sp; regs->gpr[1] = sp;
regs->msr = MSR_USER; regs->msr = MSR_USER;
......
...@@ -87,11 +87,6 @@ unsigned tb_last_stamp; ...@@ -87,11 +87,6 @@ unsigned tb_last_stamp;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
#ifdef CONFIG_PPC_ISERIES
extern u64 get_tb64(void);
extern u64 next_jiffy_update_tb[];
#endif
static long time_offset; static long time_offset;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
...@@ -111,8 +106,6 @@ static inline int tb_delta(unsigned *jiffy_stamp) { ...@@ -111,8 +106,6 @@ static inline int tb_delta(unsigned *jiffy_stamp) {
return delta; return delta;
} }
#ifndef CONFIG_PPC_ISERIES /* iSeries version is in iSeries_time.c */
extern unsigned long prof_cpu_mask; extern unsigned long prof_cpu_mask;
extern unsigned int * prof_buffer; extern unsigned int * prof_buffer;
extern unsigned long prof_len; extern unsigned long prof_len;
...@@ -213,7 +206,6 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -213,7 +206,6 @@ void timer_interrupt(struct pt_regs * regs)
irq_exit(); irq_exit();
} }
#endif /* CONFIG_PPC_ISERIES */
/* /*
* This version of gettimeofday has microsecond resolution. * This version of gettimeofday has microsecond resolution.
...@@ -226,11 +218,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -226,11 +218,7 @@ void do_gettimeofday(struct timeval *tv)
read_lock_irqsave(&xtime_lock, flags); read_lock_irqsave(&xtime_lock, flags);
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec = (xtime.tv_nsec / 1000); usec = (xtime.tv_nsec / 1000);
#ifdef CONFIG_PPC_ISERIES
delta = tb_ticks_per_jiffy - ( next_jiffy_update_tb[0] - get_tb64() );
#else
delta = tb_ticks_since(tb_last_stamp); delta = tb_ticks_since(tb_last_stamp);
#endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* As long as timebases are not in sync, gettimeofday can only /* As long as timebases are not in sync, gettimeofday can only
* have jiffy resolution on SMP. * have jiffy resolution on SMP.
......
...@@ -10,6 +10,5 @@ obj-y := fault.o init.o mem_pieces.o extable.o \ ...@@ -10,6 +10,5 @@ obj-y := fault.o init.o mem_pieces.o extable.o \
mmu_context.o pgtable.o mmu_context.o pgtable.o
obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o
obj-$(CONFIG_PPC_ISERIES) += iSeries_hashtable.o iSeries_mmu.o tlb.o
obj-$(CONFIG_40x) += 4xx_mmu.o obj-$(CONFIG_40x) += 4xx_mmu.o
obj-$(CONFIG_NOT_COHERENT_CACHE) += cachemap.o obj-$(CONFIG_NOT_COHERENT_CACHE) += cachemap.o
/*
*
*
* Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com> IBM Corporation
* updated by Dave Boutcher (boutcher@us.ibm.com)
*
* Module name: iSeries_hashtable.c
*
* Description:
* Handles Hash Table faults for iSeries LPAR.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/processor.h>
#include <asm/pgtable.h>
#include <linux/mm.h>
#include <asm/mmu_context.h>
#include <asm/page.h>
#include <asm/types.h>
#include <linux/spinlock.h>
#include <asm/iSeries/HvCallHpt.h>
#include <asm/iSeries/LparData.h>
#include <asm/mmu_context.h>
int iSeries_hpt_loaded;
static spinlock_t mmu_hash_lock = SPIN_LOCK_UNLOCKED;
extern unsigned long htab_reloads; // Defined in ppc/kernel/ppc_htab.c
extern unsigned long htab_evicts;
unsigned long htab_pp_update = 0;
unsigned long flush_hash_page_count = 0;
unsigned long Hash_mask;
unsigned long flush_hash_range_count = 0;
unsigned long flush_hash_range_hv_finds = 0;
unsigned long flush_hash_range_hv_evicts = 0;
extern int iSeries_max_kernel_hpt_slot;
static unsigned next_slot = 4; // good enough starting value
extern void flush_hash_range( u64, u64, unsigned );
static inline u32 computeHptePP( unsigned long pte )
{
return ( ( pte & _PAGE_USER ) >> 1 ) |
( ( ( pte & _PAGE_USER ) >> 2 ) &
( (~pte & _PAGE_RW ) >> 10 ) );
}
static inline u32 computeHpteHash( unsigned long vsid,
unsigned long pid )
{
return ( vsid ^ pid ) & Hash_mask;
}
/*
* Should be called with hash page lock
*/
static void __create_hpte(unsigned long vsid, unsigned long va, unsigned long newpte)
{
PTE hpte;
u64 vpn;
u64 rtnIndex;
u64 *hpte0Ptr, *hpte1Ptr;
u32 newpp, gIndex;
vsid = vsid & 0x7ffffff;
vpn = ((u64)vsid << 16) | ((va >> 12) & 0xffff);
hpte0Ptr = (u64 *)&hpte;
hpte1Ptr = hpte0Ptr + 1;
*hpte0Ptr = *hpte1Ptr = 0;
rtnIndex = HvCallHpt_findValid( &hpte, vpn );
newpp = computeHptePP( newpte );
if ( hpte.v ) {
/* A matching valid entry was found
* Just update the pp bits
*/
++htab_pp_update;
HvCallHpt_setPp( rtnIndex, newpp );
} else { /* No matching entry was found Build new hpte */
hpte.vsid = vsid;
hpte.api = (va >> 23) & 0x1f;
hpte.v = 1;
hpte.rpn = physRpn_to_absRpn(newpte>>12);
hpte.r = 1;
hpte.c = 1;
hpte.m = 1;
hpte.pp = newpp;
if ( ( rtnIndex != ~0 ) &&
( rtnIndex != 0x00000000ffffffff ) ) {
/* Free entry was found */
if ( ( rtnIndex >> 63 ) ||
( rtnIndex & 0x80000000 ) )
hpte.h = 1;
HvCallHpt_addValidate(
rtnIndex,
hpte.h,
&hpte );
} else {
/* No free entry was found */
gIndex = computeHpteHash( vsid, vpn & 0xffff );
rtnIndex = gIndex*8 + next_slot;
if ( ++next_slot > 7 )
next_slot = iSeries_max_kernel_hpt_slot+1;
HvCallHpt_invalidateSetSwBitsGet(
rtnIndex, 0, 1 );
HvCallHpt_addValidate(
rtnIndex, 0, &hpte );
++htab_evicts;
}
}
}
int iSeries_create_hpte( unsigned long access, unsigned long va )
{
struct thread_struct *ts;
pgd_t * pg;
pmd_t * pm;
pte_t * pt;
u32 vsid;
unsigned flags;
vsid = mfsrin( va ) & 0x07ffffff;
if ( va >= KERNELBASE )
pg = swapper_pg_dir;
else {
// Get the thread structure
ts = (struct thread_struct *)mfspr(SPRG3);
// Get the page directory
pg = ts->pgdir;
}
pg = pg + pgd_index( va ); // offset into first level
pm = pmd_offset( pg, va ); // offset into second level
if ( pmd_none( *pm ) ) // if no third level
return 1; // indicate failure
pt = pte_offset( pm, va ); // offset into third level
access |= _PAGE_PRESENT; // _PAGE_PRESENT also needed
spin_lock( &mmu_hash_lock );
// check if pte is in the required state
if ( ( access & ~(pte_val(*pt)) ) ) {
spin_unlock( &mmu_hash_lock );
return 1;
}
/* pte allows the access we are making */
flags = _PAGE_ACCESSED | _PAGE_HASHPTE | _PAGE_COHERENT;
if ( access & _PAGE_RW ) /* If write access */
flags |= _PAGE_RW;
/* atomically update pte */
pte_update( pt, 0, flags );
__create_hpte(vsid,
va,
pte_val(*pt));
spin_unlock( &mmu_hash_lock );
return 0;
}
void add_hash_page(unsigned context, unsigned long va, pte_t *ptep)
{
spin_lock( &mmu_hash_lock );
pte_update(ptep,0,_PAGE_HASHPTE);
__create_hpte(CTX_TO_VSID(context, va),
va,
pte_val(*ptep));
spin_unlock( &mmu_hash_lock );
}
int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep)
{
int rc;
PTE hpte;
u64 vpn;
unsigned long vsid;
u64 rtnIndex;
u64 *hpte0Ptr, *hpte1Ptr;
vsid = CTX_TO_VSID(context, va);
vpn = ((u64)vsid << 16) | ((va >> 12) & 0xffff);
hpte0Ptr = (u64 *)&hpte;
hpte1Ptr = hpte0Ptr + 1;
*hpte0Ptr = *hpte1Ptr = 0;
spin_lock( &mmu_hash_lock );
rtnIndex = HvCallHpt_findValid( &hpte, vpn );
if ( hpte.v ) {
pte_update(ptep, _PAGE_HASHPTE, 0);
HvCallHpt_invalidateSetSwBitsGet(rtnIndex, 0, 1 );
rc = 0;
} else
rc = 1;
spin_unlock( &mmu_hash_lock );
return rc;
}
/*
* Procedures for MMU handling on iSeries systems, where we
* have to call the hypervisor to change things in the hash
* table.
*
* Copyright (C) 2001 IBM Corp.
* updated by Dave Boutcher (boutcher@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 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/stddef.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/bootmem.h>
#include <linux/highmem.h>
#include <asm/pgalloc.h>
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
#include <asm/bootx.h>
#include <asm/machdep.h>
#include <asm/setup.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCallHpt.h>
#include <linux/pci.h>
#include <asm/iSeries/iSeries_dma.h>
#include "mmu_decl.h"
int iSeries_max_kernel_hpt_slot = 0;
extern unsigned maxPacas;
extern int iSeries_hpt_loaded;
PTE *Hash = 0;
#ifdef CONFIG_PCI
extern int iSeries_Is_IoMmAddress(unsigned long address);
/*********************************************************************/
/* iSeries maps I/O space to device, just leave the address where is.*/
/*********************************************************************/
void* ioremap(unsigned long addr, unsigned long size)
{
return (void*)addr;
}
void* __ioremap(unsigned long addr, unsigned long size, unsigned long flags)
{
return (void*)addr;
}
/********************************************************************/
/* iSeries did not remapped the space. */
/********************************************************************/
void iounmap(void *addr)
{
return;
}
#endif /* CONFIG_PCI */
/*
* Map as much of memory as will fit into the first entry of each
* PPC HPTE Group. (These are the "bolted" entries which will
* never be cast out). The iSeries Hypervisor has already mapped
* the first 32 MB (specified in LparMap.h). Here we map as
* much more as we can.
*/
void __init MMU_init_hw(void)
{
PTE hpte;
u64 *hpte0Ptr, *hpte1Ptr;
u32 HptSizeGroups, msPages, rpn, vsid, ea;
u64 rtnIndex;
u32 hpteIndex;
u32 group;
unsigned long numAdded;
if ( ppc_md.progress ) ppc_md.progress("hash:enter", 0x105);
hpte0Ptr = (u64 *)&hpte;
hpte1Ptr = hpte0Ptr + 1;
/* Get the number of Hpt groups */
HptSizeGroups = (u32)HvCallHpt_getHptPages() * 32;
Hash_mask = HptSizeGroups - 1;
/* Number of pages in memory */
msPages = totalLpChunks << 6;
/* For each virtual page in kernel space, add a hpte if there
isn't one already in slot 0 of the primary pteg. */
numAdded = 0;
for ( ea = (u32)KERNELBASE; ea < (u32)high_memory; ea+= PAGE_SIZE) {
rpn = ea >> 12;
vsid = ((ea >> 28) * 0x111);
rtnIndex = HvCallHpt_findValid( &hpte,
(rpn & 0xffff) | (vsid << 16));
hpteIndex = (u32)rtnIndex;
if ( hpte.v ) /* If valid entry found */
continue; /* Already mapped, nothing to do */
if ( rtnIndex == ~0 ) /* If no free entry found */
BUG(); /* Can't map this page bolted */
if ( rtnIndex >> 63 ) /* If first free slot is secondary */
BUG(); /* Can't map this page bolted */
if ( (hpteIndex & 7) > 2) /* Not in first 3 slots */
BUG();
/*
* If returned index is the first in the primary group
* then build an hpt entry for this page.
*/
*hpte0Ptr = *hpte1Ptr = 0;
hpte.vsid = vsid;
hpte.api = (rpn >> 11) & 0x1f;
hpte.h = 0;
hpte.v = 1;
hpte.rpn = physRpn_to_absRpn( rpn );
hpte.r = 1;
hpte.c = 1;
hpte.m = 1;
hpte.w = 0;
hpte.i = 0;
hpte.g = 0;
hpte.pp = 0;
HvCallHpt_addValidate( hpteIndex, 0, &hpte );
++numAdded;
group = rtnIndex & 0x07;
if (group > iSeries_max_kernel_hpt_slot)
iSeries_max_kernel_hpt_slot = group;
}
printk( "iSeries_hashinit: added %ld hptes to existing mapping. Max group %x\n",
numAdded, iSeries_max_kernel_hpt_slot );
if ( ppc_md.progress ) ppc_md.progress("hash:done", 0x205);
iSeries_hpt_loaded = 1;
Hash = (void *)0xFFFFFFFF;
}
/*
* This is called at the end of handling a user page fault, when the
* fault has been handled by updating a PTE in the linux page tables.
* We use it to preload an HPTE into the hash table corresponding to
* the updated linux PTE.
*/
void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
pte_t pte)
{
struct mm_struct *mm;
pmd_t *pmd;
pte_t *ptep;
static int nopreload;
if (nopreload || address >= TASK_SIZE)
return;
mm = vma->vm_mm;
pmd = pmd_offset(pgd_offset(mm, address), address);
if (!pmd_none(*pmd)) {
ptep = pte_offset_map(pmd, address);
add_hash_page(mm->context, address, ptep);
pte_unmap(ptep);
}
}
...@@ -55,10 +55,6 @@ ...@@ -55,10 +55,6 @@
#endif #endif
#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE #define MAX_LOW_MEM CONFIG_LOWMEM_SIZE
#ifdef CONFIG_PPC_ISERIES
extern void create_virtual_bus_tce_table(void);
#endif
mmu_gather_t mmu_gathers[NR_CPUS]; mmu_gather_t mmu_gathers[NR_CPUS];
unsigned long total_memory; unsigned long total_memory;
...@@ -475,10 +471,6 @@ void __init mem_init(void) ...@@ -475,10 +471,6 @@ void __init mem_init(void)
printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page); printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page);
#endif /* defined(CONFIG_ALL_PPC) */ #endif /* defined(CONFIG_ALL_PPC) */
#ifdef CONFIG_PPC_ISERIES
create_virtual_bus_tce_table();
#endif /* CONFIG_PPC_ISERIES */
/* Make sure all our pagetable pages have page->mapping /* Make sure all our pagetable pages have page->mapping
and page->index set correctly. */ and page->index set correctly. */
for (addr = KERNELBASE; addr != 0; addr += PGDIR_SIZE) { for (addr = KERNELBASE; addr != 0; addr += PGDIR_SIZE) {
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
* PowerPC implementations where the MMU substantially follows the * PowerPC implementations where the MMU substantially follows the
* architecture specification. This includes the 6xx, 7xx, 7xxx, * architecture specification. This includes the 6xx, 7xx, 7xxx,
* 8260, and POWER3 implementations but excludes the 8xx and 4xx. * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
* Although the iSeries hardware does comply with the architecture
* specification, the need to work through the hypervisor makes
* things sufficiently different that it is handled elsewhere.
* -- paulus * -- paulus
* *
* Derived from arch/ppc/mm/init.c: * Derived from arch/ppc/mm/init.c:
......
...@@ -119,7 +119,6 @@ void pte_free(struct page *pte) ...@@ -119,7 +119,6 @@ void pte_free(struct page *pte)
__free_page(pte); __free_page(pte);
} }
#ifndef CONFIG_PPC_ISERIES
void * void *
ioremap(unsigned long addr, unsigned long size) ioremap(unsigned long addr, unsigned long size)
{ {
...@@ -219,7 +218,6 @@ void iounmap(void *addr) ...@@ -219,7 +218,6 @@ void iounmap(void *addr)
if (addr > high_memory && (unsigned long) addr < ioremap_bot) if (addr > high_memory && (unsigned long) addr < ioremap_bot)
vunmap((void *) (PAGE_MASK & (unsigned long)addr)); vunmap((void *) (PAGE_MASK & (unsigned long)addr));
} }
#endif /* CONFIG_PPC_ISERIES */
int int
map_page(unsigned long va, unsigned long pa, int flags) map_page(unsigned long va, unsigned long pa, int flags)
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
* PowerPC implementations where the MMU substantially follows the * PowerPC implementations where the MMU substantially follows the
* architecture specification. This includes the 6xx, 7xx, 7xxx, * architecture specification. This includes the 6xx, 7xx, 7xxx,
* 8260, and POWER3 implementations but excludes the 8xx and 4xx. * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
* Although the iSeries hardware does comply with the architecture
* specification, the need to work through the hypervisor makes
* things sufficiently different that it is handled elsewhere.
* -- paulus * -- paulus
* *
* Derived from arch/ppc/mm/init.c: * Derived from arch/ppc/mm/init.c:
......
...@@ -46,10 +46,7 @@ obj-$(CONFIG_PRPMC800) += prpmc800_setup.o prpmc800_pci.o ...@@ -46,10 +46,7 @@ obj-$(CONFIG_PRPMC800) += prpmc800_setup.o prpmc800_pci.o
obj-$(CONFIG_SANDPOINT) += sandpoint_setup.o sandpoint_pci.o obj-$(CONFIG_SANDPOINT) += sandpoint_setup.o sandpoint_pci.o
obj-$(CONFIG_SPRUCE) += spruce_setup.o spruce_pci.o obj-$(CONFIG_SPRUCE) += spruce_setup.o spruce_pci.o
obj-$(CONFIG_ZX4500) += zx4500_setup.o zx4500_pci.o obj-$(CONFIG_ZX4500) += zx4500_setup.o zx4500_pci.o
obj-$(CONFIG_PPC_ISERIES) += iSeries_setup.o iSeries_time.o \
iSeries_dma.o iSeries_pic.o
ifeq ($(CONFIG_SMP),y) ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_ALL_PPC) += pmac_smp.o chrp_smp.o obj-$(CONFIG_ALL_PPC) += pmac_smp.o chrp_smp.o
obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o
endif endif
/*
* iSeries_dma.c
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* Dynamic DMA mapping support.
*
* Manages the TCE space assigned to this partition
*
* modeled from pci-dma.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/iSeries/HvCallXm.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/iSeries_dma.h>
#include <asm/iSeries/iSeries_pci.h>
struct pci_dev * iSeries_veth_dev = NULL;
struct pci_dev * iSeries_vio_dev = NULL;
struct TceTable virtBusTceTable; /* Tce table for virtual bus */
struct TceTable * tceTables[256]; // Tce tables for 256 busses
// Bus 255 is the virtual bus
// zero indicates no bus defined
// allocates a contiguous range of tces (power-of-2 size)
static long alloc_tce_range( struct TceTable *,
unsigned order );
// allocates a contiguous range of tces (power-of-2 size)
// assumes lock already held
static long alloc_tce_range_nolock( struct TceTable *,
unsigned order );
// frees a contiguous range of tces (power-of-2 size)
static void free_tce_range( struct TceTable *,
long tcenum,
unsigned order );
// frees a contiguous rnage of tces (power-of-2 size)
// assumes lock already held
static void free_tce_range_nolock( struct TceTable *,
long tcenum,
unsigned order );
// allocates a range of tces and sets them to the
// pages
static dma_addr_t get_tces( struct TceTable *,
unsigned order,
void *page,
unsigned numPages,
int tceType,
int direction );
static void free_tces( struct TceTable *,
dma_addr_t tce,
unsigned order,
unsigned numPages );
static long test_tce_range( struct TceTable *,
long tcenum,
unsigned order );
static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents,
dma_addr_t dma_addr, unsigned long numTces );
static unsigned long num_tces_sg( struct scatterlist *sg,
int nents );
static dma_addr_t create_tces_sg( struct TceTable *tbl,
struct scatterlist *sg,
int nents,
unsigned numTces,
int tceType,
int direction );
static unsigned __inline__ count_leading_zeros32( unsigned long x )
{
unsigned lz;
asm("cntlzw %0,%1" : "=r"(lz) : "r"(x));
return lz;
}
static void __inline__ build_tce( struct TceTable * tbl, long tcenum,
unsigned long uaddr, int tceType, int direction )
{
union Tce tce;
tce.wholeTce = 0;
tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT;
// If for virtual bus
if ( tceType == TCE_VB ) {
tce.tceBits.valid = 1;
tce.tceBits.allIo = 1;
if ( direction != PCI_DMA_TODEVICE )
tce.tceBits.readWrite = 1;
}
// If for PCI bus
else {
tce.tceBits.readWrite = 1; // Read allowed
if ( direction != PCI_DMA_TODEVICE )
tce.tceBits.pciWrite = 1;
}
HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce );
}
// Build a TceTable structure. This contains a multi-level bit map which
// is used to manage allocation of the tce space.
struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms,
struct TceTable * tbl )
{
unsigned long bits, bytes, totalBytes;
unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS];
unsigned i, k, m;
unsigned char * pos, * p, b;
tbl->size = tceTableParms->size;
tbl->busNumber = tceTableParms->busNumber;
tbl->startOffset = tceTableParms->startOffset;
tbl->index = tceTableParms->index;
spin_lock_init( &(tbl->lock) );
tbl->mlbm.maxLevel = 0;
// Compute number of bits and bytes for each level of the
// multi-level bit map
//
totalBytes = 0;
bits = tbl->size * (PAGE_SIZE / sizeof( union Tce ));
for ( i=0; i<NUM_TCE_LEVELS; ++i ) {
bytes = (bits+7)/8;
#ifdef DEBUG_TCE
printk("build_tce_table: level %d bits=%ld, bytes=%ld\n", i, bits, bytes );
#endif
numBits[i] = bits;
numBytes[i] = bytes;
bits /= 2;
/* we need extra space at the end that's a multiple of 8 bytes */
/* for the cntlzw algorithm to work correctly. */
/* but we don't want the bits turned on or used, so we don't muck with numBytes[i] */
totalBytes += (bytes + 7) & ~7;
}
#ifdef DEBUG_TCE
printk("build_tce_table: totalBytes=%ld\n", totalBytes );
#endif
pos = (char *)__get_free_pages( GFP_ATOMIC, get_order( totalBytes ));
if ( !pos )
return NULL;
memset( pos, 0, totalBytes );
// For each level, fill in the pointer to the bit map,
// and turn on the last bit in the bit map (if the
// number of bits in the map is odd). The highest
// level will get all of its bits turned on.
for (i=0; i<NUM_TCE_LEVELS; ++i) {
if ( numBytes[i] ) {
tbl->mlbm.level[i].map = pos;
tbl->mlbm.maxLevel = i;
if ( numBits[i] & 1 ) {
p = pos + numBytes[i] - 1;
m = (( numBits[i] % 8) - 1) & 7;
*p = 0x80 >> m;
#ifdef DEBUG_TCE
printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m );
#endif
}
}
else
tbl->mlbm.level[i].map = 0;
/* see the comment up above on the totalBytes calculation for why we do this. */
pos += (numBytes[i] + 7) & ~7;
tbl->mlbm.level[i].numBits = numBits[i];
tbl->mlbm.level[i].numBytes = numBytes[i];
}
// For the highest level, turn on all the bits
i = tbl->mlbm.maxLevel;
p = tbl->mlbm.level[i].map;
m = numBits[i];
#ifdef DEBUG_TCE
printk("build_tce_table: highest level (%d) has all bits set\n", i);
#endif
for (k=0; k<numBytes[i]; ++k) {
if ( m >= 8 ) {
// handle full bytes
*p++ = 0xff;
m -= 8;
}
else {
// handle the last partial byte
b = 0x80;
*p = 0;
while (m) {
*p |= b;
b >>= 1;
--m;
}
}
}
return tbl;
}
static long alloc_tce_range( struct TceTable *tbl, unsigned order )
{
long retval;
unsigned long flags;
// Lock the tce allocation bitmap
spin_lock_irqsave( &(tbl->lock), flags );
// Do the actual work
retval = alloc_tce_range_nolock( tbl, order );
// Unlock the tce allocation bitmap
spin_unlock_irqrestore( &(tbl->lock), flags );
return retval;
}
static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order )
{
unsigned long numBits, numBytes;
unsigned long i, bit, block, mask;
long tcenum;
u32 * map;
// If the order (power of 2 size) requested is larger than our
// biggest, indicate failure
if ( order > tbl->mlbm.maxLevel ) {
#ifdef DEBUG_TCE
printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order );
#endif
return -1;
}
numBits = tbl->mlbm.level[order].numBits;
numBytes = tbl->mlbm.level[order].numBytes;
map = (u32 *)(tbl->mlbm.level[order].map);
// Initialize return value to -1 (failure)
tcenum = -1;
// Loop through the bytes of the bitmap
for (i=0; i<numBytes/4; ++i) {
if ( *map ) {
// A free block is found, compute the block
// number (of this size)
bit = count_leading_zeros32( *map );
block = (i * 32) + bit;
// turn off the bit in the map to indicate
// that the block is now in use
mask = 0xffffffff ^ (0x80000000 >> bit);
*map &= mask;
// compute the index into our tce table for
// the first tce in the block
#ifdef DEBUG_TCE
printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order );
#endif
tcenum = block << order;
break;
}
++map;
}
#ifdef DEBUG_TCE
if ( tcenum == -1 ) {
printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order );
if ( order < tbl->mlbm.maxLevel )
printk("alloc_tce_range_nolock: trying next bigger size\n" );
else
printk("alloc_tce_range_nolock: maximum size reached...failing\n");
}
#endif
// If no block of the requested size was found, try the next
// size bigger. If one of those is found, return the second
// half of the block to freespace and keep the first half
if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) {
tcenum = alloc_tce_range_nolock( tbl, order+1 );
if ( tcenum != -1 ) {
free_tce_range_nolock( tbl, tcenum+(1<<order), order );
}
}
// Return the index of the first tce in the block
// (or -1 if we failed)
return tcenum;
}
static void free_tce_range( struct TceTable *tbl, long tcenum, unsigned order )
{
unsigned long flags;
// Lock the tce allocation bitmap
spin_lock_irqsave( &(tbl->lock), flags );
// Do the actual work
free_tce_range_nolock( tbl, tcenum, order );
// Unlock the tce allocation bitmap
spin_unlock_irqrestore( &(tbl->lock), flags );
}
static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order )
{
unsigned long block;
unsigned byte, bit, mask, b;
unsigned char * map, * bytep;
if ( order > tbl->mlbm.maxLevel ) {
printk("free_tce_range: order too large, order = %d\n", order );
return;
}
block = tcenum >> order;
if ( tcenum != (block << order ) ) {
printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order );
return;
}
if ( block >= tbl->mlbm.level[order].numBits ) {
printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits );
return;
}
#ifdef DEBUG_TCE
if ( test_tce_range( tbl, tcenum, order ) ) {
printk("free_tce_range: freeing range not completely allocated.\n");
printk("free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order );
}
#endif
map = tbl->mlbm.level[order].map;
byte = block / 8;
bit = block % 8;
mask = 0x80 >> bit;
bytep = map + byte;
#ifdef DEBUG_TCE
printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order);
if ( *bytep & mask )
printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order );
#endif
*bytep |= mask;
// If there is a higher level in the bit map than this we may be
// able to buddy up this block with its partner.
// If this is the highest level we can't buddy up
// If this level has an odd number of bits and
// we are freeing the last block we can't buddy up
// don't buddy up if it's in the first 1/4 of the bits
if ( ( order < tbl->mlbm.maxLevel ) &&
( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) ||
( block < tbl->mlbm.level[order].numBits-1 ) ) &&
( block > (tbl->mlbm.level[order].numBits/4) ) ) {
// See if we can buddy up the block we just freed
bit &= 6; // get to the first of the buddy bits
mask = 0xc0 >> bit; // build two bit mask
b = *bytep & mask; // Get the two bits
if ( 0 == (b ^ mask) ) { // If both bits are on
// both of the buddy blocks are free we can combine them
*bytep ^= mask; // turn off the two bits
block = ( byte * 8 ) + bit; // block of first of buddies
tcenum = block << order;
// free the buddied block
#ifdef DEBUG_TCE
printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1);
#endif
free_tce_range_nolock( tbl, tcenum, order+1 );
}
}
}
static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order )
{
unsigned long block;
unsigned byte, bit, mask, b;
long retval, retLeft, retRight;
unsigned char * map;
map = tbl->mlbm.level[order].map;
block = tcenum >> order;
byte = block / 8; // Byte within bitmap
bit = block % 8; // Bit within byte
mask = 0x80 >> bit;
b = (*(map+byte) & mask ); // 0 if block is allocated, else free
if ( b )
retval = 1; // 1 == block is free
else
retval = 0; // 0 == block is allocated
// Test bits at all levels below this to ensure that all agree
if (order) {
retLeft = test_tce_range( tbl, tcenum, order-1 );
retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 );
if ( retLeft || retRight ) {
retval = 2;
}
}
// Test bits at all levels above this to ensure that all agree
return retval;
}
static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction )
{
long tcenum;
unsigned long uaddr;
unsigned i;
dma_addr_t retTce = NO_TCE;
uaddr = (unsigned long)page & PAGE_MASK;
// Allocate a range of tces
tcenum = alloc_tce_range( tbl, order );
if ( tcenum != -1 ) {
// We got the tces we wanted
tcenum += tbl->startOffset; // Offset into real TCE table
retTce = tcenum << PAGE_SHIFT; // Set the return dma address
// Setup a tce for each page
for (i=0; i<numPages; ++i) {
build_tce( tbl, tcenum, uaddr, tceType, direction );
++tcenum;
uaddr += PAGE_SIZE;
}
}
#ifdef DEBUG_TCE
else
printk("alloc_tce_range failed\n");
#endif
return retTce;
}
static void free_tces( struct TceTable *tbl, dma_addr_t dma_addr, unsigned order, unsigned numPages )
{
long tcenum, freeTce, maxTcenum;
unsigned i;
union Tce tce;
maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1;
tcenum = dma_addr >> PAGE_SHIFT;
tcenum -= tbl->startOffset;
if ( tcenum > maxTcenum ) {
printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n",
tcenum, maxTcenum );
printk("free_tces: TCE Table at %16lx\n", (unsigned long)tbl );
printk("free_tces: bus# %lu\n", (unsigned long)tbl->busNumber );
printk("free_tces: size %lu\n", (unsigned long)tbl->size );
printk("free_tces: startOff %lu\n", (unsigned long)tbl->startOffset );
printk("free_tces: index %lu\n", (unsigned long)tbl->index );
return;
}
freeTce = tcenum;
for (i=0; i<numPages; ++i) {
tce.wholeTce = 0;
HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce );
++tcenum;
}
free_tce_range( tbl, freeTce, order );
}
void __init create_virtual_bus_tce_table(void)
{
struct TceTable * t;
struct HvTceTableManagerCB virtBusTceTableParms;
u64 absParmsPtr;
virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */
virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */
absParmsPtr = virt_to_absolute( (u32)&virtBusTceTableParms );
HvCallXm_getTceTableParms( absParmsPtr );
t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable );
if ( t ) {
tceTables[255] = t;
printk("Virtual Bus TCE table built successfully.\n");
printk(" TCE table size = %ld entries\n",
(unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) );
printk(" TCE table token = %d\n",
(unsigned)t->index );
printk(" TCE table start entry = 0x%lx\n",
(unsigned long)t->startOffset );
}
else
printk("Virtual Bus TCE table failed.\n");
}
void __init create_pci_bus_tce_table( unsigned busNumber )
{
struct TceTable * t;
struct TceTable * newTceTable;
struct HvTceTableManagerCB pciBusTceTableParms;
u64 absParmsPtr;
unsigned i;
if ( busNumber > 254 ) {
printk("PCI Bus TCE table failed.\n");
printk(" Invalid bus number %u\n", busNumber );
return;
}
newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL );
pciBusTceTableParms.busNumber = busNumber;
pciBusTceTableParms.virtualBusFlag = 0;
absParmsPtr = virt_to_absolute( (u32)&pciBusTceTableParms );
HvCallXm_getTceTableParms( absParmsPtr );
// Determine if the table identified by the index and startOffset
// returned by the hypervisor for this bus has already been created.
for ( i=0; i<255; ++i) {
t = tceTables[i];
if ( t ) {
if ( ( t->index == pciBusTceTableParms.index ) &&
( t->startOffset == pciBusTceTableParms.startOffset ) ) {
if ( t->size != pciBusTceTableParms.size )
printk("PCI Bus %d Shares a TCE table with Bus %d, but sizes differ\n", busNumber, i );
else
printk("PCI Bus %d Shares a TCE table with bus %d\n", busNumber, i );
tceTables[busNumber] = t;
break;
}
}
}
if ( ! tceTables[busNumber] ) {
t = build_tce_table( &pciBusTceTableParms, newTceTable );
if ( t ) {
tceTables[busNumber] = t;
printk("PCI Bus %d TCE table built successfully.\n", busNumber);
printk(" TCE table size = %ld entries\n",
(unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) );
printk(" TCE table token = %d\n",
(unsigned)t->index );
printk(" TCE table start entry = 0x%lx\n",
(unsigned long)t->startOffset );
}
else {
kfree( newTceTable );
printk("PCI Bus %d TCE table failed.\n", busNumber );
}
}
}
// Allocates a contiguous real buffer and creates TCEs over it.
// Returns the virtual address of the buffer and sets dma_handle
// to the dma address (tce) of the first page.
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle)
{
struct TceTable * tbl;
void *ret = NULL;
unsigned order, nPages, bus;
dma_addr_t tce;
int tceType;
size = PAGE_ALIGN(size);
order = get_order(size);
nPages = 1 << order;
// If no pci_dev then use virtual bus
if (hwdev == NULL ) {
bus = 255;
tceType = TCE_VB;
}
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
tceType = TCE_PCI;
#else
BUG();
return NULL;
#endif /* CONFIG_PCI */
}
tbl = tceTables[bus];
if ( tbl ) {
// Alloc enough pages (and possibly more)
ret = (void *)__get_free_pages( GFP_ATOMIC, order );
if ( ret ) {
// Page allocation succeeded
memset(ret, 0, nPages << PAGE_SHIFT);
// Set up tces to cover the allocated range
tce = get_tces( tbl, order, ret, nPages, tceType,
PCI_DMA_BIDIRECTIONAL );
if ( tce == NO_TCE ) {
#ifdef DEBUG_TCE
printk("pci_alloc_consistent: get_tces failed\n" );
#endif
free_pages( (unsigned long)ret, order );
ret = NULL;
}
else
{
*dma_handle = tce;
}
}
#ifdef DEBUG_TCE
else
printk("pci_alloc_consistent: __get_free_pages failed for order = %d\n", order);
#endif
}
return ret;
}
void pci_free_consistent(struct pci_dev *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
struct TceTable * tbl;
unsigned order, nPages, bus;
size = PAGE_ALIGN(size);
order = get_order(size);
nPages = 1 << order;
// If no pci_dev then use virtual bus
if (hwdev == NULL )
bus = 255;
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
#else
BUG();
return;
#endif /* CONFIG_PCI */
}
if ( bus > 255 ) {
printk("pci_free_consistent: invalid bus # %d\n", bus );
printk("pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev );
}
tbl = tceTables[bus];
if ( tbl ) {
free_tces( tbl, dma_handle, order, nPages );
free_pages( (unsigned long)vaddr, order );
}
}
// Creates TCEs for a user provided buffer. The user buffer must be
// contiguous real kernel storage (not vmalloc). The address of the buffer
// passed here is the kernel (virtual) address of the buffer. The buffer
// need not be page aligned, the dma_addr_t returned will point to the same
// byte within the page as vaddr.
dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction )
{
struct TceTable * tbl;
dma_addr_t dma_handle;
unsigned long uaddr;
unsigned order, nPages, bus;
int tceType;
if ( direction == PCI_DMA_NONE )
BUG();
dma_handle = NO_TCE;
uaddr = (unsigned long)vaddr;
nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK );
order = get_order( nPages & PAGE_MASK );
nPages >>= PAGE_SHIFT;
// If no pci_dev then use virtual bus
if (hwdev == NULL ) {
bus = 255;
tceType = TCE_VB;
}
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
tceType = TCE_PCI;
#else
BUG();
return NO_TCE;
#endif /* CONFIG_PCI */
}
tbl = tceTables[bus];
if ( tbl ) {
dma_handle = get_tces( tbl, order, vaddr, nPages, tceType,
direction );
dma_handle |= ( uaddr & ~PAGE_MASK );
}
return dma_handle;
}
void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction )
{
struct TceTable * tbl;
unsigned order, nPages, bus;
if ( direction == PCI_DMA_NONE )
BUG();
nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK );
order = get_order( nPages & PAGE_MASK );
nPages >>= PAGE_SHIFT;
// If no pci_dev then use virtual bus
if (hwdev == NULL )
bus = 255;
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
#else
BUG();
return;
#endif /* CONFIG_PCI */
}
if ( bus > 255 ) {
printk("pci_unmap_single: invalid bus # %d\n", bus );
printk("pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev );
}
tbl = tceTables[bus];
if ( tbl )
free_tces( tbl, dma_handle, order, nPages );
}
// Figure out how many TCEs are actually going to be required
// to map this scatterlist. This code is not optimal. It
// takes into account the case where entry n ends in the same
// page in which entry n+1 starts. It does not handle the
// general case of entry n ending in the same page in which
// entry m starts.
static unsigned long num_tces_sg( struct scatterlist *sg, int nents )
{
unsigned long nTces, numPages, startPage, endPage, prevEndPage;
unsigned i;
prevEndPage = 0;
nTces = 0;
for (i=0; i<nents; ++i) {
// Compute the starting page number and
// the ending page number for this entry
startPage = (unsigned long)sg->address >> PAGE_SHIFT;
endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT;
numPages = endPage - startPage + 1;
// Simple optimization: if the previous entry ended
// in the same page in which this entry starts
// then we can reduce the required pages by one.
// This matches assumptions in fill_scatterlist_sg and
// create_tces_sg
if ( startPage == prevEndPage )
--numPages;
nTces += numPages;
prevEndPage = endPage;
sg++;
}
return nTces;
}
// Fill in the dma data in the scatterlist
// return the number of dma sg entries created
static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents,
dma_addr_t dma_addr , unsigned long numTces)
{
struct scatterlist *dma_sg;
u32 cur_start_dma;
unsigned long cur_len_dma, cur_end_virt, uaddr;
unsigned num_dma_ents;
dma_sg = sg;
num_dma_ents = 1;
// Process the first sg entry
cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK));
cur_len_dma = sg->length;
// cur_end_virt holds the address of the byte immediately after the
// end of the current buffer.
cur_end_virt = (unsigned long)sg->address + cur_len_dma;
// Later code assumes that unused sg->dma_address and sg->dma_length
// fields will be zero. Other archs seem to assume that the user
// (device driver) guarantees that...I don't want to depend on that
sg->dma_address = sg->dma_length = 0;
// Process the rest of the sg entries
while (--nents) {
++sg;
// Clear possibly unused fields. Note: sg >= dma_sg so
// this can't be clearing a field we've already set
sg->dma_address = sg->dma_length = 0;
// Check if it is possible to make this next entry
// contiguous (in dma space) with the previous entry.
// The entries can be contiguous in dma space if
// the previous entry ends immediately before the
// start of the current entry (in virtual space)
// or if the previous entry ends at a page boundary
// and the current entry starts at a page boundary.
uaddr = (unsigned long)sg->address;
if ( ( uaddr != cur_end_virt ) &&
( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) ||
( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) {
// This entry can not be contiguous in dma space.
// save the previous dma entry and start a new one
dma_sg->dma_address = cur_start_dma;
dma_sg->dma_length = cur_len_dma;
++dma_sg;
++num_dma_ents;
cur_start_dma += cur_len_dma-1;
// If the previous entry ends and this entry starts
// in the same page then they share a tce. In that
// case don't bump cur_start_dma to the next page
// in dma space. This matches assumptions made in
// num_tces_sg and create_tces_sg.
if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK))
cur_start_dma &= PAGE_MASK;
else
cur_start_dma = PAGE_ALIGN(cur_start_dma+1);
cur_start_dma += ( uaddr & (~PAGE_MASK) );
cur_len_dma = 0;
}
// Accumulate the length of this entry for the next
// dma entry
cur_len_dma += sg->length;
cur_end_virt = uaddr + sg->length;
}
// Fill in the last dma entry
dma_sg->dma_address = cur_start_dma;
dma_sg->dma_length = cur_len_dma;
if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces)
{
printk("fill_scatterlist_sg: numTces %ld, used tces %d\n",
numTces,
(unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1));
}
return num_dma_ents;
}
// Call the hypervisor to create the TCE entries.
// return the number of TCEs created
static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg,
int nents, unsigned numTces, int tceType, int direction )
{
unsigned order, i, j;
unsigned long startPage, endPage, prevEndPage, numPages, uaddr;
long tcenum, starttcenum;
dma_addr_t dmaAddr;
dmaAddr = NO_TCE;
order = get_order( numTces << PAGE_SHIFT );
// allocate a block of tces
tcenum = alloc_tce_range( tbl, order );
if ( tcenum != -1 ) {
tcenum += tbl->startOffset;
starttcenum = tcenum;
dmaAddr = tcenum << PAGE_SHIFT;
prevEndPage = 0;
for (j=0; j<nents; ++j) {
startPage = (unsigned long)sg->address >> PAGE_SHIFT;
endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT;
numPages = endPage - startPage + 1;
uaddr = (unsigned long)sg->address;
// If the previous entry ended in the same page that
// the current page starts then they share that
// tce and we reduce the number of tces we need
// by one. This matches assumptions made in
// num_tces_sg and fill_scatterlist_sg
if ( startPage == prevEndPage ) {
--numPages;
uaddr += PAGE_SIZE;
}
for (i=0; i<numPages; ++i) {
build_tce( tbl, tcenum, uaddr, tceType,
direction );
++tcenum;
uaddr += PAGE_SIZE;
}
prevEndPage = endPage;
sg++;
}
if ((tcenum - starttcenum) != numTces)
printk("create_tces_sg: numTces %d, tces used %d\n",
numTces, (unsigned)(tcenum - starttcenum));
}
return dmaAddr;
}
int pci_map_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nents, int direction )
{
struct TceTable * tbl;
unsigned bus, numTces;
int tceType, num_dma;
dma_addr_t dma_handle;
// Fast path for a single entry scatterlist
if ( nents == 1 ) {
sg->dma_address = pci_map_single( hwdev, sg->address,
sg->length, direction );
sg->dma_length = sg->length;
return 1;
}
if ( direction == PCI_DMA_NONE )
BUG();
// If no pci_dev then use virtual bus
if (hwdev == NULL ) {
bus = 255;
tceType = TCE_VB;
}
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
tceType = TCE_PCI;
#else
BUG();
return 0;
#endif /* CONFIG_PCI */
}
tbl = tceTables[bus];
if ( tbl ) {
// Compute the number of tces required
numTces = num_tces_sg( sg, nents );
// Create the tces and get the dma address
dma_handle = create_tces_sg( tbl, sg, nents, numTces,
tceType, direction );
// Fill in the dma scatterlist
num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces );
}
return num_dma;
}
void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction )
{
struct TceTable * tbl;
unsigned order, numTces, bus, i;
dma_addr_t dma_end_page, dma_start_page;
if ( direction == PCI_DMA_NONE )
BUG();
dma_start_page = sg->dma_address & PAGE_MASK;
for ( i=nelms; i>0; --i ) {
unsigned k = i - 1;
if ( sg[k].dma_length ) {
dma_end_page = ( sg[k].dma_address +
sg[k].dma_length - 1 ) & PAGE_MASK;
break;
}
}
numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1;
order = get_order( numTces << PAGE_SHIFT );
// If no pci_dev then use virtual bus
if (hwdev == NULL )
bus = 255;
else {
#ifdef CONFIG_PCI
// Get the iSeries bus # to use as an index
// into the TCE table array
bus = ISERIES_GET_BUS( hwdev );
#else
BUG();
return;
#endif /* CONFIG_PCI */
}
if ( bus > 255 ) {
printk("pci_unmap_sg: invalid bus # %d\n", bus );
printk("pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev );
}
tbl = tceTables[bus];
if ( tbl )
free_tces( tbl, dma_start_page, order, numTces );
}
/*
* arch/ppc/platforms/iSeries_pic.c
*
*/
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/threads.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
#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 <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/cache.h>
#include <asm/prom.h>
#include <asm/ptrace.h>
#include <asm/iSeries/LparData.h>
extern void iSeries_smp_message_recv( struct pt_regs * );
extern int is_soft_enabled(void);
extern void __no_lpq_restore_flags(unsigned long flags);
extern void iSeries_smp_message_recv( struct pt_regs * );
unsigned lpEvent_count = 0;
void do_IRQ(struct pt_regs *regs)
{
int cpu = smp_processor_id();
unsigned long flags;
struct Paca * paca;
struct ItLpQueue *lpq;
if ( is_soft_enabled() )
BUG();
irq_enter();
paca = (struct Paca *)mfspr(SPRG1);
#ifdef CONFIG_SMP
if ( paca->xLpPacaPtr->xIpiCnt ) {
paca->xLpPacaPtr->xIpiCnt = 0;
iSeries_smp_message_recv( regs );
}
#endif /* CONFIG_SMP */
lpq = paca->lpQueuePtr;
if ( lpq ) {
local_irq_save(flags);
lpEvent_count += ItLpQueue_process( lpq, regs );
local_irq_restore( flags );
}
irq_exit();
if ( paca->xLpPacaPtr->xDecrInt ) {
paca->xLpPacaPtr->xDecrInt = 0;
/* Signal a fake decrementer interrupt */
timer_interrupt( regs );
}
}
/*
*
*
* Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
* Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
*
* Module name: iSeries_setup.c
*
* Description:
* Architecture- / platform-specific boot-time initialization code for
* the IBM iSeries LPAR. Adapted from original code by Grant Erickson and
* code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek
* <dan@net4x.com>.
*
*/
#include <linux/pci.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/smp.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/bootmem.h>
#include <linux/blk.h>
#include <linux/ide.h>
#include <linux/root_dev.h>
#include <linux/seq_file.h>
#include <asm/processor.h>
#include <asm/machdep.h>
#include <asm/page.h>
#include <asm/bootinfo.h>
#include <asm/time.h>
#include "iSeries_setup.h"
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCallHpt.h>
#include <asm/iSeries/HvLpConfig.h>
#include <asm/iSeries/HvCallEvent.h>
#include <asm/iSeries/HvCallSm.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/IoHriMainStore.h>
#include <asm/iSeries/iSeries_proc.h>
#include <asm/iSeries/pmc_proc.h>
#include <asm/iSeries/mf.h>
#include <asm/pci-bridge.h>
#include <asm/iSeries/HvCallXm.h>
#include <asm/iSeries/iSeries_fixup.h>
#include <asm/iSeries/HvReleaseData.h>
/* Function Prototypes */
extern void abort(void);
static void build_iSeries_Memory_Map( void );
static void setup_iSeries_cache_sizes( void );
extern void iSeries_pci_Initialize(void);
static int iSeries_show_cpuinfo(struct seq_file *m);
static int iSeries_show_percpuinfo(struct seq_file *m, int i);
extern void iSeries_idle(void);
extern struct pci_ops iSeries_pci_ops;
/* Global Variables */
unsigned short iSeries_icache_line_size = 0;
unsigned short iSeries_dcache_line_size = 0;
unsigned short iSeries_icache_lines_per_page = 0;
unsigned short iSeries_dcache_lines_per_page = 0;
unsigned short iSeries_log_icache_line_size = 0;
unsigned short iSeries_log_dcache_line_size = 0;
unsigned long procFreqHz = 0;
unsigned long procFreqMhz = 0;
unsigned long procFreqMhzHundreths = 0;
unsigned long tbFreqHz = 0;
unsigned long tbFreqMhz = 0;
unsigned long tbFreqMhzHundreths = 0;
unsigned long decr_overclock = 8;
unsigned long decr_overclock_proc0 = 8;
unsigned long decr_overclock_set = 0;
unsigned long decr_overclock_proc0_set = 0;
extern unsigned long embedded_sysmap_start;
extern unsigned long embedded_sysmap_end;
extern unsigned long sysmap;
extern unsigned long sysmap_size;
extern unsigned long end_of_DRAM; // Defined in ppc/mm/init.c
#ifdef CONFIG_SMP
extern struct smp_ops_t iSeries_smp_ops;
#endif /* CONFIG_SMP */
/* XXX for now... */
#ifndef CONFIG_PCI
unsigned long isa_io_base;
#endif
/*
* void __init platform_init()
*
* Description:
* This routine...
*
* Input(s):
* r3 - Optional pointer to a board information structure.
* r4 - Optional pointer to the physical starting address of the init RAM
* disk.
* r5 - Optional pointer to the physical ending address of the init RAM
* disk.
* r6 - Optional pointer to the physical starting address of any kernel
* command-line parameters.
* r7 - Optional pointer to the physical ending address of any kernel
* command-line parameters.
*
* Output(s):
* N/A
*
* Returns:
* N/A
*
*/
extern int rd_size; // Defined in drivers/block/rd.c
extern u64 next_jiffy_update_tb[];
extern u64 get_tb64(void);
unsigned long __init iSeries_find_end_of_memory(void)
{
/* totalLpChunks contains the size of memory (in units of 256K) */
unsigned long memory_end = (totalLpChunks << 18);
#ifndef CONFIG_HIGHMEM
/* Max memory if highmem is not configured is 768 MB */
if (memory_end > (768 << 20))
memory_end = 768 << 20;
#endif /* CONFIG_HIGHMEM */
return memory_end;
}
void __init
platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7)
{
parse_bootinfo(find_bootinfo());
#if defined(CONFIG_BLK_DEV_INITRD)
/*
* If the init RAM disk has been configured and there is
* a non-zero starting address for it, set it up
*/
if ( xNaca.xRamDisk ) {
initrd_start = xNaca.xRamDisk + KERNELBASE;
initrd_end = initrd_start + xNaca.xRamDiskSize * PAGE_SIZE;
initrd_below_start_ok = 1; // ramdisk in kernel space
ROOT_DEV = Root_RAM0;
if ( ((rd_size*1024)/PAGE_SIZE) < xNaca.xRamDiskSize )
rd_size = (xNaca.xRamDiskSize*PAGE_SIZE)/1024;
} else
#endif /* CONFIG_BLK_DEV_INITRD */
#if CONFIG_VIODASD_IDE
{
ROOT_DEV = Root_HDA1;
}
#elif defined(CONFIG_VIODASD)
{
ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 );
}
#endif /* CONFIG_VIODASD_IDE */
/* If an embedded System.map has been added to the kernel,
* set it up.
*/
if ( embedded_sysmap_start ) {
sysmap = embedded_sysmap_start + KERNELBASE;
sysmap_size = embedded_sysmap_end - embedded_sysmap_start;
}
/* Copy the kernel command line arguments to a safe place. */
if (r6) {
*(char *)(r7 + KERNELBASE) = 0;
strcpy(cmd_line, (char *)(r6 + KERNELBASE));
}
/* Initialize the table which translate Linux physical addresses to
* iSeries absolute addresses
*/
build_iSeries_Memory_Map();
setup_iSeries_cache_sizes();
/* Initialize machine-dependency vectors */
ppc_md.setup_arch = iSeries_setup_arch;
ppc_md.show_cpuinfo = iSeries_show_cpuinfo;
ppc_md.show_percpuinfo = iSeries_show_percpuinfo;
ppc_md.irq_cannonicalize = NULL;
ppc_md.init_IRQ = iSeries_init_IRQ;
ppc_md.get_irq = iSeries_get_irq;
ppc_md.init = NULL;
ppc_md.restart = iSeries_restart;
ppc_md.power_off = iSeries_power_off;
ppc_md.halt = iSeries_halt;
ppc_md.time_init = NULL;
ppc_md.set_rtc_time = iSeries_set_rtc_time;
ppc_md.get_rtc_time = iSeries_get_rtc_time;
ppc_md.calibrate_decr = iSeries_calibrate_decr;
ppc_md.progress = iSeries_progress;
ppc_md.find_end_of_memory = iSeries_find_end_of_memory;
#ifdef CONFIG_PCI
ppc_md.pcibios_fixup_bus = iSeries_fixup_bus;
ppc_md.pcibios_fixup = iSeries_fixup;
#else
ppc_md.pcibios_fixup_bus = NULL;
ppc_md.pcibios_fixup = NULL;
#endif /* CONFIG_PCI */
#ifdef CONFIG_SMP
ppc_md.smp_ops = &iSeries_smp_ops;
#endif /* CONFIG_SMP */
// Associate Lp Event Queue 0 with processor 0
HvCallEvent_setLpEventQueueInterruptProc( 0, 0 );
{
// copy the command line parameter from the primary VSP
char *p, *q;
HvCallEvent_dmaToSp( cmd_line,
2*64*1024,
256,
HvLpDma_Direction_RemoteToLocal );
p = q = cmd_line + 255;
while( p > cmd_line ) {
if ((*p == 0) || (*p == ' ') || (*p == '\n'))
--p;
else
break;
}
if ( p < q )
*(p+1) = 0;
}
next_jiffy_update_tb[0] = get_tb64();
iSeries_proc_early_init();
mf_init();
iSeries_proc_callback( &pmc_proc_init );
return;
}
/*
* The iSeries may have very large memories ( > 128 GB ) and a partition
* may get memory in "chunks" that may be anywhere in the 2**52 real
* address space. The chunks are 256K in size. To map this to the
* memory model Linux expects, the iSeries specific code builds a
* translation table to translate what Linux thinks are "physical"
* addresses to the actual real addresses. This allows us to make
* it appear to Linux that we have contiguous memory starting at
* physical address zero while in fact this could be far from the truth.
* To avoid confusion, I'll let the words physical and/or real address
* apply to the Linux addresses while I'll use "absolute address" to
* refer to the actual hardware real address.
*
* build_iSeries_Memory_Map gets information from the Hypervisor and
* looks at the Main Store VPD to determine the absolute addresses
* of the memory that has been assigned to our partition and builds
* a table used to translate Linux's physical addresses to these
* absolute addresses. Absolute addresses are needed when
* communicating with the hypervisor (e.g. to build HPT entries)
*/
static void __init build_iSeries_Memory_Map(void)
{
u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize;
u32 hptFirstChunk, hptLastChunk, hptSizeChunks;
u32 absAddrHi, absAddrLo;
u32 nextPhysChunk;
u32 holeFirstChunk, holeSizeChunks;
u32 totalChunks,moreChunks;
u32 currChunk, thisChunk, absChunk;
u32 currDword;
u32 chunkBit;
u64 holeStart, holeEnd, holeSize;
u64 map;
struct IoHriMainStoreSegment4 * msVpd;
// Get absolute address of our load area
// and map it to physical address 0
// This guarantees that the loadarea ends up at physical 0
// otherwise, it might not be returned by PLIC as the first
// chunks
loadAreaFirstChunk = (u32)(itLpNaca.xLoadAreaAddr >> 18);
loadAreaSize = itLpNaca.xLoadAreaChunks;
loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;
// Get absolute address of our HPT and remember it so
// we won't map it to any physical address
hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 );
hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 );
hptLastChunk = hptFirstChunk + hptSizeChunks - 1;
loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;
absAddrLo = loadAreaFirstChunk << 18;
absAddrHi = loadAreaFirstChunk >> 14;
printk( "Mapping load area - physical addr = 0, absolute addr = %08x%08x\n",
absAddrHi, absAddrLo );
printk( "Load area size %dK\n", loadAreaSize*256 );
nextPhysChunk = 0;
for ( absChunk = loadAreaFirstChunk; absChunk <= loadAreaLastChunk; ++absChunk ) {
if ( ( absChunk < hptFirstChunk ) ||
( absChunk > hptLastChunk ) ) {
msChunks[nextPhysChunk] = absChunk;
++nextPhysChunk;
}
}
// Get absolute address of our HPT and remember it so
// we won't map it to any physical address
hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 );
hptSizeChunks = (u32)(HvCallHpt_getHptPages() >> 6 );
hptLastChunk = hptFirstChunk + hptSizeChunks - 1;
absAddrLo = hptFirstChunk << 18;
absAddrHi = hptFirstChunk >> 14;
printk( "HPT absolute addr = %08x%08x, size = %dK\n",
absAddrHi, absAddrLo, hptSizeChunks*256 );
// Determine if absolute memory has any
// holes so that we can interpret the
// access map we get back from the hypervisor
// correctly.
msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd;
holeStart = msVpd->nonInterleavedBlocksStartAdr;
holeEnd = msVpd->nonInterleavedBlocksEndAdr;
holeSize = holeEnd - holeStart;
if ( holeSize ) {
holeStart = holeStart & 0x000fffffffffffff;
holeStart = holeStart >> 18;
holeFirstChunk = (u32)holeStart;
holeSize = holeSize >> 18;
holeSizeChunks = (u32)holeSize;
printk( "Main store hole: start chunk = %0x, size = %0x chunks\n",
holeFirstChunk, holeSizeChunks );
}
else {
holeFirstChunk = 0xffffffff;
holeSizeChunks = 0;
}
// Process the main store access map from the hypervisor
// to build up our physical -> absolute translation table
totalChunks = (u32)HvLpConfig_getMsChunks();
if ((totalChunks-hptSizeChunks) > 16384) {
panic("More than 4GB of memory assigned to this partition");
}
currChunk = 0;
currDword = 0;
moreChunks = totalChunks;
while ( moreChunks ) {
map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex,
currDword );
thisChunk = currChunk;
while ( map ) {
chunkBit = map >> 63;
map <<= 1;
if ( chunkBit ) {
--moreChunks;
absChunk = thisChunk;
if ( absChunk >= holeFirstChunk )
absChunk += holeSizeChunks;
if ( ( ( absChunk < hptFirstChunk ) ||
( absChunk > hptLastChunk ) ) &&
( ( absChunk < loadAreaFirstChunk ) ||
( absChunk > loadAreaLastChunk ) ) ) {
// printk( "Mapping physical = %0x to absolute %0x for 256K\n", nextPhysChunk << 18, absChunk << 18 );
msChunks[nextPhysChunk] = absChunk;
++nextPhysChunk;
}
}
++thisChunk;
}
++currDword;
currChunk += 64;
}
// main store size (in chunks) is
// totalChunks - hptSizeChunks
// which should be equal to
// nextPhysChunk
totalLpChunks = nextPhysChunk;
}
/*
* Set up the variables that describe the cache line sizes
* for this machine.
*/
static void __init setup_iSeries_cache_sizes(void)
{
unsigned i,n;
iSeries_icache_line_size = xIoHriProcessorVpd[0].xInstCacheOperandSize;
iSeries_dcache_line_size = xIoHriProcessorVpd[0].xDataCacheOperandSize;
iSeries_icache_lines_per_page = PAGE_SIZE / iSeries_icache_line_size;
iSeries_dcache_lines_per_page = PAGE_SIZE / iSeries_dcache_line_size;
i = iSeries_icache_line_size;
n = 0;
while ((i=(i/2))) ++n;
iSeries_log_icache_line_size = n;
i = iSeries_dcache_line_size;
n = 0;
while ((i=(i/2))) ++n;
iSeries_log_dcache_line_size = n;
printk( "D-cache line size = %d (log = %d)\n",
(unsigned)iSeries_dcache_line_size,
(unsigned)iSeries_log_dcache_line_size );
printk( "I-cache line size = %d (log = %d)\n",
(unsigned)iSeries_icache_line_size,
(unsigned)iSeries_log_icache_line_size );
}
int piranha_simulator = 0;
/*
* Document me.
*/
void __init
iSeries_setup_arch(void)
{
void * eventStack;
u32 procFreq;
u32 tbFreq;
// u32 evStackContigReal;
// u64 evStackReal;
/* Setup the Lp Event Queue */
/* Associate Lp Event Queue 0 with processor 0 */
// HvCallEvent_setLpEventQueueInterruptProc( 0, 0 );
/* Allocate a page for the Event Stack
* The hypervisor wants the absolute real address, so
* we subtract out the KERNELBASE and add in the
* absolute real address of the kernel load area
*/
eventStack = alloc_bootmem_pages( LpEventStackSize );
memset( eventStack, 0, LpEventStackSize );
/* Invoke the hypervisor to initialize the event stack */
HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize );
/* Initialize fields in our Lp Event Queue */
xItLpQueue.xHSlicEventStackPtr = 0;
xItLpQueue.xSlicEventStackPtr = (char *)eventStack;
xItLpQueue.xHSlicCurEventPtr = 0;
xItLpQueue.xSlicCurEventPtr = (char *)eventStack;
xItLpQueue.xHSlicLastValidEventPtr = 0;
xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack +
(LpEventStackSize - LpEventMaxSize);
xItLpQueue.xIndex = 0;
if ( itLpNaca.xPirEnvironMode == 0 ) {
printk("Running on Piranha simulator\n");
piranha_simulator = 1;
}
// Compute processor frequency
procFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xProcFreq / 1600) );
procFreqHz = procFreq * 10000;
procFreqMhz = procFreq / 100;
procFreqMhzHundreths = procFreq - (procFreqMhz * 100 );
// Compute time base frequency
tbFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xTimeBaseFreq / 400) );
tbFreqHz = tbFreq * 10000;
tbFreqMhz = tbFreq / 100;
tbFreqMhzHundreths = tbFreq - (tbFreqMhz * 100 );
printk("Max logical processors = %d\n",
itVpdAreas.xSlicMaxLogicalProcs );
printk("Max physical processors = %d\n",
itVpdAreas.xSlicMaxPhysicalProcs );
printk("Processor frequency = %lu.%02lu\n",
procFreqMhz,
procFreqMhzHundreths );
printk("Time base frequency = %lu.%02lu\n",
tbFreqMhz,
tbFreqMhzHundreths );
printk("Processor version = %x\n",
xIoHriProcessorVpd[0].xPVR );
#ifdef CONFIG_PCI
/* Initialize the flight recorder, global bus map and pci memory table */
iSeries_pci_Initialize();
/* Setup the PCI controller list */
iSeries_build_hose_list();
#endif /* CONFIG_PCI */
/*
// copy the command line parameter from the primary VSP
HvCallEvent_dmaToSp( cmd_line,
2*64*1024,
256,
HvLpDma_Direction_RemoteToLocal );
mf_init();
viopath_init();
*/
ppc_md.idle = iSeries_idle;
}
/*
* int iSeries_show_percpuinfo()
*
* Description:
* This routine pretty-prints CPU information gathered from the VPD
* for use in /proc/cpuinfo
*
* Input(s):
* *buffer - Buffer into which CPU data is to be printed.
*
* Output(s):
* *buffer - Buffer with CPU data.
*
* Returns:
* The number of bytes copied into 'buffer' if OK, otherwise zero or less
* on error.
*/
static int
iSeries_show_percpuinfo(struct seq_file *m, int i)
{
seq_printf(m, "clock\t\t: %lu.%02luMhz\n",
procFreqMhz, procFreqMhzHundreths );
// seq_printf(m, " processor clock\t\t: %ldMHz\n",
// ((unsigned long)xIoHriProcessorVpd[0].xProcFreq)/1000000);
seq_printf(m, "time base\t: %lu.%02luMHz\n",
tbFreqMhz, tbFreqMhzHundreths );
// seq_printf(m, " time base freq\t\t: %ldMHz\n",
// ((unsigned long)xIoHriProcessorVpd[0].xTimeBaseFreq)/1000000);
seq_printf(m, "i-cache\t\t: %d\n",
iSeries_icache_line_size);
seq_printf(m, "d-cache\t\t: %d\n",
iSeries_dcache_line_size);
return 0;
}
static int iSeries_show_cpuinfo(struct seq_file *m)
{
seq_printf(m, "machine\t\t: iSeries Logical Partition\n");
return 0;
}
#ifndef CONFIG_PCI
/*
* Document me.
* and Implement me.
* If no Native I/O, do nothing routine.
*/
void __init
iSeries_init_IRQ(void)
{
return;
}
#endif
/*
* Document me.
* and Implement me.
*/
int
iSeries_get_irq(struct pt_regs *regs)
{
/*
return (ppc4xx_pic_get_irq(regs));
*/
/* -2 means ignore this interrupt */
return -2;
}
/*
* Document me.
*/
void
iSeries_restart(char *cmd)
{
mf_reboot();
}
/*
* Document me.
*/
void
iSeries_power_off(void)
{
mf_powerOff();
}
/*
* Document me.
*/
void
iSeries_halt(void)
{
mf_powerOff();
}
/*
* Nothing to do here.
*/
void __init
iSeries_time_init(void)
{
/* Nothing to do */
}
/*
* Set the RTC in the virtual service processor
* This requires flowing LpEvents to the primary partition
*/
int iSeries_set_rtc_time(unsigned long time)
{
mf_setRtcTime(time);
return 0;
}
/*
* Get the RTC from the virtual service processor
* This requires flowing LpEvents to the primary partition
*/
unsigned long iSeries_get_rtc_time(void)
{
/* XXX - Implement me */
unsigned long time;
mf_getRtcTime(&time);
return (time);
}
/*
* void __init iSeries_calibrate_decr()
*
* Description:
* This routine retrieves the internal processor frequency from the VPD,
* and sets up the kernel timer decrementer based on that value.
*
*/
void __init
iSeries_calibrate_decr(void)
{
u32 freq;
u32 tbf;
struct Paca * paca;
/* Compute decrementer (and TB) frequency
* in cycles/sec
*/
tbf = xIoHriProcessorVpd[0].xTimeBaseFreq / 16;
freq = 0x010000000;
freq = freq / tbf; /* cycles / usec */
freq = freq * 1000000; /* now in cycles/sec */
/* Set the amount to refresh the decrementer by. This
* is the number of decrementer ticks it takes for
* 1/HZ seconds.
*/
/* decrementer_count = freq / HZ;
* count_period_num = 1;
* count_period_den = freq; */
if ( decr_overclock_set && !decr_overclock_proc0_set )
decr_overclock_proc0 = decr_overclock;
tb_ticks_per_jiffy = freq / HZ;
paca = (struct Paca *)mfspr(SPRG1);
paca->default_decr = tb_ticks_per_jiffy / decr_overclock_proc0;
tb_to_us = mulhwu_scale_factor(freq, 1000000);
}
void __init
iSeries_progress( char * st, unsigned short code )
{
printk( "Progress: [%04x] - %s\n", (unsigned)code, st );
if (code != 0xffff)
mf_displayProgress( code );
else
mf_clearSrc();
}
#ifdef CONFIG_PCI
/*
* unsigned int __init iSeries_build_hose_list()
*
* Description:
* This routine builds a list of the PCI host bridges that
* connect PCI buses either partially or fully owned by
* this guest partition
*
*/
unsigned int __init iSeries_build_hose_list ( ) {
struct pci_controller* hose;
struct iSeries_hose_arch_data* hose_data;
u64 hvRc;
u16 hvbusnum;
int LxBusNumber = 0; /* Linux Bus number for grins */
/* Check to make sure the device probing will work on this iSeries Release. */
if(hvReleaseData.xVrmIndex !=3) {
printk("PCI: iSeries Lpar and Linux native PCI I/O code is incompatible.\n");
printk("PCI: A newer version of the Linux kernel is need for this iSeries release.\n");
return 0;
}
for (hvbusnum = 0; hvbusnum < 256; hvbusnum++) { /* All PCI buses which could be owned by this guest partition will be numbered by the hypervisor between 1 & 255 */
hvRc = HvCallXm_testBus (hvbusnum); /* Call the system hypervisor to query guest partition ownership status of this bus */
if (hvRc == 0) { /* This bus is partially/fully owned by this guest partition */
hose = (struct pci_controller*)pcibios_alloc_controller(); // Create the hose for this PCI bus
hose->first_busno = LxBusNumber; /* This just for debug. pcibios will */
hose->last_busno = 0xff; /* assign the bus numbers. */
hose->ops = &iSeries_pci_ops;
/* Create the iSeries_arch_data for the hose and cache the HV bus number in it so that pci bios can build the global bus map */
hose_data = (struct iSeries_hose_arch_data *) alloc_bootmem(sizeof(struct iSeries_hose_arch_data));
memset(hose_data, 0, sizeof(*hose_data));
hose->arch_data = (void *) hose_data;
((struct iSeries_hose_arch_data *)(hose->arch_data))->hvBusNumber = hvbusnum;
LxBusNumber += 1; /* Keep track for debug */
}
}
pci_assign_all_busses = 1; /* Let Linux assign the bus numbers in pcibios_init */
return 0;
}
#endif /* CONFIG_PCI */
int iSeries_spread_lpevents( char * str )
{
/* The parameter is the number of processors to share in processing lp events */
unsigned long i;
unsigned long val = simple_strtoul(str, NULL, 0 );
if ( ( val > 0 ) && ( val <= maxPacas ) ) {
for( i=1; i<val; ++i )
xPaca[i].lpQueuePtr = xPaca[0].lpQueuePtr;
}
else
printk("invalid spread_lpevents %ld\n", val);
return 1;
}
int iSeries_decr_overclock_proc0( char * str )
{
unsigned long val = simple_strtoul(str, NULL, 0 );
if ( ( val >= 1 ) && ( val <= 48 ) ) {
decr_overclock_proc0_set = 1;
decr_overclock_proc0 = val;
printk("proc 0 decrementer overclock factor of %ld\n", val);
}
else {
printk("invalid proc 0 decrementer overclock factor of %ld\n", val);
}
return 1;
}
int iSeries_decr_overclock( char * str )
{
unsigned long val = simple_strtoul( str, NULL, 0 );
if ( ( val >= 1 ) && ( val <= 48 ) ) {
decr_overclock_set = 1;
decr_overclock = val;
printk("decrementer overclock factor of %ld\n", val);
}
else {
printk("invalid decrementer overclock factor of %ld\n", val);
}
return 1;
}
__setup("spread_lpevents=", iSeries_spread_lpevents );
__setup("decr_overclock_proc0=", iSeries_decr_overclock_proc0 );
__setup("decr_overclock=", iSeries_decr_overclock );
/*
* Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
* Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
*
* Module name: iSeries_setup.h
*
* Description:
* Architecture- / platform-specific boot-time initialization code for
* the IBM iSeries LPAR. Adapted from original code by Grant Erickson and
* code by Gary Thomas, Cort Dougan <cort@cs.nmt.edu>, and Dan Malek
* <dan@netx4.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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __ISERIES_SETUP_H__
#define __ISERIES_SETUP_H__
#ifdef __cplusplus
extern "C" {
#endif
extern void iSeries_init(unsigned long r3,
unsigned long ird_start,
unsigned long ird_end,
unsigned long cline_start,
unsigned long cline_end);
extern void iSeries_setup_arch(void);
extern int iSeries_setup_residual(char *buffer);
extern int iSeries_get_cpuinfo(char *buffer);
extern void iSeries_init_IRQ(void);
extern int iSeries_get_irq(struct pt_regs *regs);
extern void iSeries_restart(char *cmd);
extern void iSeries_power_off(void);
extern void iSeries_halt(void);
extern void iSeries_time_init(void);
extern int iSeries_set_rtc_time(unsigned long now);
extern unsigned long iSeries_get_rtc_time(void);
extern void iSeries_calibrate_decr(void);
extern void iSeries_progress( char *, unsigned short );
extern unsigned int iSeries_build_hose_list(void);
#ifdef __cplusplus
}
#endif
#endif /* __ISERIES_SETUP_H__ */
/*
* SMP support for iSeries/LPAR.
*
* Copyright (C) 2001 IBM Corp.
*
* 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/sched.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/hardirq.h>
#include <asm/softirq.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/smp.h>
#include <asm/residual.h>
#include <asm/time.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCall.h>
extern u64 get_tb64(void);
extern u64 next_jiffy_update_tb[];
static unsigned long iSeries_smp_message[NR_CPUS];
void iSeries_smp_message_recv( struct pt_regs * regs )
{
int cpu = smp_processor_id();
int msg;
if ( num_online_cpus() < 2 )
return;
for ( msg = 0; msg < 4; ++msg )
if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) )
smp_message_recv( msg, regs );
}
static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait)
{
int i;
for (i = 0; i < NR_CPUS; ++i) {
if (!cpu_online(i))
continue;
if ( (target == MSG_ALL) ||
(target == i) ||
((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) {
set_bit( msg, &iSeries_smp_message[i] );
HvCall_sendIPI(&(xPaca[i]));
}
}
}
static int smp_iSeries_probe(void)
{
unsigned i;
unsigned np;
struct ItLpPaca * lpPaca;
np = 0;
for (i=0; i<maxPacas; ++i) {
lpPaca = xPaca[i].xLpPacaPtr;
if ( lpPaca->xDynProcStatus < 2 )
++np;
}
smp_tb_synchronized = 1;
return np;
}
extern unsigned long decr_overclock;
static void smp_iSeries_kick_cpu(int nr)
{
struct ItLpPaca * lpPaca;
// Verify we have a Paca for processor nr
if ( ( nr <= 0 ) ||
( nr >= maxPacas ) )
return;
// Verify that our partition has a processor nr
lpPaca = xPaca[nr].xLpPacaPtr;
if ( lpPaca->xDynProcStatus >= 2 )
return;
xPaca[nr].default_decr = tb_ticks_per_jiffy / decr_overclock;
// The processor is currently spinning, waiting
// for the xProcStart field to become non-zero
// After we set xProcStart, the processor will
// continue on to secondary_start in iSeries_head.S
xPaca[nr].xProcStart = 1;
}
static void smp_iSeries_setup_cpu(int nr)
{
set_dec( xPaca[nr].default_decr );
}
static void smp_iSeries_space_timers(unsigned nr)
{
unsigned offset,i;
offset = tb_ticks_per_jiffy / nr;
for ( i=1; i<nr; ++i ) {
next_jiffy_update_tb[i] = next_jiffy_update_tb[i-1] + offset;
}
}
struct smp_ops_t iSeries_smp_ops = {
smp_iSeries_message_pass,
smp_iSeries_probe,
smp_iSeries_kick_cpu,
smp_iSeries_setup_cpu,
smp_iSeries_space_timers,
.give_timebase = smp_generic_give_timebase,
.take_timebase = smp_generic_take_timebase,
};
/*
* Time routes for iSeries LPAR.
*
* Copyright (C) 2001 IBM Corp.
*
* 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/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/kernel_stat.h>
#include <linux/mc146818rtc.h>
#include <linux/time.h>
#include <linux/init.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/nvram.h>
#include <asm/cache.h>
#include <asm/8xx_immap.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/iSeries/Paca.h>
u64 next_jiffy_update_tb[NR_CPUS];
extern u64 get_tb64(void);
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies;
extern unsigned long prof_cpu_mask;
extern unsigned int * prof_buffer;
extern unsigned long prof_len;
extern unsigned long prof_shift;
extern char _stext;
extern int is_soft_enabled(void);
static inline void ppc_do_profile (unsigned long nip)
{
if (!prof_buffer)
return;
/*
* Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
* (default is all CPUs.)
*/
if (!((1<<smp_processor_id()) & prof_cpu_mask))
return;
nip -= (unsigned long) &_stext;
nip >>= prof_shift;
/*
* Don't ignore out-of-bounds EIP values silently,
* put them into the last histogram slot, so if
* present, they will show up as a sharp peak.
*/
if (nip > prof_len-1)
nip = prof_len-1;
atomic_inc((atomic_t *)&prof_buffer[nip]);
}
/*
* For iSeries shared processors, we have to let the hypervisor
* set the hardware decrementer. We set a virtual decrementer
* in the ItLpPaca and call the hypervisor if the virtual
* decrementer is less than the current value in the hardware
* decrementer. (almost always the new decrementer value will
* be greater than the current hardware decementer so the hypervisor
* call will not be needed)
*
* When we yield the processor in idle.c (needed for shared processors)
* we cannot yield for too long or the 32-bit tbl may wrap and then
* we would lose jiffies. In addition, if we yield for too long,
* we might be pretty far off on timing for device drivers and such.
* When the hypervisor returns to us after a yield we need to
* determine whether decrementers might have been missed. If so
* we need to drive the timer_interrupt code to catch up (using
* the tb)
*
* For processors other than processor 0, there is no correction
* (in the code below) to next_dec so they're last_jiffy_stamp
* values are going to be way off.
*
*/
int timerRetEnabled = 0;
int timerRetDisabled = 0;
extern unsigned long iSeries_dec_value;
void timer_interrupt(struct pt_regs * regs)
{
long next_dec;
struct Paca * paca;
unsigned long cpu = smp_processor_id();
paca = (struct Paca *)mfspr(SPRG1);
if ( is_soft_enabled() )
BUG();
if (regs->softEnable)
timerRetEnabled++;
else
timerRetDisabled++;
irq_enter();
if (!user_mode(regs))
ppc_do_profile(instruction_pointer(regs));
while ( next_jiffy_update_tb[cpu] < get_tb64() ) {
#ifdef CONFIG_SMP
smp_local_timer_interrupt(regs);
#endif
if ( cpu == 0 ) {
write_lock(&xtime_lock);
do_timer(regs);
if ( (time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec - last_rtc_update >= 659 &&
abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ &&
jiffies - wall_jiffies == 1) {
if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0)
last_rtc_update = xtime.tv_sec+1;
else
/* Try again one minute later */
last_rtc_update += 60;
}
write_unlock(&xtime_lock);
}
next_jiffy_update_tb[cpu] += tb_ticks_per_jiffy;
}
next_dec = next_jiffy_update_tb[cpu] - get_tb64();
if ( next_dec > paca->default_decr )
next_dec = paca->default_decr;
paca->xLpPacaPtr->xDecrInt = 0;
set_dec( (unsigned)next_dec );
irq_exit();
}
/*
* HvCall.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//===========================================================================
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from the OS.
//
//===========================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
#include <asm/iSeries/Paca.h>
//-------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------
#ifndef _HVCALL_H
#define _HVCALL_H
/*
enum HvCall_ReturnCode
{
HvCall_Good = 0,
HvCall_Partial = 1,
HvCall_NotOwned = 2,
HvCall_NotFreed = 3,
HvCall_UnspecifiedError = 4
};
enum HvCall_TypeOfSIT
{
HvCall_ReduceOnly = 0,
HvCall_Unconditional = 1
};
enum HvCall_TypeOfYield
{
HvCall_YieldTimed = 0, // Yield until specified time
HvCall_YieldToActive = 1, // Yield until all active procs have run
HvCall_YieldToProc = 2 // Yield until the specified processor has run
};
enum HvCall_InterruptMasks
{
HvCall_MaskIPI = 0x00000001,
HvCall_MaskLpEvent = 0x00000002,
HvCall_MaskLpProd = 0x00000004,
HvCall_MaskTimeout = 0x00000008
};
enum HvCall_VaryOffChunkRc
{
HvCall_VaryOffSucceeded = 0,
HvCall_VaryOffWithdrawn = 1,
HvCall_ChunkInLoadArea = 2,
HvCall_ChunkInHPT = 3,
HvCall_ChunkNotAccessible = 4,
HvCall_ChunkInUse = 5
};
*/
/* Type of yield for HvCallBaseYieldProcessor */
#define HvCall_YieldTimed 0 // Yield until specified time (tb)
#define HvCall_YieldToActive 1 // Yield until all active procs have run
#define HvCall_YieldToProc 2 // Yield until the specified processor has run
/* interrupt masks for setEnabledInterrupts */
#define HvCall_MaskIPI 0x00000001
#define HvCall_MaskLpEvent 0x00000002
#define HvCall_MaskLpProd 0x00000004
#define HvCall_MaskTimeout 0x00000008
/* Log buffer formats */
#define HvCall_LogBuffer_ASCII 0
#define HvCall_LogBuffer_EBCDIC 1
#define HvCallBaseAckDeferredInts HvCallBase + 0
#define HvCallBaseCpmPowerOff HvCallBase + 1
#define HvCallBaseGetHwPatch HvCallBase + 2
#define HvCallBaseReIplSpAttn HvCallBase + 3
#define HvCallBaseSetASR HvCallBase + 4
#define HvCallBaseSetASRAndRfi HvCallBase + 5
#define HvCallBaseSetIMR HvCallBase + 6
#define HvCallBaseSendIPI HvCallBase + 7
#define HvCallBaseTerminateMachine HvCallBase + 8
#define HvCallBaseTerminateMachineSrc HvCallBase + 9
#define HvCallBaseProcessPlicInterrupts HvCallBase + 10
#define HvCallBaseIsPrimaryCpmOrMsdIpl HvCallBase + 11
#define HvCallBaseSetVirtualSIT HvCallBase + 12
#define HvCallBaseVaryOffThisProcessor HvCallBase + 13
#define HvCallBaseVaryOffMemoryChunk HvCallBase + 14
#define HvCallBaseVaryOffInteractivePercentage HvCallBase + 15
#define HvCallBaseSendLpProd HvCallBase + 16
#define HvCallBaseSetEnabledInterrupts HvCallBase + 17
#define HvCallBaseYieldProcessor HvCallBase + 18
#define HvCallBaseVaryOffSharedProcUnits HvCallBase + 19
#define HvCallBaseSetVirtualDecr HvCallBase + 20
#define HvCallBaseClearLogBuffer HvCallBase + 21
#define HvCallBaseGetLogBufferCodePage HvCallBase + 22
#define HvCallBaseGetLogBufferFormat HvCallBase + 23
#define HvCallBaseGetLogBufferLength HvCallBase + 24
#define HvCallBaseReadLogBuffer HvCallBase + 25
#define HvCallBaseSetLogBufferFormatAndCodePage HvCallBase + 26
#define HvCallBaseWriteLogBuffer HvCallBase + 27
#define HvCallBaseRouter28 HvCallBase + 28
#define HvCallBaseRouter29 HvCallBase + 29
#define HvCallBaseRouter30 HvCallBase + 30
//=====================================================================================
static inline void HvCall_setVirtualDecr(void)
{
// Ignore any error return codes - most likely means that the target value for the
// LP has been increased and this vary off would bring us below the new target.
HvCall0(HvCallBaseSetVirtualDecr);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=====================================================================
static inline void HvCall_yieldProcessor(unsigned typeOfYield, u64 yieldParm)
{
HvCall2( HvCallBaseYieldProcessor, typeOfYield, yieldParm );
}
//=====================================================================
static inline void HvCall_setEnabledInterrupts(u64 enabledInterrupts)
{
HvCall1(HvCallBaseSetEnabledInterrupts,enabledInterrupts);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=====================================================================
static inline void HvCall_clearLogBuffer(HvLpIndex lpindex)
{
HvCall1(HvCallBaseClearLogBuffer,lpindex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=====================================================================
static inline u32 HvCall_getLogBufferCodePage(HvLpIndex lpindex)
{
u32 retVal = HvCall1(HvCallBaseGetLogBufferCodePage,lpindex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================
static inline int HvCall_getLogBufferFormat(HvLpIndex lpindex)
{
int retVal = HvCall1(HvCallBaseGetLogBufferFormat,lpindex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================
static inline u32 HvCall_getLogBufferLength(HvLpIndex lpindex)
{
u32 retVal = HvCall1(HvCallBaseGetLogBufferLength,lpindex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================
static inline void HvCall_setLogBufferFormatAndCodepage(int format, u32 codePage)
{
HvCall2(HvCallBaseSetLogBufferFormatAndCodePage,format, codePage);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=====================================================================
int HvCall_readLogBuffer(HvLpIndex lpindex, void *buffer, u64 bufLen);
void HvCall_writeLogBuffer(const void *buffer, u64 bufLen);
//=====================================================================
static inline void HvCall_sendIPI(struct Paca * targetPaca)
{
HvCall1( HvCallBaseSendIPI, targetPaca->xPacaIndex );
}
//=====================================================================
static inline void HvCall_terminateMachineSrc(void)
{
HvCall0( HvCallBaseTerminateMachineSrc );
}
#endif // _HVCALL_H
/*
* HvCallCfg.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=====================================================================================
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from the OS.
//
//=====================================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
//-------------------------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------------------------
#ifndef _HVCALLCFG_H
#define _HVCALLCFG_H
enum HvCallCfg_ReqQual
{
HvCallCfg_Cur = 0,
HvCallCfg_Init = 1,
HvCallCfg_Max = 2,
HvCallCfg_Min = 3
};
#define HvCallCfgGetLps HvCallCfg + 0
#define HvCallCfgGetActiveLpMap HvCallCfg + 1
#define HvCallCfgGetLpVrmIndex HvCallCfg + 2
#define HvCallCfgGetLpMinSupportedPlicVrmIndex HvCallCfg + 3
#define HvCallCfgGetLpMinCompatablePlicVrmIndex HvCallCfg + 4
#define HvCallCfgGetLpVrmName HvCallCfg + 5
#define HvCallCfgGetSystemPhysicalProcessors HvCallCfg + 6
#define HvCallCfgGetPhysicalProcessors HvCallCfg + 7
#define HvCallCfgGetSystemMsChunks HvCallCfg + 8
#define HvCallCfgGetMsChunks HvCallCfg + 9
#define HvCallCfgGetInteractivePercentage HvCallCfg + 10
#define HvCallCfgIsBusDedicated HvCallCfg + 11
#define HvCallCfgGetBusOwner HvCallCfg + 12
#define HvCallCfgGetBusAllocation HvCallCfg + 13
#define HvCallCfgGetBusUnitOwner HvCallCfg + 14
#define HvCallCfgGetBusUnitAllocation HvCallCfg + 15
#define HvCallCfgGetVirtualBusPool HvCallCfg + 16
#define HvCallCfgGetBusUnitInterruptProc HvCallCfg + 17
#define HvCallCfgGetConfiguredBusUnitsForIntProc HvCallCfg + 18
#define HvCallCfgGetRioSanBusPool HvCallCfg + 19
#define HvCallCfgGetSharedPoolIndex HvCallCfg + 20
#define HvCallCfgGetSharedProcUnits HvCallCfg + 21
#define HvCallCfgGetNumProcsInSharedPool HvCallCfg + 22
#define HvCallCfgRouter23 HvCallCfg + 23
#define HvCallCfgRouter24 HvCallCfg + 24
#define HvCallCfgRouter25 HvCallCfg + 25
#define HvCallCfgRouter26 HvCallCfg + 26
#define HvCallCfgRouter27 HvCallCfg + 27
#define HvCallCfgGetMinRuntimeMsChunks HvCallCfg + 28
#define HvCallCfgSetMinRuntimeMsChunks HvCallCfg + 29
#define HvCallCfgGetVirtualLanIndexMap HvCallCfg + 30
#define HvCallCfgGetLpExecutionMode HvCallCfg + 31
#define HvCallCfgGetHostingLpIndex HvCallCfg + 32
//====================================================================
static inline HvLpIndex HvCallCfg_getLps(void)
{
HvLpIndex retVal = HvCall0(HvCallCfgGetLps);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline int HvCallCfg_isBusDedicated(u64 busIndex)
{
int retVal = HvCall1(HvCallCfgIsBusDedicated,busIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpIndex HvCallCfg_getBusOwner(u64 busIndex)
{
HvLpIndex retVal = HvCall1(HvCallCfgGetBusOwner,busIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpIndexMap HvCallCfg_getBusAllocation(u64 busIndex)
{
HvLpIndexMap retVal = HvCall1(HvCallCfgGetBusAllocation,busIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpIndexMap HvCallCfg_getActiveLpMap(void)
{
HvLpIndexMap retVal = HvCall0(HvCallCfgGetActiveLpMap);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpVirtualLanIndexMap HvCallCfg_getVirtualLanIndexMap(HvLpIndex lp)
{
// This is a new function in V5R1 so calls to this on older
// hypervisors will return -1
u64 retVal = HvCall1(HvCallCfgGetVirtualLanIndexMap, lp);
if(retVal == -1)
retVal = 0;
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getSystemMsChunks(void)
{
u64 retVal = HvCall0(HvCallCfgGetSystemMsChunks);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getMsChunks(HvLpIndex lp,enum HvCallCfg_ReqQual qual)
{
u64 retVal = HvCall2(HvCallCfgGetMsChunks,lp,qual);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getMinRuntimeMsChunks(HvLpIndex lp)
{
// NOTE: This function was added in v5r1 so older hypervisors will return a -1 value
u64 retVal = HvCall1(HvCallCfgGetMinRuntimeMsChunks,lp);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_setMinRuntimeMsChunks(u64 chunks)
{
u64 retVal = HvCall1(HvCallCfgSetMinRuntimeMsChunks,chunks);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getSystemPhysicalProcessors(void)
{
u64 retVal = HvCall0(HvCallCfgGetSystemPhysicalProcessors);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getPhysicalProcessors(HvLpIndex lp,enum HvCallCfg_ReqQual qual)
{
u64 retVal = HvCall2(HvCallCfgGetPhysicalProcessors,lp,qual);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline u64 HvCallCfg_getConfiguredBusUnitsForInterruptProc(HvLpIndex lp,
u16 hvLogicalProcIndex)
{
u64 retVal = HvCall2(HvCallCfgGetConfiguredBusUnitsForIntProc,lp,hvLogicalProcIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//==================================================================
static inline HvLpSharedPoolIndex HvCallCfg_getSharedPoolIndex(HvLpIndex lp)
{
HvLpSharedPoolIndex retVal =
HvCall1(HvCallCfgGetSharedPoolIndex,lp);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//==================================================================
static inline u64 HvCallCfg_getSharedProcUnits(HvLpIndex lp,enum HvCallCfg_ReqQual qual)
{
u64 retVal = HvCall2(HvCallCfgGetSharedProcUnits,lp,qual);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//==================================================================
static inline u64 HvCallCfg_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI)
{
u16 retVal = HvCall1(HvCallCfgGetNumProcsInSharedPool,sPI);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//==================================================================
static inline HvLpIndex HvCallCfg_getHostingLpIndex(HvLpIndex lp)
{
u64 retVal = HvCall1(HvCallCfgGetHostingLpIndex,lp);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
#endif // _HVCALLCFG_H
/*
* HvCallEvent.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//==================================================================
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from the OS.
//
//==================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include <asm/iSeries/HvCallSc.h>
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
#include <asm/iSeries/LparData.h>
//-------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------
#ifndef _HVCALLEVENT_H
#define _HVCALLEVENT_H
struct HvLpEvent;
typedef u8 HvLpEvent_Type;
typedef u8 HvLpEvent_AckInd;
typedef u8 HvLpEvent_AckType;
struct HvCallEvent_PackedParms
{
u8 xAckType:1;
u8 xAckInd:1;
u8 xRsvd:1;
u8 xTargetLp:5;
u8 xType;
u16 xSubtype;
HvLpInstanceId xSourceInstId;
HvLpInstanceId xTargetInstId;
};
typedef u8 HvLpDma_Direction;
typedef u8 HvLpDma_AddressType;
struct HvCallEvent_PackedDmaParms
{
u8 xDirection:1;
u8 xLocalAddrType:1;
u8 xRemoteAddrType:1;
u8 xRsvd1:5;
HvLpIndex xRemoteLp;
u8 xType;
u8 xRsvd2;
HvLpInstanceId xLocalInstId;
HvLpInstanceId xRemoteInstId;
};
typedef u64 HvLpEvent_Rc;
typedef u64 HvLpDma_Rc;
#define HvCallEventAckLpEvent HvCallEvent + 0
#define HvCallEventCancelLpEvent HvCallEvent + 1
#define HvCallEventCloseLpEventPath HvCallEvent + 2
#define HvCallEventDmaBufList HvCallEvent + 3
#define HvCallEventDmaSingle HvCallEvent + 4
#define HvCallEventDmaToSp HvCallEvent + 5
#define HvCallEventGetOverflowLpEvents HvCallEvent + 6
#define HvCallEventGetSourceLpInstanceId HvCallEvent + 7
#define HvCallEventGetTargetLpInstanceId HvCallEvent + 8
#define HvCallEventOpenLpEventPath HvCallEvent + 9
#define HvCallEventSetLpEventStack HvCallEvent + 10
#define HvCallEventSignalLpEvent HvCallEvent + 11
#define HvCallEventSignalLpEventParms HvCallEvent + 12
#define HvCallEventSetInterLpQueueIndex HvCallEvent + 13
#define HvCallEventSetLpEventQueueInterruptProc HvCallEvent + 14
#define HvCallEventRouter15 HvCallEvent + 15
//======================================================================
static inline void HvCallEvent_getOverflowLpEvents(u8 queueIndex)
{
HvCall1(HvCallEventGetOverflowLpEvents,queueIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//======================================================================
static inline void HvCallEvent_setInterLpQueueIndex(u8 queueIndex)
{
HvCall1(HvCallEventSetInterLpQueueIndex,queueIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//======================================================================
static inline void HvCallEvent_setLpEventStack(u8 queueIndex,
char * eventStackAddr,
u32 eventStackSize)
{
u64 abs_addr;
abs_addr = virt_to_absolute_outline( (unsigned long) eventStackAddr );
HvCall3(HvCallEventSetLpEventStack, queueIndex, abs_addr, eventStackSize);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//======================================================================
static inline void HvCallEvent_setLpEventQueueInterruptProc(u8 queueIndex,
u16 lpLogicalProcIndex)
{
HvCall2(HvCallEventSetLpEventQueueInterruptProc,queueIndex,lpLogicalProcIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=====================================================================
static inline HvLpEvent_Rc HvCallEvent_signalLpEvent(struct HvLpEvent* event)
{
u64 abs_addr;
HvLpEvent_Rc retVal;
abs_addr = virt_to_absolute_outline( (unsigned long) event );
retVal = (HvLpEvent_Rc)HvCall1(HvCallEventSignalLpEvent, abs_addr);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================
static inline HvLpEvent_Rc HvCallEvent_signalLpEventFast(HvLpIndex targetLp,
HvLpEvent_Type type,
u16 subtype,
HvLpEvent_AckInd ackInd,
HvLpEvent_AckType ackType,
HvLpInstanceId sourceInstanceId,
HvLpInstanceId targetInstanceId,
u64 correlationToken,
u64 eventData1,
u64 eventData2,
u64 eventData3,
u64 eventData4,
u64 eventData5)
{
HvLpEvent_Rc retVal;
// Pack the misc bits into a single Dword to pass to PLIC
union
{
struct HvCallEvent_PackedParms parms;
u64 dword;
} packed;
packed.parms.xAckType = ackType;
packed.parms.xAckInd = ackInd;
packed.parms.xRsvd = 0;
packed.parms.xTargetLp = targetLp;
packed.parms.xType = type;
packed.parms.xSubtype = subtype;
packed.parms.xSourceInstId = sourceInstanceId;
packed.parms.xTargetInstId = targetInstanceId;
retVal = (HvLpEvent_Rc)HvCall7(HvCallEventSignalLpEventParms,
packed.dword,
correlationToken,
eventData1,eventData2,
eventData3,eventData4,
eventData5);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpEvent_Rc HvCallEvent_ackLpEvent(struct HvLpEvent* event)
{
u64 abs_addr;
HvLpEvent_Rc retVal;
abs_addr = virt_to_absolute_outline( (unsigned long) event );
retVal = (HvLpEvent_Rc)HvCall1(HvCallEventAckLpEvent, abs_addr);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//====================================================================
static inline HvLpEvent_Rc HvCallEvent_cancelLpEvent(struct HvLpEvent* event)
{
u64 abs_addr;
HvLpEvent_Rc retVal;
abs_addr = virt_to_absolute_outline( (unsigned long) event );
retVal = (HvLpEvent_Rc)HvCall1(HvCallEventCancelLpEvent, abs_addr);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline HvLpInstanceId HvCallEvent_getSourceLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type)
{
HvLpInstanceId retVal;
retVal = HvCall2(HvCallEventGetSourceLpInstanceId,targetLp,type);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline HvLpInstanceId HvCallEvent_getTargetLpInstanceId(HvLpIndex targetLp, HvLpEvent_Type type)
{
HvLpInstanceId retVal;
retVal = HvCall2(HvCallEventGetTargetLpInstanceId,targetLp,type);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//===================================================================
static inline void HvCallEvent_openLpEventPath(HvLpIndex targetLp,
HvLpEvent_Type type)
{
HvCall2(HvCallEventOpenLpEventPath,targetLp,type);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//===================================================================
static inline void HvCallEvent_closeLpEventPath(HvLpIndex targetLp,
HvLpEvent_Type type)
{
HvCall2(HvCallEventCloseLpEventPath,targetLp,type);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//===================================================================
static inline HvLpDma_Rc HvCallEvent_dmaBufList(HvLpEvent_Type type,
HvLpIndex remoteLp,
HvLpDma_Direction direction,
HvLpInstanceId localInstanceId,
HvLpInstanceId remoteInstanceId,
HvLpDma_AddressType localAddressType,
HvLpDma_AddressType remoteAddressType,
// Do these need to be converted to
// absolute addresses?
u64 localBufList,
u64 remoteBufList,
u32 transferLength)
{
HvLpDma_Rc retVal;
// Pack the misc bits into a single Dword to pass to PLIC
union
{
struct HvCallEvent_PackedDmaParms parms;
u64 dword;
} packed;
packed.parms.xDirection = direction;
packed.parms.xLocalAddrType = localAddressType;
packed.parms.xRemoteAddrType = remoteAddressType;
packed.parms.xRsvd1 = 0;
packed.parms.xRemoteLp = remoteLp;
packed.parms.xType = type;
packed.parms.xRsvd2 = 0;
packed.parms.xLocalInstId = localInstanceId;
packed.parms.xRemoteInstId = remoteInstanceId;
retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaBufList,
packed.dword,
localBufList,
remoteBufList,
transferLength);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=================================================================
static inline HvLpDma_Rc HvCallEvent_dmaSingle(HvLpEvent_Type type,
HvLpIndex remoteLp,
HvLpDma_Direction direction,
HvLpInstanceId localInstanceId,
HvLpInstanceId remoteInstanceId,
HvLpDma_AddressType localAddressType,
HvLpDma_AddressType remoteAddressType,
u64 localAddrOrTce,
u64 remoteAddrOrTce,
u32 transferLength)
{
HvLpDma_Rc retVal;
// Pack the misc bits into a single Dword to pass to PLIC
union
{
struct HvCallEvent_PackedDmaParms parms;
u64 dword;
} packed;
packed.parms.xDirection = direction;
packed.parms.xLocalAddrType = localAddressType;
packed.parms.xRemoteAddrType = remoteAddressType;
packed.parms.xRsvd1 = 0;
packed.parms.xRemoteLp = remoteLp;
packed.parms.xType = type;
packed.parms.xRsvd2 = 0;
packed.parms.xLocalInstId = localInstanceId;
packed.parms.xRemoteInstId = remoteInstanceId;
retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaSingle,
packed.dword,
localAddrOrTce,
remoteAddrOrTce,
transferLength);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=================================================================
static inline HvLpDma_Rc HvCallEvent_dmaToSp(void* local, u32 remote, u32 length, HvLpDma_Direction dir)
{
u64 abs_addr;
HvLpDma_Rc retVal;
abs_addr = virt_to_absolute_outline( (unsigned long) local );
retVal = (HvLpDma_Rc)HvCall4(HvCallEventDmaToSp,
abs_addr,
remote,
length,
dir);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//================================================================
#endif // _HVCALLEVENT_H
/*
* HvCallHpt.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//============================================================================
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from the OS.
//
//============================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
//-------------------------------------------------------------------
// Other Includes
//-------------------------------------------------------------------
#ifndef _PPC_MMU_H
#include <asm/mmu.h>
#endif
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#ifndef _HVCALLHPT_H
#define _HVCALLHPT_H
#define HvCallHptGetHptAddress HvCallHpt + 0
#define HvCallHptGetHptPages HvCallHpt + 1
#define HvCallHptSetPp HvCallHpt + 5
#define HvCallHptUpdate HvCallHpt + 7
#define HvCallHptInvalidateNoSyncICache HvCallHpt + 8
#define HvCallHptGet HvCallHpt + 11
#define HvCallHptFindNextValid HvCallHpt + 12
#define HvCallHptFindValid HvCallHpt + 13
#define HvCallHptAddValidate HvCallHpt + 16
#define HvCallHptInvalidateSetSwBitsGet HvCallHpt + 18
//============================================================================
static inline u64 HvCallHpt_getHptAddress(void)
{
u64 retval = HvCall0(HvCallHptGetHptAddress);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retval;
}
//============================================================================
static inline u64 HvCallHpt_getHptPages(void)
{
u64 retval = HvCall0(HvCallHptGetHptPages);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retval;
}
//=============================================================================
static inline void HvCallHpt_setPp(u32 hpteIndex, u8 value)
{
HvCall2( HvCallHptSetPp, hpteIndex, value );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=============================================================================
static inline void HvCallHpt_invalidateNoSyncICache(u32 hpteIndex)
{
HvCall1( HvCallHptInvalidateNoSyncICache, hpteIndex );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=============================================================================
static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, u8 bitsoff )
{
u64 compressedStatus;
compressedStatus = HvCall4( HvCallHptInvalidateSetSwBitsGet, hpteIndex, bitson, bitsoff, 1 );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return compressedStatus;
}
//=============================================================================
static inline u64 HvCallHpt_findValid( PTE *hpte, u64 vpn )
{
u64 retIndex = HvCall1Ret16( HvCallHptFindValid, hpte, vpn );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retIndex;
}
//=============================================================================
static inline u64 HvCallHpt_findNextValid( PTE *hpte, u32 hpteIndex, u8 bitson, u8 bitsoff )
{
u64 retIndex = HvCall3Ret16( HvCallHptFindNextValid, hpte, hpteIndex, bitson, bitsoff );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retIndex;
}
//=============================================================================
static inline void HvCallHpt_get( PTE *hpte, u32 hpteIndex )
{
HvCall2Ret16( HvCallHptFindValid, hpte, hpteIndex, 0 );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//============================================================================
static inline void HvCallHpt_addValidate( u32 hpteIndex,
u32 hBit,
PTE *hpte )
{
HvCall4( HvCallHptAddValidate, hpteIndex,
hBit, (*((u64 *)hpte)), (*(((u64 *)hpte)+1)) );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//=============================================================================
#endif // _HVCALLHPT_H
/************************************************************************/
/* Provides the Hypervisor PCI calls for iSeries Linux Parition. */
/* Copyright (C) 20yy <Wayne G Holm> <IBM Corporation> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Jan 9, 2001 */
/************************************************************************/
//============================================================================
// Header File Id
// Name______________: HvCallPci.H
//
// Description_______:
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from SLIC.
//
//============================================================================
//-------------------------------------------------------------------
// Forward declarations
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/as400/HvTypes.h>
#endif
//-------------------------------------------------------------------
// Other Includes
//-------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#ifndef _HVCALLPCI_H
#define _HVCALLPCI_H
struct HvCallPci_DsaAddr { // make sure this struct size is 64-bits total
u16 busNumber;
u8 subBusNumber;
u8 deviceId;
u8 barNumber;
u8 reserved[3];
};
struct HvCallPci_LoadReturn {
u64 rc;
u64 value;
};
enum HvCallPci_DeviceType {HvCallPci_NodeDevice = 1,
HvCallPci_SpDevice = 2,
HvCallPci_IopDevice = 3,
HvCallPci_BridgeDevice = 4,
HvCallPci_MultiFunctionDevice = 5,
HvCallPci_IoaDevice = 6
};
struct HvCallPci_DeviceInfo {
u32 deviceType; // See DeviceType enum for values
};
struct HvCallPci_BusUnitInfo {
u32 sizeReturned; // length of data returned
u32 deviceType; // see DeviceType enum for values
};
struct HvCallPci_BridgeInfo {
struct HvCallPci_BusUnitInfo busUnitInfo; // Generic bus unit info
u8 subBusNumber; // Bus number of secondary bus
u8 maxAgents; // Max idsels on secondary bus
};
// Maximum BusUnitInfo buffer size. Provided for clients so they can allocate
// a buffer big enough for any type of bus unit. Increase as needed.
enum {HvCallPci_MaxBusUnitInfoSize = 128};
struct HvCallPci_BarParms {
u64 vaddr;
u64 raddr;
u64 size;
u64 protectStart;
u64 protectEnd;
u64 relocationOffset;
u64 pciAddress;
u64 reserved[3];
};
enum HvCallPci_VpdType {
HvCallPci_BusVpd = 1,
HvCallPci_BusAdapterVpd = 2
};
#define HvCallPciConfigLoad8 HvCallPci + 0
#define HvCallPciConfigLoad16 HvCallPci + 1
#define HvCallPciConfigLoad32 HvCallPci + 2
#define HvCallPciConfigStore8 HvCallPci + 3
#define HvCallPciConfigStore16 HvCallPci + 4
#define HvCallPciConfigStore32 HvCallPci + 5
#define HvCallPciEoi HvCallPci + 16
#define HvCallPciGetBarParms HvCallPci + 18
#define HvCallPciMaskFisr HvCallPci + 20
#define HvCallPciUnmaskFisr HvCallPci + 21
#define HvCallPciSetSlotReset HvCallPci + 25
#define HvCallPciGetDeviceInfo HvCallPci + 27
#define HvCallPciGetCardVpd HvCallPci + 28
#define HvCallPciBarLoad8 HvCallPci + 40
#define HvCallPciBarLoad16 HvCallPci + 41
#define HvCallPciBarLoad32 HvCallPci + 42
#define HvCallPciBarLoad64 HvCallPci + 43
#define HvCallPciBarStore8 HvCallPci + 44
#define HvCallPciBarStore16 HvCallPci + 45
#define HvCallPciBarStore32 HvCallPci + 46
#define HvCallPciBarStore64 HvCallPci + 47
#define HvCallPciMaskInterrupts HvCallPci + 48
#define HvCallPciUnmaskInterrupts HvCallPci + 49
#define HvCallPciGetBusUnitInfo HvCallPci + 50
//============================================================================
static inline u64 HvCallPci_configLoad8(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u8 *value)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
HvCall3Ret16(HvCallPciConfigLoad8, &retVal, *(u64 *)&dsa, offset, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*value = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_configLoad16(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u16 *value)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
HvCall3Ret16(HvCallPciConfigLoad16, &retVal, *(u64 *)&dsa, offset, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*value = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_configLoad32(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u32 *value)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
HvCall3Ret16(HvCallPciConfigLoad32, &retVal, *(u64 *)&dsa, offset, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*value = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u8 value)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
retVal = HvCall4(HvCallPciConfigStore8, *(u64 *)&dsa, offset, value, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_configStore16(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u16 value)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
retVal = HvCall4(HvCallPciConfigStore16, *(u64 *)&dsa, offset, value, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_configStore32(u16 busNumber, u8 subBusNumber,
u8 deviceId, u32 offset,
u32 value)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumber;
dsa.subBusNumber = subBusNumber;
dsa.deviceId = deviceId;
retVal = HvCall4(HvCallPciConfigStore32, *(u64 *)&dsa, offset, value, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_barLoad8(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u8* valueParm)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
HvCall3Ret16(HvCallPciBarLoad8, &retVal, *(u64 *)&dsa, offsetParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*valueParm = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_barLoad16(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u16* valueParm)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
HvCall3Ret16(HvCallPciBarLoad16, &retVal, *(u64 *)&dsa, offsetParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*valueParm = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_barLoad32(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u32* valueParm)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
HvCall3Ret16(HvCallPciBarLoad32, &retVal, *(u64 *)&dsa, offsetParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*valueParm = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_barLoad64(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u64* valueParm)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
HvCall3Ret16(HvCallPciBarLoad64, &retVal, *(u64 *)&dsa, offsetParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
*valueParm = retVal.value;
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_barStore8(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u8 valueParm)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
retVal = HvCall4(HvCallPciBarStore8, *(u64 *)&dsa, offsetParm, valueParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_barStore16(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u16 valueParm)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
retVal = HvCall4(HvCallPciBarStore16, *(u64 *)&dsa, offsetParm, valueParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_barStore32(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u32 valueParm)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
retVal = HvCall4(HvCallPciBarStore32, *(u64 *)&dsa, offsetParm, valueParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_barStore64(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 offsetParm,
u64 valueParm)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
retVal = HvCall4(HvCallPciBarStore64, *(u64 *)&dsa, offsetParm, valueParm, 0);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_eoi(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm)
{
struct HvCallPci_DsaAddr dsa;
struct HvCallPci_LoadReturn retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
HvCall1Ret16(HvCallPciEoi, &retVal, *(u64*)&dsa);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal.rc;
}
//============================================================================
static inline u64 HvCallPci_getBarParms(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u8 barNumberParm,
u64 parms,
u32 sizeofParms)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
dsa.barNumber = barNumberParm;
retVal = HvCall3(HvCallPciGetBarParms, *(u64*)&dsa, parms, sizeofParms);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_maskFisr(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 fisrMask)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall2(HvCallPciMaskFisr, *(u64*)&dsa, fisrMask);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_unmaskFisr(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 fisrMask)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall2(HvCallPciUnmaskFisr, *(u64*)&dsa, fisrMask);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_setSlotReset(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 onNotOff)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall2(HvCallPciSetSlotReset, *(u64*)&dsa, onNotOff);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_getDeviceInfo(u16 busNumberParm,
u8 subBusParm,
u8 deviceNumberParm,
u64 parms,
u32 sizeofParms)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceNumberParm << 4;
retVal = HvCall3(HvCallPciGetDeviceInfo, *(u64*)&dsa, parms, sizeofParms);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_maskInterrupts(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 interruptMask)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall2(HvCallPciMaskInterrupts, *(u64*)&dsa, interruptMask);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_unmaskInterrupts(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 interruptMask)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall2(HvCallPciUnmaskInterrupts, *(u64*)&dsa, interruptMask);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline u64 HvCallPci_getBusUnitInfo(u16 busNumberParm,
u8 subBusParm,
u8 deviceIdParm,
u64 parms,
u32 sizeofParms)
{
struct HvCallPci_DsaAddr dsa;
u64 retVal;
*((u64*)&dsa) = 0;
dsa.busNumber = busNumberParm;
dsa.subBusNumber = subBusParm;
dsa.deviceId = deviceIdParm;
retVal = HvCall3(HvCallPciGetBusUnitInfo, *(u64*)&dsa, parms, sizeofParms);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//============================================================================
static inline int HvCallPci_getBusVpd(u16 busNumParm, u64 destParm, u16 sizeParm) {
int xRetSize;
u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusVpd);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
if (xRc == -1)
xRetSize = -1;
else
xRetSize = xRc & 0xFFFF;
return xRetSize;
}
//============================================================================
static inline int HvCallPci_getBusAdapterVpd(u16 busNumParm, u64 destParm, u16 sizeParm) {
int xRetSize;
u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, sizeParm, HvCallPci_BusAdapterVpd);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
if (xRc == -1)
xRetSize = -1;
else
xRetSize = xRc & 0xFFFF;
return xRetSize;
}
//============================================================================
#endif // _HVCALLPCI_H
/*
* HvCallSc.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
#ifndef _HVCALLSC_H
#define _HVCALLSC_H
#define HvCallBase 0x80000000
#define HvCallCfg 0x80020000
#define HvCallEvent 0x80030000
#define HvCallHpt 0x80040000
#define HvCallPci 0x80050000
#define HvCallSm 0x80070000
#define HvCallXm 0x80090000
u64 HvCall0( u32 );
u64 HvCall1( u32, u64 );
u64 HvCall2( u32, u64, u64 );
u64 HvCall3( u32, u64, u64, u64 );
u64 HvCall4( u32, u64, u64, u64, u64 );
u64 HvCall5( u32, u64, u64, u64, u64, u64 );
u64 HvCall6( u32, u64, u64, u64, u64, u64, u64 );
u64 HvCall7( u32, u64, u64, u64, u64, u64, u64, u64 );
u64 HvCall0Ret16( u32, void * );
u64 HvCall1Ret16( u32, void *, u64 );
u64 HvCall2Ret16( u32, void *, u64, u64 );
u64 HvCall3Ret16( u32, void *, u64, u64, u64 );
u64 HvCall4Ret16( u32, void *, u64, u64, u64, u64 );
u64 HvCall5Ret16( u32, void *, u64, u64, u64, u64, u64 );
u64 HvCall6Ret16( u32, void *, u64, u64, u64, u64, u64, u64 );
u64 HvCall7Ret16( u32, void *, u64, u64 ,u64 ,u64 ,u64 ,u64 ,u64 );
#endif /* _HVCALLSC_H */
/*
* HvCallSm.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//============================================================================
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from the OS.
//
//============================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#ifndef _HVCALLSM_H
#define _HVCALLSM_H
#define HvCallSmGet64BitsOfAccessMap HvCallSm + 11
//============================================================================
static inline u64 HvCallSm_get64BitsOfAccessMap(
HvLpIndex lpIndex, u64 indexIntoBitMap )
{
u64 retval = HvCall2(HvCallSmGet64BitsOfAccessMap, lpIndex,
indexIntoBitMap );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retval;
}
//============================================================================
#endif // _HVCALLSM_H
//============================================================================
// Header File Id
// Name______________: HvCallXm.H
//
// Description_______:
//
// This file contains the "hypervisor call" interface which is used to
// drive the hypervisor from SLIC.
//
//============================================================================
//-------------------------------------------------------------------
// Forward declarations
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _HVCALLSC_H
#include "HvCallSc.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
//-------------------------------------------------------------------
// Other Includes
//-------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
#ifndef _HVCALLXM_H
#define _HVCALLXM_H
#define HvCallXmGetTceTableParms HvCallXm + 0
#define HvCallXmTestBus HvCallXm + 1
#define HvCallXmConnectBusUnit HvCallXm + 2
#define HvCallXmLoadTod HvCallXm + 8
#define HvCallXmTestBusUnit HvCallXm + 9
#define HvCallXmSetTce HvCallXm + 11
#define HvCallXmSetTces HvCallXm + 13
//============================================================================
static inline void HvCallXm_getTceTableParms(u64 cb)
{
HvCall1(HvCallXmGetTceTableParms, cb);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
}
//============================================================================
static inline u64 HvCallXm_setTce(u64 tceTableToken, u64 tceOffset, u64 tce)
{
u64 retval = HvCall3(HvCallXmSetTce, tceTableToken, tceOffset, tce );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retval;
}
//============================================================================
static inline u64 HvCallXm_setTces(u64 tceTableToken, u64 tceOffset, u64 numTces, u64 tce1, u64 tce2, u64 tce3, u64 tce4)
{
u64 retval = HvCall7(HvCallXmSetTces, tceTableToken, tceOffset, numTces,
tce1, tce2, tce3, tce4 );
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retval;
}
//=============================================================================
static inline u64 HvCallXm_testBus(u16 busNumber)
{
u64 retVal = HvCall1(HvCallXmTestBus, busNumber);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================================
static inline u64 HvCallXm_testBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId) {
u64 busUnitNumber = (subBusNumber << 8) | deviceId;
u64 retVal = HvCall2(HvCallXmTestBusUnit, busNumber, busUnitNumber);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================================
static inline u64 HvCallXm_connectBusUnit(u16 busNumber, u8 subBusNumber, u8 deviceId,
u64 interruptToken)
{
u64 busUnitNumber = (subBusNumber << 8) | deviceId;
u64 queueIndex = 0; // HvLpConfig::mapDsaToQueueIndex(HvLpDSA(busNumber, xBoard, xCard));
u64 retVal = HvCall5(HvCallXmConnectBusUnit, busNumber, busUnitNumber,
interruptToken, 0, queueIndex);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================================
static inline u64 HvCallXm_loadTod(void)
{
u64 retVal = HvCall0(HvCallXmLoadTod);
// getPaca()->adjustHmtForNoOfSpinLocksHeld();
return retVal;
}
//=====================================================================================
#endif // _HVCALLXM_H
/*
* HvLpConfig.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//===========================================================================
//
// This file contains the interface to the LPAR configuration data
// to determine which resources should be allocated to each partition.
//
//===========================================================================
#ifndef _HVCALLCFG_H
#include "HvCallCfg.h"
#endif
#ifndef _HVTYPES_H
#include <asm/iSeries/HvTypes.h>
#endif
#ifndef _ITLPNACA_H
#include <asm/iSeries/ItLpNaca.h>
#endif
#ifndef _LPARDATA_H
#include <asm/iSeries/LparData.h>
#endif
#ifndef _HVLPCONFIG_H
#define _HVLPCONFIG_H
//-------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------
extern HvLpIndex HvLpConfig_getLpIndex_outline(void);
//===================================================================
static inline HvLpIndex HvLpConfig_getLpIndex(void)
{
return itLpNaca.xLpIndex;
}
//===================================================================
static inline HvLpIndex HvLpConfig_getPrimaryLpIndex(void)
{
return itLpNaca.xPrimaryLpIndex;
}
//=================================================================
static inline HvLpIndex HvLpConfig_getLps(void)
{
return HvCallCfg_getLps();
}
//=================================================================
static inline HvLpIndexMap HvLpConfig_getActiveLpMap(void)
{
return HvCallCfg_getActiveLpMap();
}
//=================================================================
static inline u64 HvLpConfig_getSystemMsMegs(void)
{
return HvCallCfg_getSystemMsChunks() / HvChunksPerMeg;
}
//=================================================================
static inline u64 HvLpConfig_getSystemMsChunks(void)
{
return HvCallCfg_getSystemMsChunks();
}
//=================================================================
static inline u64 HvLpConfig_getSystemMsPages(void)
{
return HvCallCfg_getSystemMsChunks() * HvPagesPerChunk;
}
//================================================================
static inline u64 HvLpConfig_getMsMegs(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) / HvChunksPerMeg;
}
//================================================================
static inline u64 HvLpConfig_getMsChunks(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur);
}
//================================================================
static inline u64 HvLpConfig_getMsPages(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * HvPagesPerChunk;
}
//================================================================
static inline u64 HvLpConfig_getMinMsMegs(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) / HvChunksPerMeg;
}
//================================================================
static inline u64 HvLpConfig_getMinMsChunks(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min);
}
//================================================================
static inline u64 HvLpConfig_getMinMsPages(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Min) * HvPagesPerChunk;
}
//================================================================
static inline u64 HvLpConfig_getMinRuntimeMsMegs(void)
{
return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) / HvChunksPerMeg;
}
//===============================================================
static inline u64 HvLpConfig_getMinRuntimeMsChunks(void)
{
return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex());
}
//===============================================================
static inline u64 HvLpConfig_getMinRuntimeMsPages(void)
{
return HvCallCfg_getMinRuntimeMsChunks(HvLpConfig_getLpIndex()) * HvPagesPerChunk;
}
//===============================================================
static inline u64 HvLpConfig_getMaxMsMegs(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) / HvChunksPerMeg;
}
//===============================================================
static inline u64 HvLpConfig_getMaxMsChunks(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max);
}
//===============================================================
static inline u64 HvLpConfig_getMaxMsPages(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Max) * HvPagesPerChunk;
}
//===============================================================
static inline u64 HvLpConfig_getInitMsMegs(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) / HvChunksPerMeg;
}
//===============================================================
static inline u64 HvLpConfig_getInitMsChunks(void)
{
return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init);
}
//===============================================================
static inline u64 HvLpConfig_getInitMsPages(void)
{ return HvCallCfg_getMsChunks(HvLpConfig_getLpIndex(),HvCallCfg_Init) * HvPagesPerChunk;
}
//===============================================================
static inline u64 HvLpConfig_getSystemPhysicalProcessors(void)
{
return HvCallCfg_getSystemPhysicalProcessors();
}
//===============================================================
static inline u64 HvLpConfig_getSystemLogicalProcessors(void)
{
return HvCallCfg_getSystemPhysicalProcessors() * (/*getPaca()->getSecondaryThreadCount() +*/ 1);
}
//===============================================================
static inline u64 HvLpConfig_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI)
{
return HvCallCfg_getNumProcsInSharedPool(sPI);
}
//===============================================================
static inline u64 HvLpConfig_getPhysicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur);
}
//===============================================================
static inline u64 HvLpConfig_getLogicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Cur) * (/*getPaca()->getSecondaryThreadCount() +*/ 1);
}
//===============================================================
static inline HvLpSharedPoolIndex HvLpConfig_getSharedPoolIndex(void)
{
return HvCallCfg_getSharedPoolIndex(HvLpConfig_getLpIndex());
}
//===============================================================
static inline u64 HvLpConfig_getSharedProcUnits(void)
{
return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Cur);
}
//===============================================================
static inline u64 HvLpConfig_getMinSharedProcUnits(void)
{
return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Min);
}
//===============================================================
static inline u64 HvLpConfig_getMaxSharedProcUnits(void)
{
return HvCallCfg_getSharedProcUnits(HvLpConfig_getLpIndex(),HvCallCfg_Max);
}
//===============================================================
static inline u64 HvLpConfig_getMinPhysicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min);
}
//===============================================================
static inline u64 HvLpConfig_getMinLogicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Min) * (/*getPaca()->getSecondaryThreadCount() +*/ 1);
}
//===============================================================
static inline u64 HvLpConfig_getMaxPhysicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max);
}
//===============================================================
static inline u64 HvLpConfig_getMaxLogicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Max) * (/*getPaca()->getSecondaryThreadCount() +*/ 1);
}
//===============================================================
static inline u64 HvLpConfig_getInitPhysicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init);
}
//===============================================================
static inline u64 HvLpConfig_getInitLogicalProcessors(void)
{
return HvCallCfg_getPhysicalProcessors(HvLpConfig_getLpIndex(),HvCallCfg_Init) * (/*getPaca()->getSecondaryThreadCount() +*/ 1);
}
//================================================================
static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMap(void)
{
return HvCallCfg_getVirtualLanIndexMap(HvLpConfig_getLpIndex_outline());
}
//===============================================================
static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMapForLp(HvLpIndex lp)
{
return HvCallCfg_getVirtualLanIndexMap(lp);
}
//================================================================
static inline HvLpIndex HvLpConfig_getBusOwner(HvBusNumber busNumber)
{
return HvCallCfg_getBusOwner(busNumber);
}
//===============================================================
static inline int HvLpConfig_isBusDedicated(HvBusNumber busNumber)
{
return HvCallCfg_isBusDedicated(busNumber);
}
//================================================================
static inline HvLpIndexMap HvLpConfig_getBusAllocation(HvBusNumber busNumber)
{
return HvCallCfg_getBusAllocation(busNumber);
}
//================================================================
// returns the absolute real address of the load area
static inline u64 HvLpConfig_getLoadAddress(void)
{
return itLpNaca.xLoadAreaAddr & 0x7fffffffffffffff;
}
//================================================================
static inline u64 HvLpConfig_getLoadPages(void)
{
return itLpNaca.xLoadAreaChunks * HvPagesPerChunk;
}
//================================================================
static inline int HvLpConfig_isBusOwnedByThisLp(HvBusNumber busNumber)
{
HvLpIndex busOwner = HvLpConfig_getBusOwner(busNumber);
return (busOwner == HvLpConfig_getLpIndex());
}
//================================================================
static inline int HvLpConfig_doLpsCommunicateOnVirtualLan(HvLpIndex lp1, HvLpIndex lp2)
{
HvLpVirtualLanIndexMap virtualLanIndexMap1 = HvCallCfg_getVirtualLanIndexMap( lp1 );
HvLpVirtualLanIndexMap virtualLanIndexMap2 = HvCallCfg_getVirtualLanIndexMap( lp2 );
return ((virtualLanIndexMap1 & virtualLanIndexMap2) != 0);
}
//================================================================
static inline HvLpIndex HvLpConfig_getHostingLpIndex(HvLpIndex lp)
{
return HvCallCfg_getHostingLpIndex(lp);
}
//================================================================
#endif // _HVLPCONFIG_H
/*
* HvLpEvent.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//======================================================================
//
// This file contains the class for HV events in the system.
//
//=====================================================================
#ifndef _HVLPEVENT_H
#define _HVLPEVENT_H
#include <asm/types.h>
#include <asm/ptrace.h>
#include <asm/iSeries/HvTypes.h>
#ifndef _HVCALLEVENT_H
#include <asm/iSeries/HvCallEvent.h>
#endif
//=====================================================================
//
// HvLpEvent is the structure for Lp Event messages passed between
// partitions through PLIC.
//
//=====================================================================
struct HvEventFlags
{
u8 xValid:1; // Indicates a valid request x00-x00
u8 xRsvd1:4; // Reserved ...
u8 xAckType:1; // Immediate or deferred ...
u8 xAckInd:1; // Indicates if ACK required ...
u8 xFunction:1; // Interrupt or Acknowledge ...
};
struct HvLpEvent
{
struct HvEventFlags xFlags; // Event flags x00-x00
u8 xType; // Type of message x01-x01
u16 xSubtype; // Subtype for event x02-x03
u8 xSourceLp; // Source LP x04-x04
u8 xTargetLp; // Target LP x05-x05
u8 xSizeMinus1; // Size of Derived class - 1 x06-x06
u8 xRc; // RC for Ack flows x07-x07
u16 xSourceInstanceId; // Source sides instance id x08-x09
u16 xTargetInstanceId; // Target sides instance id x0A-x0B
union {
u32 xSubtypeData; // Data usable by the subtype x0C-x0F
u16 xSubtypeDataShort[2]; // Data as 2 shorts
u8 xSubtypeDataChar[4]; // Data as 4 chars
} x;
u64 xCorrelationToken; // Unique value for source/type x10-x17
};
// Lp Event handler function
typedef void (*LpEventHandler)(struct HvLpEvent *, struct pt_regs *);
// Register a handler for an event type
// returns 0 on success
extern int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler hdlr);
// Unregister a handler for an event type
// returns 0 on success
// Unregister will fail if there are any paths open for the type
extern int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType );
// Open an Lp Event Path for an event type
// returns 0 on success
// openPath will fail if there is no handler registered for the event type.
// The lpIndex specified is the partition index for the target partition
// (for VirtualIo, VirtualLan and SessionMgr) other types specify zero)
extern int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex );
// Close an Lp Event Path for a type and partition
// returns 0 on sucess
extern int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex );
#define HvLpEvent_Type_Hypervisor 0
#define HvLpEvent_Type_MachineFac 1
#define HvLpEvent_Type_SessionMgr 2
#define HvLpEvent_Type_SpdIo 3
#define HvLpEvent_Type_VirtualBus 4
#define HvLpEvent_Type_PciIo 5
#define HvLpEvent_Type_RioIo 6
#define HvLpEvent_Type_VirtualLan 7
#define HvLpEvent_Type_VirtualIo 8
#define HvLpEvent_Type_NumTypes 9
#define HvLpEvent_Rc_Good 0
#define HvLpEvent_Rc_BufferNotAvailable 1
#define HvLpEvent_Rc_Cancelled 2
#define HvLpEvent_Rc_GenericError 3
#define HvLpEvent_Rc_InvalidAddress 4
#define HvLpEvent_Rc_InvalidPartition 5
#define HvLpEvent_Rc_InvalidSize 6
#define HvLpEvent_Rc_InvalidSubtype 7
#define HvLpEvent_Rc_InvalidSubtypeData 8
#define HvLpEvent_Rc_InvalidType 9
#define HvLpEvent_Rc_PartitionDead 10
#define HvLpEvent_Rc_PathClosed 11
#define HvLpEvent_Rc_SubtypeError 12
#define HvLpEvent_Function_Ack 0
#define HvLpEvent_Function_Int 1
#define HvLpEvent_AckInd_NoAck 0
#define HvLpEvent_AckInd_DoAck 1
#define HvLpEvent_AckType_ImmediateAck 0
#define HvLpEvent_AckType_DeferredAck 1
#define HvLpDma_Direction_LocalToRemote 0
#define HvLpDma_Direction_RemoteToLocal 1
#define HvLpDma_AddressType_TceIndex 0
#define HvLpDma_AddressType_RealAddress 1
#define HvLpDma_Rc_Good 0
#define HvLpDma_Rc_Error 1
#define HvLpDma_Rc_PartitionDead 2
#define HvLpDma_Rc_PathClosed 3
#define HvLpDma_Rc_InvalidAddress 4
#define HvLpDma_Rc_InvalidLength 5
#endif // _HVLPEVENT_H
/*
* HvReleaseData.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=============================================================================
//
// This control block contains the critical information about the
// release so that it can be changed in the future (ie, the virtual
// address of the OS's NACA).
//
//-----------------------------------------------------------------------------
// Standard Includes
//-----------------------------------------------------------------------------
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _HVRELEASEDATA_H
#define _HVRELEASEDATA_H
//=============================================================================
//
// When we IPL a secondary partition, we will check if if the
// secondary xMinPlicVrmIndex > the primary xVrmIndex.
// If it is then this tells PLIC that this secondary is not
// supported running on this "old" of a level of PLIC.
//
// Likewise, we will compare the primary xMinSlicVrmIndex to
// the secondary xVrmIndex.
// If the primary xMinSlicVrmDelta > secondary xVrmDelta then we
// know that this PLIC does not support running an OS "that old".
//
//=============================================================================
struct HvReleaseData
{
u32 xDesc; // Descriptor "HvRD" ebcdic x00-x03
u16 xSize; // Size of this control block x04-x05
u16 xVpdAreasPtrOffset; // Offset in NACA of ItVpdAreas x06-x07
u32 xSlicNacaAddr64; // Virtual address of OS's NACA x08-x0F
struct Naca * xSlicNacaAddr; // Virtual address of OS's NACA x08-x0F
u32 xMsNucDataOffset; // Offset of Linux Mapping Data x10-x13
u32 xRsvd1; // Reserved x14-x17
u16 xTagsMode:1; // 0 == tags active, 1 == tags inactive
u16 xAddressSize:1; // 0 == 64-bit, 1 == 32-bit
u16 xNoSharedProcs:1; // 0 == shared procs, 1 == no shared
u16 xNoHMT:1; // 0 == allow HMT, 1 == no HMT
u16 xRsvd2:12; // Reserved x18-x19
u16 xVrmIndex; // VRM Index of OS image x1A-x1B
u16 xMinSupportedPlicVrmIndex;// Min PLIC level (soft) x1C-x1D
u16 xMinCompatablePlicVrmIndex;// Min PLIC levelP (hard) x1E-x1F
char xVrmName[12]; // Displayable name x20-x2B
char xRsvd3[20]; // Reserved x2C-x3F
};
#endif // _HVRELEASEDATA_H
/*
* HvTypes.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//===========================================================================
// Header File Id
// Name______________: HvTypes.H
//
// Description_______:
//
// General typedefs for the hypervisor.
//
// Declared Class(es):
//
//===========================================================================
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _HVTYPES_H
#define _HVTYPES_H
//-------------------------------------------------------------------
// Typedefs
//-------------------------------------------------------------------
typedef u8 HvLpIndex;
typedef u16 HvLpInstanceId;
typedef u64 HvLpTOD;
typedef u64 HvLpSystemSerialNum;
typedef u8 HvLpDeviceSerialNum[12];
typedef u16 HvLpSanHwSet;
typedef u16 HvLpBus;
typedef u16 HvLpBoard;
typedef u16 HvLpCard;
typedef u8 HvLpDeviceType[4];
typedef u8 HvLpDeviceModel[3];
typedef u64 HvIoToken;
typedef u8 HvLpName[8];
typedef u32 HvIoId;
typedef u64 HvRealMemoryIndex;
typedef u32 HvLpIndexMap; // Must hold HvMaxArchitectedLps bits!!!
typedef u16 HvLpVrmIndex;
typedef u32 HvXmGenerationId;
typedef u8 HvLpBusPool;
typedef u8 HvLpSharedPoolIndex;
typedef u16 HvLpSharedProcUnitsX100;
typedef u8 HvLpVirtualLanIndex;
typedef u16 HvLpVirtualLanIndexMap; // Must hold HvMaxArchitectedVirtualLans bits!!!
typedef u16 HvBusNumber; // Hypervisor Bus Number
typedef u8 HvSubBusNumber; // Hypervisor SubBus Number
typedef u8 HvAgentId; // Hypervisor DevFn
#define HVMAXARCHITECTEDLPS 32
#define HVCHUNKSIZE 256 * 1024
#define HVPAGESIZE 4 * 1024
#define HVLPMINMEGSPRIMARY 256
#define HVLPMINMEGSSECONDARY 64
#define HVCHUNKSPERMEG 4
#define HVPAGESPERMEG 256
#define HVPAGESPERCHUNK 64
static const HvLpIndexMap HvLpIndexMapDefault = (1 << (sizeof(HvLpIndexMap) * 8 - 1));
static const HvLpIndex HvHardcodedPrimaryLpIndex = 0;
static const HvLpIndex HvMaxArchitectedLps = HVMAXARCHITECTEDLPS;
static const HvLpVirtualLanIndex HvMaxArchitectedVirtualLans = 16;
static const HvLpSharedPoolIndex HvMaxArchitectedSharedPools = 16;
static const HvLpSharedPoolIndex HvMaxSupportedSharedPools = 1;
static const HvLpIndex HvMaxRuntimeLpsPreCondor = 12;
static const HvLpIndex HvMaxRuntimeLps = HVMAXARCHITECTEDLPS;
static const HvLpIndex HvLpIndexInvalid = 0xff;
static const u16 HvInvalidProcIndex = 0xffff;
static const u32 HvVirtualFlashSize = 0x200;
static const u32 HvMaxBusesPreCondor = 32;
static const u32 HvMaxBusesCondor = 256;
static const u32 HvMaxArchitectedBuses = 512;
static const HvLpBus HvCspBusNumber = 1;
static const u32 HvMaxSanHwSets = 16;
static const HvLpCard HvMaxSystemIops = 200;
static const HvLpCard HvMaxBusIops = 20;
static const u16 HvMaxUnitsPerIop = 100;
static const u64 HvPageSize = 4 * 1024;
static const u64 HvChunkSize = HVCHUNKSIZE;
static const u64 HvChunksPerMeg = HVCHUNKSPERMEG;
static const u64 HvPagesPerChunk = HVPAGESPERCHUNK;
static const u64 HvPagesPerMeg = HVPAGESPERMEG;
static const u64 HvLpMinMegsPrimary = HVLPMINMEGSPRIMARY;
static const u64 HvLpMinMegsSecondary = HVLPMINMEGSSECONDARY;
static const u64 HvLpMinChunksPrimary = HVLPMINMEGSPRIMARY * HVCHUNKSPERMEG;
static const u64 HvLpMinChunksSecondary = HVLPMINMEGSSECONDARY * HVCHUNKSPERMEG;
static const u64 HvLpMinPagesPrimary = HVLPMINMEGSPRIMARY * HVPAGESPERMEG;
static const u64 HvLpMinPagesSecondary = HVLPMINMEGSSECONDARY * HVPAGESPERMEG;
static const u8 HvLpMinProcs = 1;
static const u8 HvLpConfigMinInteract = 1;
static const u16 HvLpMinSharedProcUnitsX100 = 10;
static const u16 HvLpMaxSharedProcUnitsX100 = 100;
static const HvLpSharedPoolIndex HvLpSharedPoolIndexInvalid = 0xff;
//--------------------------------------------------------------------
// Enums for the sub-components under PLIC
// Used in HvCall and HvPrimaryCall
//--------------------------------------------------------------------
enum HvCallCompIds
{
HvCallCompId = 0,
HvCallCpuCtlsCompId = 1,
HvCallCfgCompId = 2,
HvCallEventCompId = 3,
HvCallHptCompId = 4,
HvCallPciCompId = 5,
HvCallSlmCompId = 6,
HvCallSmCompId = 7,
HvCallSpdCompId = 8,
HvCallXmCompId = 9,
HvCallRioCompId = 10,
HvCallRsvd3CompId = 11,
HvCallRsvd2CompId = 12,
HvCallRsvd1CompId = 13,
HvCallMaxCompId = 14,
HvPrimaryCallCompId = 0,
HvPrimaryCallCfgCompId = 1,
HvPrimaryCallPciCompId = 2,
HvPrimaryCallSmCompId = 3,
HvPrimaryCallSpdCompId = 4,
HvPrimaryCallXmCompId = 5,
HvPrimaryCallRioCompId = 6,
HvPrimaryCallRsvd7CompId = 7,
HvPrimaryCallRsvd6CompId = 8,
HvPrimaryCallRsvd5CompId = 9,
HvPrimaryCallRsvd4CompId = 10,
HvPrimaryCallRsvd3CompId = 11,
HvPrimaryCallRsvd2CompId = 12,
HvPrimaryCallRsvd1CompId = 13,
HvPrimaryCallMaxCompId = HvCallMaxCompId
};
struct HvLpBufferList {
u64 addr;
u64 len;
};
#endif // _HVTYPES_H
/*
* IoHriMainStore.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _IOHRIMAINSTORE_H
#define _IOHRIMAINSTORE_H
struct IoHriMainStoreSegment4 {
u8 msArea0Exists:1;
u8 msArea1Exists:1;
u8 msArea2Exists:1;
u8 msArea3Exists:1;
u8 reserved1:4;
u8 reserved2;
u8 msArea0Functional:1;
u8 msArea1Functional:1;
u8 msArea2Functional:1;
u8 msArea3Functional:1;
u8 reserved3:4;
u8 reserved4;
u32 totalMainStore;
u64 msArea0Ptr;
u64 msArea1Ptr;
u64 msArea2Ptr;
u64 msArea3Ptr;
u32 cardProductionLevel;
u32 msAdrHole;
u8 msArea0HasRiserVpd:1;
u8 msArea1HasRiserVpd:1;
u8 msArea2HasRiserVpd:1;
u8 msArea3HasRiserVpd:1;
u8 reserved5:4;
u8 reserved6;
u16 reserved7;
u8 reserved8[28];
u64 nonInterleavedBlocksStartAdr;
u64 nonInterleavedBlocksEndAdr;
};
#endif // _IOHRIMAINSTORE_H
/*
* IoHriProcessorVpd.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//===================================================================
//
// This struct maps Processor Vpd that is DMAd to SLIC by CSP
//
#ifndef _TYPES_H
#include <asm/types.h>
#endif
#ifndef _IOHRIPROCESSORVPD_H
#define _IOHRIPROCESSORVPD_H
struct IoHriProcessorVpd
{
u8 xFormat; // VPD format indicator x00-x00
u8 xProcStatus:8; // Processor State x01-x01
u8 xSecondaryThreadCount; // Secondary thread cnt x02-x02
u8 xSrcType:1; // Src Type x03-x03
u8 xSrcSoft:1; // Src stay soft ...
u8 xSrcParable:1; // Src parable ...
u8 xRsvd1:5; // Reserved ...
u16 xHvPhysicalProcIndex; // Hypervisor physical proc index04-x05
u16 xRsvd2; // Reserved x06-x07
u32 xHwNodeId; // Hardware node id x08-x0B
u32 xHwProcId; // Hardware processor id x0C-x0F
u32 xTypeNum; // Card Type/CCIN number x10-x13
u32 xModelNum; // Model/Feature number x14-x17
u64 xSerialNum; // Serial number x18-x1F
char xPartNum[12]; // Book Part or FPU number x20-x2B
char xMfgID[4]; // Manufacturing ID x2C-x2F
u32 xProcFreq; // Processor Frequency x30-x33
u32 xTimeBaseFreq; // Time Base Frequency x34-x37
u32 xChipEcLevel; // Chip EC Levels x38-x3B
u32 xProcIdReg; // PIR SPR value x3C-x3F
u32 xPVR; // PVR value x40-x43
u8 xRsvd3[12]; // Reserved x44-x4F
u32 xInstCacheSize; // Instruction cache size in KB x50-x53
u32 xInstBlockSize; // Instruction cache block size x54-x57
u32 xDataCacheOperandSize; // Data cache operand size x58-x5B
u32 xInstCacheOperandSize; // Inst cache operand size x5C-x5F
u32 xDataL1CacheSizeKB; // L1 data cache size in KB x60-x63
u32 xDataL1CacheLineSize; // L1 data cache block size x64-x67
u64 xRsvd4; // Reserved x68-x6F
u32 xDataL2CacheSizeKB; // L2 data cache size in KB x70-x73
u32 xDataL2CacheLineSize; // L2 data cache block size x74-x77
u64 xRsvd5; // Reserved x78-x7F
u32 xDataL3CacheSizeKB; // L3 data cache size in KB x80-x83
u32 xDataL3CacheLineSize; // L3 data cache block size x84-x87
u64 xRsvd6; // Reserved x88-x8F
u64 xFruLabel; // Card Location Label x90-x97
u8 xSlotsOnCard; // Slots on card (0=no slots) x98-x98
u8 xPartLocFlag; // Location flag (0-pluggable 1-imbedded) x99-x99
u16 xSlotMapIndex; // Index in slot map table x9A-x9B
u8 xSmartCardPortNo; // Smart card port number x9C-x9C
u8 xRsvd7; // Reserved x9D-x9D
u16 xFrameIdAndRackUnit; // Frame ID and rack unit adr x9E-x9F
u8 xRsvd8[24]; // Reserved xA0-xB7
char xProcSrc[72]; // CSP format SRC xB8-xFF
};
#endif // _IOHRIPROCESSORVPD_H
/*
* ItIplParmsReal.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//==============================================================================
//
// This struct maps the IPL Parameters DMA'd from the SP.
//
// Warning:
// This data must map in exactly 64 bytes and match the architecture for
// the IPL parms
//
//=============================================================================
//-------------------------------------------------------------------
// Standard Includes
//-------------------------------------------------------------------
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _ITIPLPARMSREAL_H
#define _ITIPLPARMSREAL_H
struct ItIplParmsReal
{
u8 xFormat; // Defines format of IplParms x00-x00
u8 xRsvd01:6; // Reserved x01-x01
u8 xAlternateSearch:1; // Alternate search indicator ...
u8 xUaSupplied:1; // UA Supplied on programmed IPL ...
u8 xLsUaFormat; // Format byte for UA x02-x02
u8 xRsvd02; // Reserved x03-x03
u32 xLsUa; // LS UA x04-x07
u32 xUnusedLsLid; // First OS LID to load x08-x0B
u16 xLsBusNumber; // LS Bus Number x0C-x0D
u8 xLsCardAdr; // LS Card Address x0E-x0E
u8 xLsBoardAdr; // LS Board Address x0F-x0F
u32 xRsvd03; // Reserved x10-x13
u8 xSpcnPresent:1; // SPCN present x14-x14
u8 xCpmPresent:1; // CPM present ...
u8 xRsvd04:6; // Reserved ...
u8 xRsvd05:4; // Reserved x15-x15
u8 xKeyLock:4; // Keylock setting ...
u8 xRsvd06:6; // Reserved x16-x16
u8 xIplMode:2; // Ipl mode (A|B|C|D) ...
u8 xHwIplType; // Fast v slow v slow EC HW IPL x17-x17
u16 xCpmEnabledIpl:1; // CPM in effect when IPL initiated x18-x19
u16 xPowerOnResetIpl:1; // Indicate POR condition ...
u16 xMainStorePreserved:1; // Main Storage is preserved ...
u16 xRsvd07:13; // Reserved ...
u16 xIplSource:16; // Ipl source x1A-x1B
u8 xIplReason:8; // Reason for this IPL x1C-x1C
u8 xRsvd08; // Reserved x1D-x1D
u16 xRsvd09; // Reserved x1E-x1F
u16 xSysBoxType; // System Box Type x20-x21
u16 xSysProcType; // System Processor Type x22-x23
u32 xRsvd10; // Reserved x24-x27
u64 xRsvd11; // Reserved x28-x2F
u64 xRsvd12; // Reserved x30-x37
u64 xRsvd13; // Reserved x38-x3F
};
#endif // _ITIPLPARMSREAL_H
/*
* ItLpNaca.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=============================================================================
//
// This control block contains the data that is shared between the
// hypervisor (PLIC) and the OS.
//
//=============================================================================
#ifndef _ITLPNACA_H
#define _ITLPNACA_H
struct ItLpNaca
{
//=============================================================================
// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data
//=============================================================================
u32 xDesc; // Eye catcher x00-x03
u16 xSize; // Size of this class x04-x05
u16 xIntHdlrOffset; // Offset to IntHdlr array x06-x07
u8 xMaxIntHdlrEntries; // Number of entries in array x08-x08
u8 xPrimaryLpIndex; // LP Index of Primary x09-x09
u8 xServiceLpIndex; // LP Ind of Service Focal Pointx0A-x0A
u8 xLpIndex; // LP Index x0B-x0B
u16 xMaxLpQueues; // Number of allocated queues x0C-x0D
u16 xLpQueueOffset; // Offset to start of LP queues x0E-x0F
u8 xPirEnvironMode:8; // Piranha or hardware x10-x10
u8 xPirConsoleMode:8; // Piranha console indicator x11-x11
u8 xPirDasdMode:8; // Piranha dasd indicator x12-x12
u8 xRsvd1_0[5]; // Reserved for Piranha related x13-x17
u8 xLparInstalled:1; // Is LPAR installed on system x18-x1F
u8 xSysPartitioned:1; // Is the system partitioned ...
u8 xHwSyncedTBs:1; // Hardware synced TBs ...
u8 xIntProcUtilHmt:1; // Utilize HMT for interrupts ...
u8 xRsvd1_1:4; // Reserved ...
u8 xSpVpdFormat:8; // VPD areas are in CSP format ...
u8 xIntProcRatio:8; // Ratio of int procs to procs ...
u8 xRsvd1_2[5]; // Reserved ...
u16 xRsvd1_3; // Reserved x20-x21
u16 xPlicVrmIndex; // VRM index of PLIC x22-x23
u16 xMinSupportedSlicVrmInd;// Min supported OS VRM index x24-x25
u16 xMinCompatableSlicVrmInd;// Min compatable OS VRM index x26-x27
u64 xLoadAreaAddr; // ER address of load area x28-x2F
u32 xLoadAreaChunks; // Chunks for the load area x30-x33
u32 xRsvd1_4; // x34-x37
u8 xRsvd1_5[72]; // x38-x7F
//=============================================================================
// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data
//=============================================================================
u8 xRsvd2_0[128]; // Reserved x00-x7F
//=============================================================================
// CACHE_LINE_3-6 0x0100 - 0x02FF Contains LP Queue indicators
// NB: Padding required to keep xInterrruptHdlr at x300 which is required
// for v4r4 PLIC.
//=============================================================================
u8 xOldLpQueue[128]; // LP Queue needed for v4r4 100-17F
u8 xRsvd3_0[384]; // Reserved 180-2FF
//=============================================================================
// CACHE_LINE_7-8 0x0300 - 0x03FF Contains the address of the OS interrupt
// handlers
//=============================================================================
u64 xInterruptHdlr[32]; // Interrupt handlers 300-x3FF
};
//=============================================================================
#endif // _ITLPNACA_H
/*
* ItLpPaca.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=============================================================================
//
// This control block contains the data that is shared between the
// hypervisor (PLIC) and the OS.
//
//
//----------------------------------------------------------------------------
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _ITLPPACA_H
#define _ITLPPACA_H
struct ItLpPaca
{
//=============================================================================
// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data
// NOTE: The xDynXyz fields are fields that will be dynamically changed by
// PLIC when preparing to bring a processor online or when dispatching a
// virtual processor!
//=============================================================================
u32 xDesc; // Eye catcher 0xD397D781 x00-x03
u16 xSize; // Size of this struct x04-x05
u16 xRsvd1_0; // Reserved x06-x07
u16 xRsvd1_1:14; // Reserved x08-x09
u8 xSharedProc:1; // Shared processor indicator ...
u8 xSecondaryThread:1; // Secondary thread indicator ...
volatile u8 xDynProcStatus:8; // Dynamic Status of this proc x0A-x0A
u8 xSecondaryThreadCnt; // Secondary thread count x0B-x0B
volatile u16 xDynHvPhysicalProcIndex;// Dynamic HV Physical Proc Index0C-x0D
volatile u16 xDynHvLogicalProcIndex;// Dynamic HV Logical Proc Indexx0E-x0F
u32 xDecrVal; // Value for Decr programming x10-x13
u32 xPMCVal; // Value for PMC regs x14-x17
volatile u32 xDynHwNodeId; // Dynamic Hardware Node id x18-x1B
volatile u32 xDynHwProcId; // Dynamic Hardware Proc Id x1C-x1F
volatile u32 xDynPIR; // Dynamic ProcIdReg value x20-x23
u32 xDseiData; // DSEI data x24-x27
u64 xSPRG3; // SPRG3 value x28-x2F
u8 xRsvd1_3[80]; // Reserved x30-x7F
//=============================================================================
// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data
//=============================================================================
// This Dword contains a byte for each type of interrupt that can occur.
// The IPI is a count while the others are just a binary 1 or 0.
u16 xRsvd; // Reserved - cleared by #mpasmbl
u8 xXirrInt; // Indicates xXirrValue is valid or Immed IO
u8 xIpiCnt; // IPI Count
u8 xDecrInt; // DECR interrupt occurred
u8 xPdcInt; // PDC interrupt occurred
u8 xQuantumInt; // Interrupt quantum reached
u8 xOldPlicDeferredExtInt; // Old PLIC has deferred XIRR pending
u64 xPlicDeferIntsArea;
// Used to pass the real SRR0/1 from PLIC to the OS as well as to
// pass the target SRR0/1 from the OS to PLIC on a SetAsrAndRfid.
u64 xSavedSrr0; // Saved SRR0 x10-x17
u64 xSavedSrr1; // Saved SRR1 x18-x1F
// Used to pass parms from the OS to PLIC for SetAsrAndRfid
u64 xSavedGpr3; // Saved GPR3 x20-x27
u64 xSavedGpr4; // Saved GPR4 x28-x2F
u64 xSavedGpr5; // Saved GPR5 x30-x37
u8 xRsvd2_1; // Reserved x38-x38
u8 xCpuCtlsTaskAttributes; // Task attributes for cpuctls x39-x39
u8 xFPRegsInUse; // FP regs in use x3A-x3A
u8 xPMCRegsInUse; // PMC regs in use x3B-x3B
volatile u32 xSavedDecr; // Saved Decr Value x3C-x3F
volatile u64 xEmulatedTimeBase; // Emulated TB for this thread x40-x47
volatile u64 xCurPLICLatency; // Unaccounted PLIC latency x48-x4F
u64 xTotPLICLatency; // Accumulated PLIC latency x50-x57
u64 xWaitStateCycles; // Wait cycles for this proc x58-x5F
u64 xEndOfQuantum; // TB at end of quantum x60-x67
u64 xPDCSavedSPRG1; // Saved SPRG1 for PMC int x68-x6F
u64 xPDCSavedSRR0; // Saved SRR0 for PMC int x70-x77
volatile u32 xVirtualDecr; // Virtual DECR for shared procsx78-x7B
u32 xRsvd2_2; // Reserved x7C-x7F
//=============================================================================
// CACHE_LINE_3 0x0100 - 0x007F: This line is shared with other processors
//=============================================================================
// This is the xYieldCount. An "odd" value (low bit on) means that
// the processor is yielded (either because of an OS yield or a PLIC
// preempt). An even value implies that the processor is currently
// executing.
// NOTE: This value will ALWAYS be zero for dedicated processors and
// will NEVER be zero for shared processors (ie, initialized to a 1).
volatile u32 xYieldCount; // PLIC increments each dispatchx00-x03
u8 xRsvd3_0[124]; // Reserved x04-x7F
//=============================================================================
// CACHE_LINE_4-5 0x0100 - 0x01FF Contains PMC interrupt data
//=============================================================================
u8 xPmcSaveArea[256]; // PMC interrupt Area x00-xFF
};
#endif // _ITLPPACA_H
/*
* ItLpQueue.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=============================================================================
//
// This control block defines the simple LP queue structure that is
// shared between the hypervisor (PLIC) and the OS in order to send
// events to an LP.
//
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#include <asm/ptrace.h>
struct HvLpEvent;
#ifndef _ITLPQUEUE_H
#define _ITLPQUEUE_H
#define ITMaxLpQueues 8
#define NotUsed 0 // Queue will not be used by PLIC
#define DedicatedIo 1 // Queue dedicated to IO processor specified
#define DedicatedLp 2 // Queue dedicated to LP specified
#define Shared 3 // Queue shared for both IO and LP
#define LpEventStackSize 4096
#define LpEventMaxSize 256
#define LpEventAlign 64
struct ItLpQueue
{
//
// The xSlicCurEventPtr is the pointer to the next event stack entry that will
// become valid. The OS must peek at this entry to determine if it is valid.
// PLIC will set the valid indicator as the very last store into that entry.
//
// When the OS has completed processing of the event then it will mark the event
// as invalid so that PLIC knows it can store into that event location again.
//
// If the event stack fills and there are overflow events, then PLIC will set
// the xPlicOverflowIntPending flag in which case the OS will have to fetch the
// additional LP events once they have drained the event stack.
//
// The first 16-bytes are known by both the OS and PLIC. The remainder of the
// cache line is for use by the OS.
//
//=============================================================================
u8 xPlicOverflowIntPending; // 0x00 Overflow events are pending
u8 xPlicStatus; // 0x01 DedicatedIo or DedicatedLp or NotUsed
u16 xSlicLogicalProcIndex; // 0x02 Logical Proc Index for correlation
u8 xPlicRsvd[12]; // 0x04
u32 xHSlicCurEventPtr; // 0x10 High 32 bits of ptr
char* xSlicCurEventPtr; // 0x14 Low 32 bits of ptr
u32 xHSlicLastValidEventPtr; // 0x18 High 32 bits of ptr
char* xSlicLastValidEventPtr; // 0x1C Low 32 bits of ptr
u32 xHSlicEventStackPtr; // 0x20 High 32 bits of ptr
char* xSlicEventStackPtr; // 0x24 Low 32 bits of ptr
u8 xIndex; // 0x28 unique sequential index.
u8 xSlicRsvd[3]; // 0x29-2B
u32 xInUseWord; // 0x2C
u64 xLpIntCount; // 0x30 Total Lp Int msgs processed
u64 xLpIntCountByType[9]; // 0x38-0x7F Event counts by type
};
extern struct ItLpQueue xItLpQueue;
extern struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * );
extern int ItLpQueue_isLpIntPending( struct ItLpQueue * );
extern unsigned ItLpQueue_process( struct ItLpQueue *, struct pt_regs * );
extern void ItLpQueue_clearValid( struct HvLpEvent * );
//=============================================================================
#endif // _ITLPQUEUE_H
/*
* ItLpRegSave.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=====================================================================================
//
// This control block contains the data that is shared between PLIC
// and the OS
//
//
#ifndef _ITLPREGSAVE_H
#define _ITLPREGSAVE_H
struct ItLpRegSave
{
u32 xDesc; // Eye catcher "LpRS" ebcdic 000-003
u16 xSize; // Size of this class 004-005
u8 xInUse; // Area is live 006-007
u8 xRsvd1[9]; // Reserved 007-00F
u8 xFixedRegSave[352]; // Fixed Register Save Area 010-16F
u32 xCTRL; // Control Register 170-173
u32 xDEC; // Decrementer 174-177
u32 xFPSCR; // FP Status and Control Reg 178-17B
u32 xPVR; // Processor Version Number 17C-17F
u64 xMMCR0; // Monitor Mode Control Reg 0 180-187
u32 xPMC1; // Perf Monitor Counter 1 188-18B
u32 xPMC2; // Perf Monitor Counter 2 18C-18F
u32 xPMC3; // Perf Monitor Counter 3 190-193
u32 xPMC4; // Perf Monitor Counter 4 194-197
u32 xPIR; // Processor ID Reg 198-19B
u32 xMMCR1; // Monitor Mode Control Reg 1 19C-19F
u32 xMMCRA; // Monitor Mode Control Reg A 1A0-1A3
u32 xPMC5; // Perf Monitor Counter 5 1A4-1A7
u32 xPMC6; // Perf Monitor Counter 6 1A8-1AB
u32 xPMC7; // Perf Monitor Counter 7 1AC-1AF
u32 xPMC8; // Perf Monitor Counter 8 1B0-1B3
u32 xTSC; // Thread Switch Control 1B4-1B7
u32 xTST; // Thread Switch Timeout 1B8-1BB
u32 xRsvd; // Reserved 1BC-1BF
u64 xACCR; // Address Compare Control Reg 1C0-1C7
u64 xIMR; // Instruction Match Register 1C8-1CF
u64 xSDR1; // Storage Description Reg 1 1D0-1D7
u64 xSPRG0; // Special Purpose Reg General0 1D8-1DF
u64 xSPRG1; // Special Purpose Reg General1 1E0-1E7
u64 xSPRG2; // Special Purpose Reg General2 1E8-1EF
u64 xSPRG3; // Special Purpose Reg General3 1F0-1F7
u64 xTB; // Time Base Register 1F8-1FF
u64 xFPR[32]; // Floating Point Registers 200-2FF
u64 xMSR; // Machine State Register 300-307
u64 xNIA; // Next Instruction Address 308-30F
u64 xDABR; // Data Address Breakpoint Reg 310-317
u64 xIABR; // Inst Address Breakpoint Reg 318-31F
u64 xHID0; // HW Implementation Dependent0 320-327
u64 xHID4; // HW Implementation Dependent4 328-32F
u64 xSCOMd; // SCON Data Reg (SPRG4) 330-337
u64 xSCOMc; // SCON Command Reg (SPRG5) 338-33F
u64 xSDAR; // Sample Data Address Register 340-347
u64 xSIAR; // Sample Inst Address Register 348-34F
u8 xRsvd3[176]; // Reserved 350-3FF
};
#endif // _ITLPREGSAVE_H
/*
* ItSpCommArea.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ITSPCOMMAREA_H
#define _ITSPCOMMAREA_H
struct SpCommArea
{
u32 xDesc; // Descriptor (only in new formats) 000-003
u8 xFormat; // Format (only in new formats) 004-004
u8 xRsvd1[11]; // Reserved 005-00F
u64 xRawTbAtIplStart; // Raw HW TB value when IPL is started 010-017
u64 xRawTodAtIplStart; // Raw HW TOD value when IPL is started 018-01F
u64 xBcdTimeAtIplStart; // BCD time when IPL is started 020-027
u64 xBcdTimeAtOsStart; // BCD time when OS passed control 028-02F
u8 xRsvd2[80]; // Reserved 030-07F
};
extern struct SpCommArea xSpCommArea;
#endif /* _ITSPCOMMAREA_H */
/*
* ItVpdAreas.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//=====================================================================================
//
// This file defines the address and length of all of the VPD area passed to
// the OS from PLIC (most of which start from the SP).
//
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _ITVPDAREAS_H
#define _ITVPDAREAS_H
// VPD Entry index is carved in stone - cannot be changed (easily).
#define ItVpdCecVpd 0
#define ItVpdDynamicSpace 1
#define ItVpdExtVpd 2
#define ItVpdExtVpdOnPanel 3
#define ItVpdFirstPaca 4
#define ItVpdIoVpd 5
#define ItVpdIplParms 6
#define ItVpdMsVpd 7
#define ItVpdPanelVpd 8
#define ItVpdLpNaca 9
#define ItVpdBackplaneAndMaybeClockCardVpd 10
#define ItVpdRecoveryLogBuffer 11
#define ItVpdSpCommArea 12
#define ItVpdSpLogBuffer 13
#define ItVpdSpLogBufferSave 14
#define ItVpdSpCardVpd 15
#define ItVpdFirstProcVpd 16
#define ItVpdApModelVpd 17
#define ItVpdClockCardVpd 18
#define ItVpdBusExtCardVpd 19
#define ItVpdProcCapacityVpd 20
#define ItVpdInteractiveCapacityVpd 21
#define ItVpdFirstSlotLabel 22
#define ItVpdFirstLpQueue 23
#define ItVpdFirstL3CacheVpd 24
#define ItVpdFirstProcFruVpd 25
#define ItVpdMaxEntries 26
#define ItDmaMaxEntries 10
#define ItVpdAreasMaxSlotLabels 192
struct SlicVpdAdrs {
u32 pad1;
void * vpdAddr;
};
struct ItVpdAreas
{
u32 xSlicDesc; // Descriptor 000-003
u16 xSlicSize; // Size of this control block 004-005
u16 xPlicAdjustVpdLens:1; // Flag to indicate new interface 006-007
u16 xRsvd1:15; // Reserved bits ...
u16 xSlicVpdEntries; // Number of VPD entries 008-009
u16 xSlicDmaEntries; // Number of DMA entries 00A-00B
u16 xSlicMaxLogicalProcs; // Maximum logical processors 00C-00D
u16 xSlicMaxPhysicalProcs; // Maximum physical processors 00E-00F
u16 xSlicDmaToksOffset; // Offset into this of array 010-011
u16 xSlicVpdAdrsOffset; // Offset into this of array 012-013
u16 xSlicDmaLensOffset; // Offset into this of array 014-015
u16 xSlicVpdLensOffset; // Offset into this of array 016-017
u16 xSlicMaxSlotLabels; // Maximum number of slot labels 018-019
u16 xSlicMaxLpQueues; // Maximum number of LP Queues 01A-01B
u8 xRsvd2[4]; // Reserved 01C-01F
u64 xRsvd3[12]; // Reserved 020-07F
u32 xPlicDmaLens[ItDmaMaxEntries];// Array of DMA lengths 080-0A7
u32 xPlicDmaToks[ItDmaMaxEntries];// Array of DMA tokens 0A8-0CF
u32 xSlicVpdLens[ItVpdMaxEntries];// Array of VPD lengths 0D0-12F
struct SlicVpdAdrs xSlicVpdAdrs[ItVpdMaxEntries];// Array of VPD buffers 130-1EF
};
#endif // _ITVPDAREAS_H
/*
* LparData.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _LPARDATA_H
#define _LPARDATA_H
#include <asm/page.h>
extern u32 msChunks[];
#define phys_to_absolute( x ) (((u64)msChunks[((x)>>18)])<<18)+((u64)((x)&0x3ffff))
#define physRpn_to_absRpn( x ) (((u64)msChunks[((x)>>6)])<<6)+((u64)((x)&0x3f))
#define virt_to_absolute( x ) (((u64)msChunks[(((x)-KERNELBASE)>>18)])<<18)+((u64)((x)&0x3ffff))
extern u64 virt_to_absolute_outline(u32 address);
#include <asm/iSeries/Naca.h>
#include <asm/iSeries/ItLpNaca.h>
#include <asm/iSeries/ItLpPaca.h>
#include <asm/iSeries/ItLpRegSave.h>
#include <asm/iSeries/Paca.h>
#include <asm/iSeries/HvReleaseData.h>
#include <asm/iSeries/LparMap.h>
#include <asm/iSeries/ItVpdAreas.h>
#include <asm/iSeries/ItIplParmsReal.h>
#include <asm/iSeries/ItLpQueue.h>
#include <asm/iSeries/IoHriProcessorVpd.h>
#include <asm/page.h>
extern struct LparMap xLparMap;
extern struct Naca xNaca;
extern struct Paca xPaca[];
extern struct HvReleaseData hvReleaseData;
extern struct ItLpNaca itLpNaca;
extern struct ItIplParmsReal xItIplParmsReal;
extern struct IoHriProcessorVpd xIoHriProcessorVpd[];
extern struct ItLpQueue xItLpQueue;
extern struct ItVpdAreas itVpdAreas;
extern u64 xMsVpd[];
extern u32 msChunks[];
extern u32 totalLpChunks;
extern unsigned maxPacas;
/*
#define phys_to_absolute( x ) (((u64)msChunks[((x)>>18)])<<18)+((u64)((x)&0x3ffff))
#define physRpn_to_absRpn( x ) (((u64)msChunks[((x)>>6)])<<6)+((u64)((x)&0x3f))
#define virt_to_absolute( x ) (((u64)msChunks[(((x)-KERNELBASE)>>18)])<<18)+((u64)((x)&0x3ffff))
*/
#endif /* _LPAR_DATA_H */
/*
* LparMap.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _LPARMAP_H
#define _LPARMAP_H
/* The iSeries hypervisor will set up mapping for one or more
* ESID/VSID pairs (in SLB/segment registers) and will set up
* mappings of one or more ranges of pages to VAs.
* We will have the hypervisor set up the ESID->VSID mapping
* for the four kernel segments (C-F). With shared processors,
* the hypervisor will clear all segment registers and reload
* these four whenever the processor is switched from one
* partition to another.
*/
/* The Vsid and Esid identified below will be used by the hypervisor
* to set up a memory mapping for part of the load area before giving
* control to the Linux kernel. The load area is 64 MB, but this must
* not attempt to map the whole load area. The Hashed Page Table may
* need to be located within the load area (if the total partition size
* is 64 MB), but cannot be mapped. Typically, this should specify
* to map half (32 MB) of the load area.
*
* The hypervisor will set up page table entries for the number of
* pages specified.
*
* In 32-bit mode, the hypervisor will load all four of the
* segment registers (identified by the low-order four bits of the
* Esid field. In 64-bit mode, the hypervisor will load one SLB
* entry to map the Esid to the Vsid.
*/
// Hypervisor initially maps 32MB of the load area
#define HvPagesToMap 8192
struct LparMap
{
u64 xNumberEsids; // Number of ESID/VSID pairs (4)
u64 xNumberRanges; // Number of VA ranges to map (1)
u64 xSegmentTableOffs; // Page number within load area of seg table (0)
u64 xRsvd[5]; // Reserved (0)
u64 xKernelEsidC; // Esid used to map kernel load (0x0C)
u64 xKernelVsidC; // Vsid used to map kernel load (0x0C)
u64 xKernelEsidD; // Esid used to map kernel load (0x0D)
u64 xKernelVsidD; // Vsid used to map kernel load (0x0D)
u64 xKernelEsidE; // Esid used to map kernel load (0x0E)
u64 xKernelVsidE; // Vsid used to map kernel load (0x0E)
u64 xKernelEsidF; // Esid used to map kernel load (0x0F)
u64 xKernelVsidF; // Vsid used to map kernel load (0x0F)
u64 xPages; // Number of pages to be mapped (8192)
u64 xOffset; // Offset from start of load area (0)
u64 xVPN; // Virtual Page Number (0x00000000000C0000)
};
#endif /* _LPARMAP_H */
/*
* Naca.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
#ifndef _NACA_H
#define _NACA_H
struct Naca
{
u32 xItVpdAreas64; // Address of ItVpdAreas
void * xItVpdAreas;
u32 xRamDisk64; // Address of initial Ramdisk
u32 xRamDisk;
u32 xRamDiskSize64; // Size of initial Ramdisk
u32 xRamDiskSize; // in pages
};
#endif /* _NACA_H */
/*
* Paca.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//============================================================================
//
// This control block defines the OS's PACA which defines the processor
// specific data for each logical processor on the system.
// There are some pointers defined that are utilized by PLIC.
//
#ifndef _PPC_TYPES_H
#include <asm/types.h>
#endif
//-----------------------------------------------------------------------------
// Other Includes
//-----------------------------------------------------------------------------
#ifndef _ITLPPACA_H
#include <asm/iSeries/ItLpPaca.h>
#endif
#ifndef _ITLPREGSAVE_H
#include <asm/iSeries/ItLpRegSave.h>
#endif
#ifndef _ITLPQUEUE_H
#include <asm/iSeries/ItLpQueue.h>
#endif
#ifndef _PACA_H
#define _PACA_H
/*
* The bolted stack structure is at the head of each bolted stack
* and is simply a singly linked list
*/
//============================================================================
//
// Defines the layout of the Paca.
//
// This structure is not directly accessed by PLIC or the SP except
// for the first two pointers that point to the ItLpPaca area and the
// ItLpRegSave area for this processor.
//
//============================================================================
struct Paca
{
//===========================================================================
// Following two fields are read by PLIC to find the LpPaca and LpRegSave area
//===========================================================================
u32 pad1; // Pointer to LpPaca for proc
struct ItLpPaca * xLpPacaPtr; // Pointer to LpPaca for proc
u32 pad2; // Pointer to LpRegSave for proc
struct ItLpRegSave * xLpRegSavePtr; // Pointer to LpRegSave for proc
u64 xR21; // Savearea for GPR21
u64 xR22; // Savearea for GPR22
u64 xKsave; // Saved Kernel stack addr or zero
u16 xPacaIndex; // Index into Paca array of this
// Paca. This is processor number
u8 xProcStart; // At startup, processor spins until
// xProcStart becomes non-zero.
u8 xProcEnabled; // 1 - soft enabled, 0 - soft disabled
u32 xrsvd2; // was bolted stacks
u32 xSavedMsr; // old msr saved here by HvCall
// and flush_hash_page.
// HvCall uses 64-bit registers
// so it must disable external
// interrupts to avoid the high
// half of the regs getting lost
// It can't stack a frame because
// some of the callers can't
// tolerate hpt faults (which might
// occur on the stack)
u32 xSavedLr; // link register saved here by
// flush_hash_page
u8 xContextOverflow; // 1 - context overflow - use temporary
// context = processor# + 1
u8 rsvd4;
u16 rsvd5;
u32 xSRR0; // Used as bolted copies of stack fields
u32 xSRR1;
u32 xGPR0;
u32 xGPR2;
u32 default_decr; // Default decrementer value
u32 ext_ints; // ext ints processed
u32 rsvd6;
u64 rsvd1[5]; // Rest of cache line reserved
//===========================================================================
// CACHE_LINE_2-3 0x0080 - 0x0180
//===========================================================================
struct ItLpQueue * lpQueuePtr; // LpQueue handled by this processor
u32 breakpoint_loop; // Loop until this field is set
// non-zero by user. Then set it
// back to zero before continuing
u64 debug_regs; // Pointer to pt_regs at breakpoint
u64 rsvd3[30]; // To be used by Linux
//===========================================================================
// CACHE_LINE_4-8 0x0180 - 0x03FF Contains ItLpPaca
//===========================================================================
struct ItLpPaca xLpPaca; // Space for ItLpPaca
//===========================================================================
// CACHE_LINE_9-16 0x0400 - 0x07FF Contains ItLpRegSave
//===========================================================================
struct ItLpRegSave xRegSav; // Register save for proc
};
#endif /* _PACA_H */
#ifndef __XMPCILPEVENT_H__
#define __XMPCILPEVENT_H__
#ifdef __cplusplus
extern "C" {
#endif
int XmPciLpEvent_init(void);
void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq);
#ifdef __cplusplus
}
#endif
#endif /* __XMPCILPEVENT_H__ */
#ifndef _ISERIES_FLIGHTRECORDER_H
#define _ISERIES_FLIGHTRECORDER_H
/************************************************************************/
/* File iSeries_FlightRecorder.h created by Allan Trautman Jan 22 2001. */
/************************************************************************/
/* This code supports the pci interface on the IBM iSeries systems. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Jan 22, 2001 */
/* Added Time stamp methods. Apr 12, 2001 */
/* End Change Activity */
/************************************************************************/
/* This is a generic Flight Recorder, simply stuffs line entries into a */
/* buffer for debug purposes. */
/* */
/* To use, */
/* 1. Create one, make it global so it isn't on the stack. */
/* FlightRecorder PciFlightRecorder; */
/* */
/* 2. Optionally create a pointer to it, just makes it easier to use. */
/* FlightRecorder* PciFr = &PciFlightRecorder; */
/* */
/* 3. Initialize with you signature. */
/* iSeries_Fr_Initialize(PciFr, "Pci Flight Recorder"); */
/* */
/* 4. Log entries. */
/* PciFr->logEntry(PciFr,"In Main"); */
/* */
/* 5. Later, you can find the Flight Recorder by looking in the */
/* System.map */
/************************************************************************/
struct iSeries_FlightRecorder; /* Forward declares */
struct rtc_time;
void logEntry(struct iSeries_FlightRecorder*, char* Text);
void logTime( struct iSeries_FlightRecorder*, char* Text);
void logDate( struct iSeries_FlightRecorder*, char* Text);
#define FlightRecorderSize 4096
/************************************************************************/
/* Generic Flight Recorder Structure */
/************************************************************************/
struct iSeries_FlightRecorder { /* Structure Defination */
char Signature[16]; /* Eye Catcher */
char* StartingPointer; /* Buffer Starting Address */
char* CurrentPointer; /* Next Entry Address */
int WrapCount; /* Number of Buffer Wraps */
void (*logEntry)(struct iSeries_FlightRecorder*,char*);
void (*logTime) (struct iSeries_FlightRecorder*,char*);
void (*logDate) (struct iSeries_FlightRecorder*,char*);
char Buffer[FlightRecorderSize];
};
typedef struct iSeries_FlightRecorder FlightRecorder; /* Short Name */
extern void iSeries_Fr_Initialize(FlightRecorder*, char* Signature);
/************************************************************************/
/* extern void iSeries_LogFr_Entry( FlightRecorder*, char* Text); */
/* extern void iSeries_LogFr_Date( FlightRecorder*, char* Text); */
/* extern void iSeries_LogFr_Time( FlightRecorder*, char* Text); */
/************************************************************************/
/* PCI Flight Recorder Helpers */
/************************************************************************/
extern FlightRecorder* PciFr; /* Ptr to Pci Fr */
extern char* PciFrBuffer; /* Ptr to Fr Work Buffer */
#define ISERIES_PCI_FR(buffer) PciFr->logEntry(PciFr,buffer);
#define ISERIES_PCI_FR_TIME(buffer) PciFr->logTime(PciFr,buffer);
#define ISERIES_PCI_FR_DATE(buffer) PciFr->logDate(PciFr,buffer);
#endif /* _ISERIES_FLIGHTRECORDER_H */
#ifndef _ISERIES_VPDINFO_H
#define _ISERIES_VPDINFO_H
/************************************************************************/
/* File iSeries_VpdInfo.h created by Allan Trautman Feb 08 2001. */
/************************************************************************/
/* This code supports the location data fon on the IBM iSeries systems. */
/* Copyright (C) 20yy <Allan H Trautman> <IBM Corp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created, Feg 8, 2001 */
/* Reformated for Card, March 8, 2001 */
/* End Change Activity */
/************************************************************************/
struct pci_dev; /* Forward Declare */
/************************************************************************/
/* Location Data extracted from the VPD list and device info. */
/************************************************************************/
struct LocationDataStruct { /* Location data structure for device */
u16 Bus; /* iSeries Bus Number 0x00*/
u16 Board; /* iSeries Board 0x02*/
u8 FrameId; /* iSeries spcn Frame Id 0x04*/
u8 PhbId; /* iSeries Phb Location 0x05*/
u16 Card; /* iSeries Card Slot 0x06*/
char CardLocation[4]; /* Char format of planar vpd 0x08*/
u8 AgentId; /* iSeries AgentId 0x0C*/
u8 SecondaryAgentId; /* iSeries Secondary Agent Id 0x0D*/
u8 LinuxBus; /* Linux Bus Number 0x0E*/
u8 LinuxDevFn; /* Linux Device Function 0x0F*/
};
typedef struct LocationDataStruct LocationData;
#define LOCATION_DATA_SIZE 16
/************************************************************************/
/* Protypes */
/************************************************************************/
extern LocationData* iSeries_GetLocationData(struct pci_dev* PciDev);
extern int iSeries_Device_Information(struct pci_dev*,char*, int);
#endif /* _ISERIES_VPDINFO_H */
/*
* iSeries_dma.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ISERIES_DMA_H
#define _ISERIES_DMA_H
#include <asm/types.h>
#ifndef __LINUX_SPINLOCK_H
#include <linux/spinlock.h>
#endif
// NUM_TCE_LEVELS defines the largest contiguous block
// of dma (tce) space we can get. NUM_TCE_LEVELS = 10
// allows up to 2**9 pages (512 * 4096) = 2 MB
#define NUM_TCE_LEVELS 10
#define NO_TCE ((dma_addr_t)-1)
// Tces come in two formats, one for the virtual bus and a different
// format for PCI
#define TCE_VB 0
#define TCE_PCI 1
union Tce {
u64 wholeTce;
struct {
u64 cacheBits :6; /* Cache hash bits - not used */
u64 rsvd :6;
u64 rpn :40; /* Absolute page number */
u64 valid :1; /* Tce is valid (vb only) */
u64 allIo :1; /* Tce is valid for all lps (vb only) */
u64 lpIndex :8; /* LpIndex for user of TCE (vb only) */
u64 pciWrite :1; /* Write allowed (pci only) */
u64 readWrite :1; /* Read allowed (pci), Write allowed
(vb) */
} tceBits;
};
struct Bitmap {
unsigned long numBits;
unsigned long numBytes;
unsigned char * map;
};
struct MultiLevelBitmap {
unsigned long maxLevel;
struct Bitmap level[NUM_TCE_LEVELS];
};
struct TceTable {
u64 busNumber;
u64 size;
u64 startOffset;
u64 index;
spinlock_t lock;
struct MultiLevelBitmap mlbm;
};
struct HvTceTableManagerCB {
u64 busNumber; /* Bus number for this tce table */
u64 start; /* Will be NULL for secondary */
u64 totalSize; /* Size (in pages) of whole table */
u64 startOffset; /* Index into real tce table of the
start of our section */
u64 size; /* Size (in pages) of our section */
u64 index; /* Index of this tce table (token?) */
u16 maxTceTableIndex; /* Max number of tables for partition */
u8 virtualBusFlag; /* Flag to indicate virtual bus */
u8 rsvd[5];
};
extern struct TceTable virtBusTceTable; /* Tce table for virtual bus */
extern struct TceTable * build_tce_table( struct HvTceTableManagerCB *,
struct TceTable *);
extern void create_virtual_bus_tce_table( void );
extern void create_pci_bus_tce_table( unsigned busNumber );
#endif // _ISERIES_DMA_H
#ifndef __ISERIES_FIXUP_H__
#define __ISERIES_FIXUP_H__
#include <linux/pci.h>
#ifdef __cplusplus
extern "C" {
#endif
void iSeries_fixup (void);
void iSeries_fixup_bus (struct pci_bus*);
unsigned int iSeries_scan_slot (struct pci_dev*, u16, u8, u8);
/* Need to store information related to the PHB bucc and make it accessible to the hose */
struct iSeries_hose_arch_data {
u32 hvBusNumber;
};
#ifdef __cplusplus
}
#endif
#endif /* __ISERIES_FIXUP_H__ */
#ifdef CONFIG_PPC_ISERIES
#ifndef _ISERIES_IO_H
#define _ISERIES_IO_H
/************************************************************************/
/* File iSeries_io.h created by Allan Trautman on Thu Dec 28 2000. */
/************************************************************************/
/* Remaps the io.h for the iSeries Io */
/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created December 28, 2000 */
/* End Change Activity */
/************************************************************************/
extern u8 iSeries_Readb(u32* IoAddress);
extern u16 iSeries_Readw(u32* IoAddress);
extern u32 iSeries_Readl(u32* IoAddress);
extern void iSeries_Writeb(u8 IoData,u32* IoAddress);
extern void iSeries_Writew(u16 IoData,u32* IoAddress);
extern void iSeries_Writel(u32 IoData,u32* IoAddress);
extern void* iSeries_memcpy_toio(void *dest, void *source, int n);
extern void* iSeries_memcpy_fromio(void *dest, void *source, int n);
#endif /* _ISERIES_IO_H */
#endif /* CONFIG_PPC_ISERIES */
#ifndef __ISERIES_IRQ_H__
#define __ISERIES_IRQ_H__
#ifdef __cplusplus
extern "C" {
#endif
unsigned int iSeries_startup_IRQ(unsigned int);
void iSeries_shutdown_IRQ(unsigned int);
void iSeries_enable_IRQ(unsigned int);
void iSeries_disable_IRQ(unsigned int);
void iSeries_end_IRQ(unsigned int);
void iSeries_init_IRQ(void);
void iSeries_init_irqMap(int);
int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId);
int iSeries_assign_IRQ(int, HvBusNumber, HvSubBusNumber, HvAgentId);
void iSeries_activate_IRQs(void);
#ifdef __cplusplus
}
#endif
#endif /* __ISERIES_IRQ_H__ */
#ifndef _ISERIES_32_PCI_H
#define _ISERIES_32_PCI_H
/************************************************************************/
/* File iSeries_pci.h created by Allan Trautman on Tue Feb 20, 2001. */
/************************************************************************/
/* Define some useful macros for the iSeries pci routines. */
/* Copyright (C) 20yy Allan H Trautman, IBM Corporation */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the: */
/* Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, */
/* Boston, MA 02111-1307 USA */
/************************************************************************/
/* Change Activity: */
/* Created Feb 20, 2001 */
/* Added device reset, March 22, 2001 */
/* End Change Activity */
/************************************************************************/
#include <linux/config.h>
#include <asm/iSeries/HvCallPci.h>
struct pci_dev; /* For Reference */
/************************************************************************/
/* Gets iSeries Bus, SubBus, of DevFn using pci_dev* structure */
/************************************************************************/
#define ISERIES_GET_BUS(DevPtr) iSeries_Get_Bus(DevPtr)
#define ISERIES_GET_SUBBUS(DevPtr) iSeries_Get_SubBus(DevPtr)
#define ISERIES_GET_DEVFUN(DevPtr) iSeries_Get_DevFn(DevPtr)
/************************************************************************/
/* Functions */
/************************************************************************/
extern u8 iSeries_Get_Bus(struct pci_dev*);
extern u8 iSeries_Get_SubBus(struct pci_dev*);
extern u8 iSeries_Get_DevFn(struct pci_dev*);
/************************************************************************/
/* Global Bus map */
/************************************************************************/
extern u8 iSeries_GlobalBusMap[256][2]; /* Global iSeries Bus Map */
/************************************************************************/
/* iSeries Device Information */
/************************************************************************/
struct iSeries_Device_Struct {
struct pci_dev* PciDevPtr; /* Pointer to pci_dev structure */
HvBusNumber BusNumber; /* Hypervisor Bus Number */
HvSubBusNumber SubBus; /* Hypervisor SubBus Number */
HvAgentId DevFn; /* Hypervisor DevFn */
u8 BarNumber; /* Bar number on Xlates */
u32 BarOffset; /* Offset within Bar on Xlates */
int RCode; /* Return Code Holder */
};
typedef struct iSeries_Device_Struct iSeries_Device;
extern void build_iSeries_Device(iSeries_Device* Device, struct pci_dev* DevPtr);
/************************************************************************/
/* Formatting device information. */
/************************************************************************/
extern int iSeries_Device_Information(struct pci_dev*,char*,int );
/************************************************************************/
/* Flight Recorder tracing */
/************************************************************************/
extern void iSeries_Set_PciFilter(struct pci_dev*);
extern int iSeries_Set_PciTraceFlag(int TraceFlag);
extern int iSeries_Get_PciTraceFlag(void);
extern int iSeries_Set_PciErpFlag(int ErpFlag);
/************************************************************************/
/* Structure to hold the data for PCI Register Save/Restore functions. */
/************************************************************************/
struct pci_config_reg_save_area { /* */
u16 Flags; /* Control & Info Flags */
u16 ByteCount; /* Number of Register Bytes to S*/
struct pci_dev* PciDev; /* Pointer to device */
u32 RCode; /* Holder for possible errors */
u32 FailReg; /* Failing Register on error */
u8 Regs[64]; /* Save Area */
};
typedef struct pci_config_reg_save_area PciReqsSaveArea;
/************************************************************************/
/* Various flavors of reset device functions. */
/************************************************************************/
/* */
/* iSeries_Device_Reset_NoIrq */
/* IRQ is not disabled and default timings are used. */
/* iSeries_Device_Reset_Generic */
/* A generic reset, IRQ is disable and re-enabled. The assert and */
/* wait timings will be the pci defaults. */
/* iSeries_Device_Reset */
/* A device Reset interface that client can control the timing of */
/* the reset and wait delays. */
/* */
/* Parameters: */
/* pci_dev = Device to reset. */
/* AssertTime = Time in .1 seconds to hold the reset down. The */
/* default (and minimum) is .5 seconds. */
/* DelayTime = Time in .1 seconds to wait for device to come ready */
/* after the reset. The default is 3 seconds. */
/* IrgDisable = A non-zero will skip irq disable & enable. */
/* */
/* Return: */
/* Zero return, reset is successful. */
/* Non-zero return code indicates failure. */
/************************************************************************/
extern int iSeries_Device_Reset_NoIrq(struct pci_dev* PciDev);
extern int iSeries_Device_Reset_Generic(struct pci_dev* PciDev);
extern int iSeries_Device_Reset(struct pci_dev* PciDev, int AssertTime, int DelayTime, int IrqDisable);
extern int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime);
extern int iSeries_Device_RestoreConfigRegs(PciReqsSaveArea* SaveArea);
extern PciReqsSaveArea* iSeries_Device_SaveConfigRegs(struct pci_dev* DevPtr);
#endif /* _ISERIES_32_PCI_H */
/*
* iSeries_proc.h
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _ISERIES_PROC_H
#define _ISERIES_PROC_H
#include <linux/proc_fs.h>
extern void iSeries_proc_early_init(void);
extern void iSeries_proc_create(void);
typedef void (*iSeriesProcFunction)(struct proc_dir_entry *iSeries_proc);
extern void iSeries_proc_callback(iSeriesProcFunction initFunction);
#endif /* _iSeries_PROC_H */
/*
* mf.h
* Copyright (C) 2001 Troy D. Armstrong IBM Corporation
*
* This modules exists as an interface between a Linux secondary partition
* running on an iSeries and the primary partition's Virtual Service
* Processor (VSP) object. The VSP has final authority over powering on/off
* all partitions in the iSeries. It also provides miscellaneous low-level
* machine facility type operations.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef MF_H_INCLUDED
#define MF_H_INCLUDED
#include <asm/iSeries/HvTypes.h>
#include <asm/iSeries/HvLpEvent.h>
struct rtc_time;
typedef void (*MFCompleteHandler)( void * clientToken, int returnCode );
extern void mf_allocateLpEvents( HvLpIndex targetLp,
HvLpEvent_Type type,
unsigned size,
unsigned amount,
MFCompleteHandler hdlr,
void * userToken );
extern void mf_deallocateLpEvents( HvLpIndex targetLp,
HvLpEvent_Type type,
unsigned count,
MFCompleteHandler hdlr,
void * userToken );
extern void mf_powerOff( void );
extern void mf_reboot( void );
extern void mf_displaySrc( u32 word );
extern void mf_displayProgress( u16 value );
extern void mf_clearSrc( void );
extern void mf_init( void );
extern void mf_setSide(char side);
extern char mf_getSide(void);
extern void mf_setCmdLine(const char *cmdline, int size, u64 side);
extern int mf_getCmdLine(char *cmdline, int *size, u64 side);
extern void mf_getSrcHistory(char *buffer, int size);
extern int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side);
extern int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side);
extern int mf_setRtcTime(unsigned long time);
extern int mf_getRtcTime(unsigned long *time);
extern int mf_getRtc( struct rtc_time * tm );
extern int mf_setRtc( struct rtc_time * tm );
#endif /* MF_H_INCLUDED */
/*
* mf_proc.h
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _MF_PROC_H
#define _MF_PROC_H
#include <linux/proc_fs.h>
void mf_proc_init(struct proc_dir_entry *iSeries_proc);
#endif /* _MF_PROC_H */
/*
* pmc_proc.h
* Copyright (C) 2001 Mike Corrigan IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _PMC_PROC_H
#define _PMC_PROC_H
#include <linux/proc_fs.h>
void pmc_proc_init(struct proc_dir_entry *iSeries_proc);
#endif /* _PMC_PROC_H */
/*
* veth-proc.h
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Change Activity: */
/* End Change Activity */
#ifndef _VETH_PROC_H
#define _VETH_PROC_H
#include <linux/proc_fs.h>
void veth_proc_init(struct proc_dir_entry *iSeries_proc);
#endif /* _VETH-PROC_H */
...@@ -42,21 +42,6 @@ extern unsigned long isa_io_base; ...@@ -42,21 +42,6 @@ extern unsigned long isa_io_base;
extern unsigned long isa_mem_base; extern unsigned long isa_mem_base;
extern unsigned long pci_dram_offset; extern unsigned long pci_dram_offset;
#if defined(CONFIG_PPC_ISERIES)
#include <asm/iSeries.h>
#if defined(CONFIG_PCI)
#include <asm/iSeries/iSeries_io.h>
#endif /* defined(CONFIG_PCI) */
#endif /* CONFIG_PPC_ISERIES */
#if defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI)
#define readb(addr) iSeries_Readb((u32*)(addr))
#define readw(addr) iSeries_Readw((u32*)(addr))
#define readl(addr) iSeries_Readl((u32*)(addr))
#define writeb(data, addr) iSeries_Writeb(data,(u32*)(addr))
#define writew(data, addr) iSeries_Writew(data,(u32*)(addr))
#define writel(data, addr) iSeries_Writel(data,(u32*)(addr))
#else
#define readb(addr) in_8((volatile u8 *)(addr)) #define readb(addr) in_8((volatile u8 *)(addr))
#define writeb(b,addr) out_8((volatile u8 *)(addr), (b)) #define writeb(b,addr) out_8((volatile u8 *)(addr), (b))
#if defined(CONFIG_APUS) #if defined(CONFIG_APUS)
...@@ -69,8 +54,7 @@ extern unsigned long pci_dram_offset; ...@@ -69,8 +54,7 @@ extern unsigned long pci_dram_offset;
#define readl(addr) in_le32((volatile u32 *)(addr)) #define readl(addr) in_le32((volatile u32 *)(addr))
#define writew(b,addr) out_le16((volatile u16 *)(addr),(b)) #define writew(b,addr) out_le16((volatile u16 *)(addr),(b))
#define writel(b,addr) out_le32((volatile u32 *)(addr),(b)) #define writel(b,addr) out_le32((volatile u32 *)(addr),(b))
#endif #endif /* CONFIG_APUS */
#endif /* CONFIG_PPC_ISERIES && defined(CONFIG_PCI) */
#define __raw_readb(addr) (*(volatile unsigned char *)(addr)) #define __raw_readb(addr) (*(volatile unsigned char *)(addr))
...@@ -165,14 +149,6 @@ __do_out_asm(outl, "stwbrx") ...@@ -165,14 +149,6 @@ __do_out_asm(outl, "stwbrx")
#define inl(port) in_be32((u32 *)((port)+_IO_BASE)) #define inl(port) in_be32((u32 *)((port)+_IO_BASE))
#define outl(val, port) out_be32((u32 *)((port)+_IO_BASE), (val)) #define outl(val, port) out_be32((u32 *)((port)+_IO_BASE), (val))
#elif defined(CONFIG_PPC_ISERIES) && defined(CONFIG_PCI)
#define inb(addr) iSeries_Readb((u32*)(addr))
#define inw(addr) iSeries_Readw((u32*)(addr))
#define inl(addr) iSeries_Readl((u32*)(addr))
#define outb(data,addr) iSeries_Writeb(data,(u32*)(addr))
#define outw(data,addr) iSeries_Writew(data,(u32*)(addr))
#define outl(data,addr) iSeries_Writel(data,(u32*)(addr))
#else /* not APUS or ALL_PPC */ #else /* not APUS or ALL_PPC */
#define inb(port) in_8((u8 *)((port)+_IO_BASE)) #define inb(port) in_8((u8 *)((port)+_IO_BASE))
#define outb(val, port) out_8((u8 *)((port)+_IO_BASE), (val)) #define outb(val, port) out_8((u8 *)((port)+_IO_BASE), (val))
...@@ -214,13 +190,8 @@ extern void _outsl_ns(volatile u32 *port, const void *buf, int nl); ...@@ -214,13 +190,8 @@ extern void _outsl_ns(volatile u32 *port, const void *buf, int nl);
#define IO_SPACE_LIMIT ~0 #define IO_SPACE_LIMIT ~0
#define memset_io(a,b,c) memset((void *)(a),(b),(c)) #define memset_io(a,b,c) memset((void *)(a),(b),(c))
#ifdef CONFIG_PPC_ISERIES
#define memcpy_fromio(a,b,c) iSeries_memcpy_fromio((void *)(a), (void *)(b), (c))
#define memcpy_toio(a,b,c) iSeries_memcpy_toio((void *)(a), (void *)(b), (c))
#else
#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) #define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) #define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
#endif
#ifdef __KERNEL__ #ifdef __KERNEL__
/* /*
......
...@@ -107,7 +107,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) ...@@ -107,7 +107,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
bdnz 0b bdnz 0b
#endif #endif
#ifndef CONFIG_PPC_ISERIES #if !defined(CONFIG_440)
/* /*
* On APUS (Amiga PowerPC cpu upgrade board), we don't know the * On APUS (Amiga PowerPC cpu upgrade board), we don't know the
* physical base address of RAM at compile time. * physical base address of RAM at compile time.
...@@ -125,7 +125,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) ...@@ -125,7 +125,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
.align 1; \ .align 1; \
.long 0b; \ .long 0b; \
.previous .previous
#else /* CONFIG_PPC_ISERIES */ #else /* CONFIG_440 */
#define tophys(rd,rs) \ #define tophys(rd,rs) \
mr rd,rs mr rd,rs
...@@ -133,12 +133,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) ...@@ -133,12 +133,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
#define tovirt(rd,rs) \ #define tovirt(rd,rs) \
mr rd,rs mr rd,rs
/* Macros to adjust thread priority for iSeries hardware multi-threading */ #endif /* CONFIG_440 */
#define HMT_LOW or 1,1,1
#define HMT_MEDIUM or 2,2,2
#define HMT_HIGH or 3,3,3
#endif /* CONFIG_PPC_ISERIES */
/* /*
* On 64-bit cpus, we use the rfid instruction instead of rfi, but * On 64-bit cpus, we use the rfid instruction instead of rfi, but
...@@ -166,17 +161,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) ...@@ -166,17 +161,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
#define CLR_TOP32(r) #define CLR_TOP32(r)
#endif /* CONFIG_PPC64BRIDGE */ #endif /* CONFIG_PPC64BRIDGE */
#ifdef CONFIG_PPC_ISERIES
#define HMT_LOW or 1,1,1
#define HMT_MEDIUM or 2,2,2
#define HMT_HIGH or 3,3,3
#else /* CONFIG_PPC_ISERIES */
#define HMT_LOW /* nothing */
#define HMT_MEDIUM /* nothing */
#define HMT_HIGH /* nothing */
#endif /* CONFIG_PPC_ISERIES */
#ifdef CONFIG_IBM405_ERR77 #ifdef CONFIG_IBM405_ERR77
#define PPC405_ERR77(ra,rb) dcbt ra, rb; #define PPC405_ERR77(ra,rb) dcbt ra, rb;
#define PPC405_ERR77_SYNC sync; #define PPC405_ERR77_SYNC sync;
......
...@@ -562,29 +562,6 @@ ...@@ -562,29 +562,6 @@
#define proc_trap() asm volatile("trap") #define proc_trap() asm volatile("trap")
#ifdef CONFIG_PPC_ISERIES
/* Macros for adjusting thread priority (hardware multi-threading) */
#define HMT_PRIO_LOW "or 1,1,1\n" /* low prio, used for spin loops */
#define HMT_PRIO_MED "or 2,2,2\n" /* medium prio, for normal code */
#define HMT_PRIO_HIGH "or 3,3,3\n" /* high priority */
#define HMT_low() asm volatile("or 1,1,1")
#define HMT_medium() asm volatile("or 2,2,2")
#define HMT_high() asm volatile("or 3,3,3")
/* iSeries CTRL register (for runlatch) */
#define CTRLT 0x098
#define CTRLF 0x088
#define RUNLATCH 0x0001
#else /* !CONFIG_PPC_ISERIES */
#define HMT_PRIO_LOW
#define HMT_PRIO_MED
#define HMT_PRIO_HIGH
#endif /* CONFIG_PPC_ISERIES */
/* Segment Registers */ /* Segment Registers */
#define SR0 0 #define SR0 0
......
...@@ -37,8 +37,6 @@ struct pt_regs { ...@@ -37,8 +37,6 @@ struct pt_regs {
unsigned long result; /* Result of a system call */ unsigned long result; /* Result of a system call */
}; };
/* iSeries uses mq field for soft enable flag */
#define softEnable mq
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#ifdef __KERNEL__ #ifdef __KERNEL__
......
...@@ -37,12 +37,10 @@ static inline void _raw_spin_lock(spinlock_t *lock) ...@@ -37,12 +37,10 @@ static inline void _raw_spin_lock(spinlock_t *lock)
__asm__ __volatile__( __asm__ __volatile__(
"b 1f # spin_lock\n\ "b 1f # spin_lock\n\
2:" HMT_PRIO_LOW 2: lwzx %0,0,%1\n\
" lwzx %0,0,%1\n\
cmpwi 0,%0,0\n\ cmpwi 0,%0,0\n\
bne+ 2b\n" bne+ 2b\n\
HMT_PRIO_MED 1: lwarx %0,0,%1\n\
"1: lwarx %0,0,%1\n\
cmpwi 0,%0,0\n\ cmpwi 0,%0,0\n\
bne- 2b\n" bne- 2b\n"
PPC405_ERR77(0,%1) PPC405_ERR77(0,%1)
...@@ -107,12 +105,10 @@ static __inline__ void _raw_read_lock(rwlock_t *rw) ...@@ -107,12 +105,10 @@ static __inline__ void _raw_read_lock(rwlock_t *rw)
__asm__ __volatile__( __asm__ __volatile__(
"b 2f # read_lock\n\ "b 2f # read_lock\n\
1:" HMT_PRIO_LOW 1: lwzx %0,0,%1\n\
" lwzx %0,0,%1\n\
cmpwi 0,%0,0\n\ cmpwi 0,%0,0\n\
blt+ 1b\n" blt+ 1b\n\
HMT_PRIO_MED 2: lwarx %0,0,%1\n\
"2: lwarx %0,0,%1\n\
addic. %0,%0,1\n\ addic. %0,%0,1\n\
ble- 1b\n" ble- 1b\n"
PPC405_ERR77(0,%1) PPC405_ERR77(0,%1)
...@@ -146,12 +142,10 @@ static __inline__ void _raw_write_lock(rwlock_t *rw) ...@@ -146,12 +142,10 @@ static __inline__ void _raw_write_lock(rwlock_t *rw)
__asm__ __volatile__( __asm__ __volatile__(
"b 2f # write_lock\n\ "b 2f # write_lock\n\
1:" HMT_PRIO_LOW 1: lwzx %0,0,%1\n\
" lwzx %0,0,%1\n\
cmpwi 0,%0,0\n\ cmpwi 0,%0,0\n\
bne+ 1b\n" bne+ 1b\n\
HMT_PRIO_MED 2: lwarx %0,0,%1\n\
"2: lwarx %0,0,%1\n\
cmpwi 0,%0,0\n\ cmpwi 0,%0,0\n\
bne- 1b\n" bne- 1b\n"
PPC405_ERR77(0,%1) PPC405_ERR77(0,%1)
......
...@@ -13,10 +13,6 @@ ...@@ -13,10 +13,6 @@
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/threads.h> #include <linux/threads.h>
#ifdef CONFIG_PPC_ISERIES
#include <asm/iSeries/Paca.h>
#include <asm/iSeries/HvCall.h>
#endif
#include <asm/processor.h> #include <asm/processor.h>
/* time.c */ /* time.c */
...@@ -53,24 +49,6 @@ static __inline__ void set_dec(unsigned int val) ...@@ -53,24 +49,6 @@ static __inline__ void set_dec(unsigned int val)
return; /* Have to let it auto-reload */ return; /* Have to let it auto-reload */
#elif defined(CONFIG_8xx_CPU6) #elif defined(CONFIG_8xx_CPU6)
set_dec_cpu6(val); set_dec_cpu6(val);
#elif defined(CONFIG_PPC_ISERIES)
/*
* Add code here to set the virtual decrementer in
* ItLpPaca if we have shared processors and to
* invoke the hypervisor as needed.
*/
struct Paca * paca;
int cur_dec;
paca = (struct Paca *)mfspr(SPRG1);
if ( paca->xLpPaca.xSharedProc ) {
paca->xLpPaca.xVirtualDecr = val;
cur_dec = get_dec();
if ( cur_dec > val )
HvCall_setVirtualDecr();
}
else
mtspr(SPRN_DEC, val);
#else #else
mtspr(SPRN_DEC, val); mtspr(SPRN_DEC, val);
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment