powerpc: Remove the main legacy iSerie platform code
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
2d4b971287
commit
8ee3e0d696
|
@ -16,7 +16,6 @@ obj-$(CONFIG_FSL_SOC_BOOKE) += 85xx/
|
|||
obj-$(CONFIG_PPC_86xx) += 86xx/
|
||||
obj-$(CONFIG_PPC_POWERNV) += powernv/
|
||||
obj-$(CONFIG_PPC_PSERIES) += pseries/
|
||||
obj-$(CONFIG_PPC_ISERIES) += iseries/
|
||||
obj-$(CONFIG_PPC_MAPLE) += maple/
|
||||
obj-$(CONFIG_PPC_PASEMI) += pasemi/
|
||||
obj-$(CONFIG_PPC_CELL) += cell/
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
ccflags-y := -mno-minimal-toc
|
||||
|
||||
obj-y += exception.o
|
||||
obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt.o mf.o lpevents.o \
|
||||
hvcall.o proc.o htab.o iommu.o misc.o irq.o
|
||||
obj-$(CONFIG_PCI) += pci.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_VIOPATH) += viopath.o vio.o
|
||||
obj-$(CONFIG_MODULES) += ksyms.o
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* 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 _PLATFORMS_ISERIES_CALL_HPT_H
|
||||
#define _PLATFORMS_ISERIES_CALL_HPT_H
|
||||
|
||||
/*
|
||||
* This file contains the "hypervisor call" interface which is used to
|
||||
* drive the hypervisor from the OS.
|
||||
*/
|
||||
|
||||
#include <asm/iseries/hv_call_sc.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
#define HvCallHptGetHptAddress HvCallHpt + 0
|
||||
#define HvCallHptGetHptPages HvCallHpt + 1
|
||||
#define HvCallHptSetPp HvCallHpt + 5
|
||||
#define HvCallHptSetSwBits HvCallHpt + 6
|
||||
#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)
|
||||
{
|
||||
return HvCall0(HvCallHptGetHptAddress);
|
||||
}
|
||||
|
||||
static inline u64 HvCallHpt_getHptPages(void)
|
||||
{
|
||||
return HvCall0(HvCallHptGetHptPages);
|
||||
}
|
||||
|
||||
static inline void HvCallHpt_setPp(u32 hpteIndex, u8 value)
|
||||
{
|
||||
HvCall2(HvCallHptSetPp, hpteIndex, value);
|
||||
}
|
||||
|
||||
static inline void HvCallHpt_setSwBits(u32 hpteIndex, u8 bitson, u8 bitsoff)
|
||||
{
|
||||
HvCall3(HvCallHptSetSwBits, hpteIndex, bitson, bitsoff);
|
||||
}
|
||||
|
||||
static inline void HvCallHpt_invalidateNoSyncICache(u32 hpteIndex)
|
||||
{
|
||||
HvCall1(HvCallHptInvalidateNoSyncICache, hpteIndex);
|
||||
}
|
||||
|
||||
static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson,
|
||||
u8 bitsoff)
|
||||
{
|
||||
u64 compressedStatus;
|
||||
|
||||
compressedStatus = HvCall4(HvCallHptInvalidateSetSwBitsGet,
|
||||
hpteIndex, bitson, bitsoff, 1);
|
||||
HvCall1(HvCallHptInvalidateNoSyncICache, hpteIndex);
|
||||
return compressedStatus;
|
||||
}
|
||||
|
||||
static inline u64 HvCallHpt_findValid(struct hash_pte *hpte, u64 vpn)
|
||||
{
|
||||
return HvCall3Ret16(HvCallHptFindValid, hpte, vpn, 0, 0);
|
||||
}
|
||||
|
||||
static inline u64 HvCallHpt_findNextValid(struct hash_pte *hpte, u32 hpteIndex,
|
||||
u8 bitson, u8 bitsoff)
|
||||
{
|
||||
return HvCall3Ret16(HvCallHptFindNextValid, hpte, hpteIndex,
|
||||
bitson, bitsoff);
|
||||
}
|
||||
|
||||
static inline void HvCallHpt_get(struct hash_pte *hpte, u32 hpteIndex)
|
||||
{
|
||||
HvCall2Ret16(HvCallHptGet, hpte, hpteIndex, 0);
|
||||
}
|
||||
|
||||
static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit,
|
||||
struct hash_pte *hpte)
|
||||
{
|
||||
HvCall4(HvCallHptAddValidate, hpteIndex, hBit, hpte->v, hpte->r);
|
||||
}
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_CALL_HPT_H */
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
* Provides the Hypervisor PCI calls for iSeries Linux Parition.
|
||||
* Copyright (C) 2001 <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
|
||||
*/
|
||||
|
||||
#ifndef _PLATFORMS_ISERIES_CALL_PCI_H
|
||||
#define _PLATFORMS_ISERIES_CALL_PCI_H
|
||||
|
||||
#include <asm/iseries/hv_call_sc.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
|
||||
/*
|
||||
* DSA == Direct Select Address
|
||||
* this struct must be 64 bits in total
|
||||
*/
|
||||
struct HvCallPci_DsaAddr {
|
||||
u16 busNumber; /* PHB index? */
|
||||
u8 subBusNumber; /* PCI bus number? */
|
||||
u8 deviceId; /* device and function? */
|
||||
u8 barNumber;
|
||||
u8 reserved[3];
|
||||
};
|
||||
|
||||
union HvDsaMap {
|
||||
u64 DsaAddr;
|
||||
struct HvCallPci_DsaAddr Dsa;
|
||||
};
|
||||
|
||||
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 */
|
||||
u8 maxSubBusNumber; /* Max Sub Bus */
|
||||
u8 logicalSlotNumber; /* Logical Slot Number for IOA */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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_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);
|
||||
|
||||
*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);
|
||||
|
||||
*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*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumber;
|
||||
dsa.subBusNumber = subBusNumber;
|
||||
dsa.deviceId = deviceId;
|
||||
|
||||
return HvCall4(HvCallPciConfigStore8, *(u64 *)&dsa, offset, value, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
dsa.barNumber = barNumberParm;
|
||||
|
||||
return HvCall3(HvCallPciGetBarParms, *(u64*)&dsa, parms, sizeofParms);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_maskFisr(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceIdParm, u64 fisrMask)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
|
||||
return HvCall2(HvCallPciMaskFisr, *(u64*)&dsa, fisrMask);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_unmaskFisr(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceIdParm, u64 fisrMask)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
|
||||
return HvCall2(HvCallPciUnmaskFisr, *(u64*)&dsa, fisrMask);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_getDeviceInfo(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceNumberParm, u64 parms, u32 sizeofParms)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceNumberParm << 4;
|
||||
|
||||
return HvCall3(HvCallPciGetDeviceInfo, *(u64*)&dsa, parms, sizeofParms);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_maskInterrupts(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceIdParm, u64 interruptMask)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
|
||||
return HvCall2(HvCallPciMaskInterrupts, *(u64*)&dsa, interruptMask);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_unmaskInterrupts(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceIdParm, u64 interruptMask)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
|
||||
return HvCall2(HvCallPciUnmaskInterrupts, *(u64*)&dsa, interruptMask);
|
||||
}
|
||||
|
||||
static inline u64 HvCallPci_getBusUnitInfo(u16 busNumberParm, u8 subBusParm,
|
||||
u8 deviceIdParm, u64 parms, u32 sizeofParms)
|
||||
{
|
||||
struct HvCallPci_DsaAddr dsa;
|
||||
|
||||
*((u64*)&dsa) = 0;
|
||||
|
||||
dsa.busNumber = busNumberParm;
|
||||
dsa.subBusNumber = subBusParm;
|
||||
dsa.deviceId = deviceIdParm;
|
||||
|
||||
return HvCall3(HvCallPciGetBusUnitInfo, *(u64*)&dsa, parms,
|
||||
sizeofParms);
|
||||
}
|
||||
|
||||
static inline int HvCallPci_getBusVpd(u16 busNumParm, u64 destParm,
|
||||
u16 sizeParm)
|
||||
{
|
||||
u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm,
|
||||
sizeParm, HvCallPci_BusVpd);
|
||||
if (xRc == -1)
|
||||
return -1;
|
||||
else
|
||||
return xRc & 0xFFFF;
|
||||
}
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_CALL_PCI_H */
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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_CALL_SM_H
|
||||
#define _ISERIES_CALL_SM_H
|
||||
|
||||
/*
|
||||
* This file contains the "hypervisor call" interface which is used to
|
||||
* drive the hypervisor from the OS.
|
||||
*/
|
||||
|
||||
#include <asm/iseries/hv_call_sc.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
|
||||
#define HvCallSmGet64BitsOfAccessMap HvCallSm + 11
|
||||
|
||||
static inline u64 HvCallSm_get64BitsOfAccessMap(HvLpIndex lpIndex,
|
||||
u64 indexIntoBitMap)
|
||||
{
|
||||
return HvCall2(HvCallSmGet64BitsOfAccessMap, lpIndex, indexIntoBitMap);
|
||||
}
|
||||
|
||||
#endif /* _ISERIES_CALL_SM_H */
|
|
@ -1,643 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2006 Michael Ellerman, IBM Corporation
|
||||
* Copyright (C) 2000-2004, IBM Corporation
|
||||
*
|
||||
* Description:
|
||||
* This file contains all the routines to build a flattened device
|
||||
* tree for a legacy iSeries machine.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/if_ether.h> /* ETH_ALEN */
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/lppaca.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_config.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/udbg.h>
|
||||
|
||||
#include "processor_vpd.h"
|
||||
#include "call_hpt.h"
|
||||
#include "call_pci.h"
|
||||
#include "pci.h"
|
||||
#include "it_exp_vpd_panel.h"
|
||||
#include "naca.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These are created by the linker script at the start and end
|
||||
* of the section containing all the strings marked with the DS macro.
|
||||
*/
|
||||
extern char __dt_strings_start[];
|
||||
extern char __dt_strings_end[];
|
||||
|
||||
#define DS(s) ({ \
|
||||
static const char __s[] __attribute__((section(".dt_strings"))) = s; \
|
||||
__s; \
|
||||
})
|
||||
|
||||
struct iseries_flat_dt {
|
||||
struct boot_param_header header;
|
||||
u64 reserve_map[2];
|
||||
};
|
||||
|
||||
static void * __initdata dt_data;
|
||||
|
||||
/*
|
||||
* Putting these strings here keeps them out of the .dt_strings section
|
||||
* that we capture for the strings blob of the flattened device tree.
|
||||
*/
|
||||
static char __initdata device_type_cpu[] = "cpu";
|
||||
static char __initdata device_type_memory[] = "memory";
|
||||
static char __initdata device_type_serial[] = "serial";
|
||||
static char __initdata device_type_network[] = "network";
|
||||
static char __initdata device_type_pci[] = "pci";
|
||||
static char __initdata device_type_vdevice[] = "vdevice";
|
||||
static char __initdata device_type_vscsi[] = "vscsi";
|
||||
|
||||
|
||||
/* EBCDIC to ASCII conversion routines */
|
||||
|
||||
static unsigned char __init e2a(unsigned char x)
|
||||
{
|
||||
switch (x) {
|
||||
case 0x81 ... 0x89:
|
||||
return x - 0x81 + 'a';
|
||||
case 0x91 ... 0x99:
|
||||
return x - 0x91 + 'j';
|
||||
case 0xA2 ... 0xA9:
|
||||
return x - 0xA2 + 's';
|
||||
case 0xC1 ... 0xC9:
|
||||
return x - 0xC1 + 'A';
|
||||
case 0xD1 ... 0xD9:
|
||||
return x - 0xD1 + 'J';
|
||||
case 0xE2 ... 0xE9:
|
||||
return x - 0xE2 + 'S';
|
||||
case 0xF0 ... 0xF9:
|
||||
return x - 0xF0 + '0';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
static unsigned char * __init strne2a(unsigned char *dest,
|
||||
const unsigned char *src, size_t n)
|
||||
{
|
||||
int i;
|
||||
|
||||
n = strnlen(src, n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
dest[i] = e2a(src[i]);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
static struct iseries_flat_dt * __init dt_init(void)
|
||||
{
|
||||
struct iseries_flat_dt *dt;
|
||||
unsigned long str_len;
|
||||
|
||||
str_len = __dt_strings_end - __dt_strings_start;
|
||||
dt = (struct iseries_flat_dt *)ALIGN(klimit, 8);
|
||||
dt->header.off_mem_rsvmap =
|
||||
offsetof(struct iseries_flat_dt, reserve_map);
|
||||
dt->header.off_dt_strings = ALIGN(sizeof(*dt), 8);
|
||||
dt->header.off_dt_struct = dt->header.off_dt_strings
|
||||
+ ALIGN(str_len, 8);
|
||||
dt_data = (void *)((unsigned long)dt + dt->header.off_dt_struct);
|
||||
dt->header.dt_strings_size = str_len;
|
||||
|
||||
/* There is no notion of hardware cpu id on iSeries */
|
||||
dt->header.boot_cpuid_phys = smp_processor_id();
|
||||
|
||||
memcpy((char *)dt + dt->header.off_dt_strings, __dt_strings_start,
|
||||
str_len);
|
||||
|
||||
dt->header.magic = OF_DT_HEADER;
|
||||
dt->header.version = 0x10;
|
||||
dt->header.last_comp_version = 0x10;
|
||||
|
||||
dt->reserve_map[0] = 0;
|
||||
dt->reserve_map[1] = 0;
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
static void __init dt_push_u32(struct iseries_flat_dt *dt, u32 value)
|
||||
{
|
||||
*((u32 *)dt_data) = value;
|
||||
dt_data += sizeof(u32);
|
||||
}
|
||||
|
||||
#ifdef notyet
|
||||
static void __init dt_push_u64(struct iseries_flat_dt *dt, u64 value)
|
||||
{
|
||||
*((u64 *)dt_data) = value;
|
||||
dt_data += sizeof(u64);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __init dt_push_bytes(struct iseries_flat_dt *dt, const char *data,
|
||||
int len)
|
||||
{
|
||||
memcpy(dt_data, data, len);
|
||||
dt_data += ALIGN(len, 4);
|
||||
}
|
||||
|
||||
static void __init dt_start_node(struct iseries_flat_dt *dt, const char *name)
|
||||
{
|
||||
dt_push_u32(dt, OF_DT_BEGIN_NODE);
|
||||
dt_push_bytes(dt, name, strlen(name) + 1);
|
||||
}
|
||||
|
||||
#define dt_end_node(dt) dt_push_u32(dt, OF_DT_END_NODE)
|
||||
|
||||
static void __init __dt_prop(struct iseries_flat_dt *dt, const char *name,
|
||||
const void *data, int len)
|
||||
{
|
||||
unsigned long offset;
|
||||
|
||||
dt_push_u32(dt, OF_DT_PROP);
|
||||
|
||||
/* Length of the data */
|
||||
dt_push_u32(dt, len);
|
||||
|
||||
offset = name - __dt_strings_start;
|
||||
|
||||
/* The offset of the properties name in the string blob. */
|
||||
dt_push_u32(dt, (u32)offset);
|
||||
|
||||
/* The actual data. */
|
||||
dt_push_bytes(dt, data, len);
|
||||
}
|
||||
#define dt_prop(dt, name, data, len) __dt_prop((dt), DS(name), (data), (len))
|
||||
|
||||
#define dt_prop_str(dt, name, data) \
|
||||
dt_prop((dt), name, (data), strlen((data)) + 1); /* + 1 for NULL */
|
||||
|
||||
static void __init __dt_prop_u32(struct iseries_flat_dt *dt, const char *name,
|
||||
u32 data)
|
||||
{
|
||||
__dt_prop(dt, name, &data, sizeof(u32));
|
||||
}
|
||||
#define dt_prop_u32(dt, name, data) __dt_prop_u32((dt), DS(name), (data))
|
||||
|
||||
static void __init __maybe_unused __dt_prop_u64(struct iseries_flat_dt *dt,
|
||||
const char *name, u64 data)
|
||||
{
|
||||
__dt_prop(dt, name, &data, sizeof(u64));
|
||||
}
|
||||
#define dt_prop_u64(dt, name, data) __dt_prop_u64((dt), DS(name), (data))
|
||||
|
||||
#define dt_prop_u64_list(dt, name, data, n) \
|
||||
dt_prop((dt), name, (data), sizeof(u64) * (n))
|
||||
|
||||
#define dt_prop_u32_list(dt, name, data, n) \
|
||||
dt_prop((dt), name, (data), sizeof(u32) * (n))
|
||||
|
||||
#define dt_prop_empty(dt, name) dt_prop((dt), name, NULL, 0)
|
||||
|
||||
static void __init dt_cpus(struct iseries_flat_dt *dt)
|
||||
{
|
||||
unsigned char buf[32];
|
||||
unsigned char *p;
|
||||
unsigned int i, index;
|
||||
struct IoHriProcessorVpd *d;
|
||||
u32 pft_size[2];
|
||||
|
||||
/* yuck */
|
||||
snprintf(buf, 32, "PowerPC,%s", cur_cpu_spec->cpu_name);
|
||||
p = strchr(buf, ' ');
|
||||
if (!p) p = buf + strlen(buf);
|
||||
|
||||
dt_start_node(dt, "cpus");
|
||||
dt_prop_u32(dt, "#address-cells", 1);
|
||||
dt_prop_u32(dt, "#size-cells", 0);
|
||||
|
||||
pft_size[0] = 0; /* NUMA CEC cookie, 0 for non NUMA */
|
||||
pft_size[1] = __ilog2(HvCallHpt_getHptPages() * HW_PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < NR_LPPACAS; i++) {
|
||||
if (lppaca[i].dyn_proc_status >= 2)
|
||||
continue;
|
||||
|
||||
snprintf(p, 32 - (p - buf), "@%d", i);
|
||||
dt_start_node(dt, buf);
|
||||
|
||||
dt_prop_str(dt, "device_type", device_type_cpu);
|
||||
|
||||
index = lppaca[i].dyn_hv_phys_proc_index;
|
||||
d = &xIoHriProcessorVpd[index];
|
||||
|
||||
dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024);
|
||||
dt_prop_u32(dt, "i-cache-line-size", d->xInstCacheOperandSize);
|
||||
|
||||
dt_prop_u32(dt, "d-cache-size", d->xDataL1CacheSizeKB * 1024);
|
||||
dt_prop_u32(dt, "d-cache-line-size", d->xDataCacheOperandSize);
|
||||
|
||||
/* magic conversions to Hz copied from old code */
|
||||
dt_prop_u32(dt, "clock-frequency",
|
||||
((1UL << 34) * 1000000) / d->xProcFreq);
|
||||
dt_prop_u32(dt, "timebase-frequency",
|
||||
((1UL << 32) * 1000000) / d->xTimeBaseFreq);
|
||||
|
||||
dt_prop_u32(dt, "reg", i);
|
||||
|
||||
dt_prop_u32_list(dt, "ibm,pft-size", pft_size, 2);
|
||||
|
||||
dt_end_node(dt);
|
||||
}
|
||||
|
||||
dt_end_node(dt);
|
||||
}
|
||||
|
||||
static void __init dt_model(struct iseries_flat_dt *dt)
|
||||
{
|
||||
char buf[16] = "IBM,";
|
||||
|
||||
/* N.B. lparcfg.c knows about the "IBM," prefixes ... */
|
||||
/* "IBM," + mfgId[2:3] + systemSerial[1:5] */
|
||||
strne2a(buf + 4, xItExtVpdPanel.mfgID + 2, 2);
|
||||
strne2a(buf + 6, xItExtVpdPanel.systemSerial + 1, 5);
|
||||
buf[11] = '\0';
|
||||
dt_prop_str(dt, "system-id", buf);
|
||||
|
||||
/* "IBM," + machineType[0:4] */
|
||||
strne2a(buf + 4, xItExtVpdPanel.machineType, 4);
|
||||
buf[8] = '\0';
|
||||
dt_prop_str(dt, "model", buf);
|
||||
|
||||
dt_prop_str(dt, "compatible", "IBM,iSeries");
|
||||
dt_prop_u32(dt, "ibm,partition-no", HvLpConfig_getLpIndex());
|
||||
}
|
||||
|
||||
static void __init dt_initrd(struct iseries_flat_dt *dt)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
if (naca.xRamDisk) {
|
||||
dt_prop_u64(dt, "linux,initrd-start", (u64)naca.xRamDisk);
|
||||
dt_prop_u64(dt, "linux,initrd-end",
|
||||
(u64)naca.xRamDisk + naca.xRamDiskSize * HW_PAGE_SIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init dt_do_vdevice(struct iseries_flat_dt *dt,
|
||||
const char *name, u32 reg, int unit,
|
||||
const char *type, const char *compat, int end)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
snprintf(buf, 32, "%s@%08x", name, reg + ((unit >= 0) ? unit : 0));
|
||||
dt_start_node(dt, buf);
|
||||
dt_prop_str(dt, "device_type", type);
|
||||
if (compat)
|
||||
dt_prop_str(dt, "compatible", compat);
|
||||
dt_prop_u32(dt, "reg", reg + ((unit >= 0) ? unit : 0));
|
||||
if (unit >= 0)
|
||||
dt_prop_u32(dt, "linux,unit_address", unit);
|
||||
if (end)
|
||||
dt_end_node(dt);
|
||||
}
|
||||
|
||||
static void __init dt_vdevices(struct iseries_flat_dt *dt)
|
||||
{
|
||||
u32 reg = 0;
|
||||
HvLpIndexMap vlan_map;
|
||||
int i;
|
||||
|
||||
dt_start_node(dt, "vdevice");
|
||||
dt_prop_str(dt, "device_type", device_type_vdevice);
|
||||
dt_prop_str(dt, "compatible", "IBM,iSeries-vdevice");
|
||||
dt_prop_u32(dt, "#address-cells", 1);
|
||||
dt_prop_u32(dt, "#size-cells", 0);
|
||||
|
||||
dt_do_vdevice(dt, "vty", reg, -1, device_type_serial,
|
||||
"IBM,iSeries-vty", 1);
|
||||
reg++;
|
||||
|
||||
dt_do_vdevice(dt, "v-scsi", reg, -1, device_type_vscsi,
|
||||
"IBM,v-scsi", 1);
|
||||
reg++;
|
||||
|
||||
vlan_map = HvLpConfig_getVirtualLanIndexMap();
|
||||
for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
|
||||
unsigned char mac_addr[ETH_ALEN];
|
||||
|
||||
if ((vlan_map & (0x8000 >> i)) == 0)
|
||||
continue;
|
||||
dt_do_vdevice(dt, "l-lan", reg, i, device_type_network,
|
||||
"IBM,iSeries-l-lan", 0);
|
||||
mac_addr[0] = 0x02;
|
||||
mac_addr[1] = 0x01;
|
||||
mac_addr[2] = 0xff;
|
||||
mac_addr[3] = i;
|
||||
mac_addr[4] = 0xff;
|
||||
mac_addr[5] = HvLpConfig_getLpIndex_outline();
|
||||
dt_prop(dt, "local-mac-address", (char *)mac_addr, ETH_ALEN);
|
||||
dt_prop(dt, "mac-address", (char *)mac_addr, ETH_ALEN);
|
||||
dt_prop_u32(dt, "max-frame-size", 9000);
|
||||
dt_prop_u32(dt, "address-bits", 48);
|
||||
|
||||
dt_end_node(dt);
|
||||
}
|
||||
|
||||
dt_end_node(dt);
|
||||
}
|
||||
|
||||
struct pci_class_name {
|
||||
u16 code;
|
||||
const char *name;
|
||||
const char *type;
|
||||
};
|
||||
|
||||
static struct pci_class_name __initdata pci_class_name[] = {
|
||||
{ PCI_CLASS_NETWORK_ETHERNET, "ethernet", device_type_network },
|
||||
};
|
||||
|
||||
static struct pci_class_name * __init dt_find_pci_class_name(u16 class_code)
|
||||
{
|
||||
struct pci_class_name *cp;
|
||||
|
||||
for (cp = pci_class_name;
|
||||
cp < &pci_class_name[ARRAY_SIZE(pci_class_name)]; cp++)
|
||||
if (cp->code == class_code)
|
||||
return cp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This assumes that the node slot is always on the primary bus!
|
||||
*/
|
||||
static void __init scan_bridge_slot(struct iseries_flat_dt *dt,
|
||||
HvBusNumber bus, struct HvCallPci_BridgeInfo *bridge_info)
|
||||
{
|
||||
HvSubBusNumber sub_bus = bridge_info->subBusNumber;
|
||||
u16 vendor_id;
|
||||
u16 device_id;
|
||||
u32 class_id;
|
||||
int err;
|
||||
char buf[32];
|
||||
u32 reg[5];
|
||||
int id_sel = ISERIES_GET_DEVICE_FROM_SUBBUS(sub_bus);
|
||||
int function = ISERIES_GET_FUNCTION_FROM_SUBBUS(sub_bus);
|
||||
HvAgentId eads_id_sel = ISERIES_PCI_AGENTID(id_sel, function);
|
||||
u8 devfn;
|
||||
struct pci_class_name *cp;
|
||||
|
||||
/*
|
||||
* Connect all functions of any device found.
|
||||
*/
|
||||
for (id_sel = 1; id_sel <= bridge_info->maxAgents; id_sel++) {
|
||||
for (function = 0; function < 8; function++) {
|
||||
HvAgentId agent_id = ISERIES_PCI_AGENTID(id_sel,
|
||||
function);
|
||||
err = HvCallXm_connectBusUnit(bus, sub_bus,
|
||||
agent_id, 0);
|
||||
if (err) {
|
||||
if (err != 0x302)
|
||||
DBG("connectBusUnit(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, agent_id, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = HvCallPci_configLoad16(bus, sub_bus, agent_id,
|
||||
PCI_VENDOR_ID, &vendor_id);
|
||||
if (err) {
|
||||
DBG("ReadVendor(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, agent_id, err);
|
||||
continue;
|
||||
}
|
||||
err = HvCallPci_configLoad16(bus, sub_bus, agent_id,
|
||||
PCI_DEVICE_ID, &device_id);
|
||||
if (err) {
|
||||
DBG("ReadDevice(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, agent_id, err);
|
||||
continue;
|
||||
}
|
||||
err = HvCallPci_configLoad32(bus, sub_bus, agent_id,
|
||||
PCI_CLASS_REVISION , &class_id);
|
||||
if (err) {
|
||||
DBG("ReadClass(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, agent_id, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(eads_id_sel),
|
||||
function);
|
||||
cp = dt_find_pci_class_name(class_id >> 16);
|
||||
if (cp && cp->name)
|
||||
strncpy(buf, cp->name, sizeof(buf) - 1);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "pci%x,%x",
|
||||
vendor_id, device_id);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
|
||||
"@%x", PCI_SLOT(devfn));
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
if (function != 0)
|
||||
snprintf(buf + strlen(buf),
|
||||
sizeof(buf) - strlen(buf),
|
||||
",%x", function);
|
||||
dt_start_node(dt, buf);
|
||||
reg[0] = (bus << 16) | (devfn << 8);
|
||||
reg[1] = 0;
|
||||
reg[2] = 0;
|
||||
reg[3] = 0;
|
||||
reg[4] = 0;
|
||||
dt_prop_u32_list(dt, "reg", reg, 5);
|
||||
if (cp && (cp->type || cp->name))
|
||||
dt_prop_str(dt, "device_type",
|
||||
cp->type ? cp->type : cp->name);
|
||||
dt_prop_u32(dt, "vendor-id", vendor_id);
|
||||
dt_prop_u32(dt, "device-id", device_id);
|
||||
dt_prop_u32(dt, "class-code", class_id >> 8);
|
||||
dt_prop_u32(dt, "revision-id", class_id & 0xff);
|
||||
dt_prop_u32(dt, "linux,subbus", sub_bus);
|
||||
dt_prop_u32(dt, "linux,agent-id", agent_id);
|
||||
dt_prop_u32(dt, "linux,logical-slot-number",
|
||||
bridge_info->logicalSlotNumber);
|
||||
dt_end_node(dt);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __init scan_bridge(struct iseries_flat_dt *dt, HvBusNumber bus,
|
||||
HvSubBusNumber sub_bus, int id_sel)
|
||||
{
|
||||
struct HvCallPci_BridgeInfo bridge_info;
|
||||
HvAgentId agent_id;
|
||||
int function;
|
||||
int ret;
|
||||
|
||||
/* Note: hvSubBus and irq is always be 0 at this level! */
|
||||
for (function = 0; function < 8; ++function) {
|
||||
agent_id = ISERIES_PCI_AGENTID(id_sel, function);
|
||||
ret = HvCallXm_connectBusUnit(bus, sub_bus, agent_id, 0);
|
||||
if (ret != 0) {
|
||||
if (ret != 0xb)
|
||||
DBG("connectBusUnit(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, agent_id, ret);
|
||||
continue;
|
||||
}
|
||||
DBG("found device at bus %d idsel %d func %d (AgentId %x)\n",
|
||||
bus, id_sel, function, agent_id);
|
||||
ret = HvCallPci_getBusUnitInfo(bus, sub_bus, agent_id,
|
||||
iseries_hv_addr(&bridge_info),
|
||||
sizeof(struct HvCallPci_BridgeInfo));
|
||||
if (ret != 0)
|
||||
continue;
|
||||
DBG("bridge info: type %x subbus %x "
|
||||
"maxAgents %x maxsubbus %x logslot %x\n",
|
||||
bridge_info.busUnitInfo.deviceType,
|
||||
bridge_info.subBusNumber,
|
||||
bridge_info.maxAgents,
|
||||
bridge_info.maxSubBusNumber,
|
||||
bridge_info.logicalSlotNumber);
|
||||
if (bridge_info.busUnitInfo.deviceType ==
|
||||
HvCallPci_BridgeDevice)
|
||||
scan_bridge_slot(dt, bus, &bridge_info);
|
||||
else
|
||||
DBG("PCI: Invalid Bridge Configuration(0x%02X)",
|
||||
bridge_info.busUnitInfo.deviceType);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init scan_phb(struct iseries_flat_dt *dt, HvBusNumber bus)
|
||||
{
|
||||
struct HvCallPci_DeviceInfo dev_info;
|
||||
const HvSubBusNumber sub_bus = 0; /* EADs is always 0. */
|
||||
int err;
|
||||
int id_sel;
|
||||
const int max_agents = 8;
|
||||
|
||||
/*
|
||||
* Probe for EADs Bridges
|
||||
*/
|
||||
for (id_sel = 1; id_sel < max_agents; ++id_sel) {
|
||||
err = HvCallPci_getDeviceInfo(bus, sub_bus, id_sel,
|
||||
iseries_hv_addr(&dev_info),
|
||||
sizeof(struct HvCallPci_DeviceInfo));
|
||||
if (err) {
|
||||
if (err != 0x302)
|
||||
DBG("getDeviceInfo(%x, %x, %x) %x\n",
|
||||
bus, sub_bus, id_sel, err);
|
||||
continue;
|
||||
}
|
||||
if (dev_info.deviceType != HvCallPci_NodeDevice) {
|
||||
DBG("PCI: Invalid System Configuration"
|
||||
"(0x%02X) for bus 0x%02x id 0x%02x.\n",
|
||||
dev_info.deviceType, bus, id_sel);
|
||||
continue;
|
||||
}
|
||||
scan_bridge(dt, bus, sub_bus, id_sel);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init dt_pci_devices(struct iseries_flat_dt *dt)
|
||||
{
|
||||
HvBusNumber bus;
|
||||
char buf[32];
|
||||
u32 buses[2];
|
||||
int phb_num = 0;
|
||||
|
||||
/* Check all possible buses. */
|
||||
for (bus = 0; bus < 256; bus++) {
|
||||
int err = HvCallXm_testBus(bus);
|
||||
|
||||
if (err) {
|
||||
/*
|
||||
* Check for Unexpected Return code, a clue that
|
||||
* something has gone wrong.
|
||||
*/
|
||||
if (err != 0x0301)
|
||||
DBG("Unexpected Return on Probe(0x%02X) "
|
||||
"0x%04X\n", bus, err);
|
||||
continue;
|
||||
}
|
||||
DBG("bus %d appears to exist\n", bus);
|
||||
snprintf(buf, 32, "pci@%d", phb_num);
|
||||
dt_start_node(dt, buf);
|
||||
dt_prop_str(dt, "device_type", device_type_pci);
|
||||
dt_prop_str(dt, "compatible", "IBM,iSeries-Logical-PHB");
|
||||
dt_prop_u32(dt, "#address-cells", 3);
|
||||
dt_prop_u32(dt, "#size-cells", 2);
|
||||
buses[0] = buses[1] = bus;
|
||||
dt_prop_u32_list(dt, "bus-range", buses, 2);
|
||||
scan_phb(dt, bus);
|
||||
dt_end_node(dt);
|
||||
phb_num++;
|
||||
}
|
||||
}
|
||||
|
||||
static void dt_finish(struct iseries_flat_dt *dt)
|
||||
{
|
||||
dt_push_u32(dt, OF_DT_END);
|
||||
dt->header.totalsize = (unsigned long)dt_data - (unsigned long)dt;
|
||||
klimit = ALIGN((unsigned long)dt_data, 8);
|
||||
}
|
||||
|
||||
void * __init build_flat_dt(unsigned long phys_mem_size)
|
||||
{
|
||||
struct iseries_flat_dt *iseries_dt;
|
||||
u64 tmp[2];
|
||||
|
||||
iseries_dt = dt_init();
|
||||
|
||||
dt_start_node(iseries_dt, "");
|
||||
|
||||
dt_prop_u32(iseries_dt, "#address-cells", 2);
|
||||
dt_prop_u32(iseries_dt, "#size-cells", 2);
|
||||
dt_model(iseries_dt);
|
||||
|
||||
/* /memory */
|
||||
dt_start_node(iseries_dt, "memory@0");
|
||||
dt_prop_str(iseries_dt, "device_type", device_type_memory);
|
||||
tmp[0] = 0;
|
||||
tmp[1] = phys_mem_size;
|
||||
dt_prop_u64_list(iseries_dt, "reg", tmp, 2);
|
||||
dt_end_node(iseries_dt);
|
||||
|
||||
/* /chosen */
|
||||
dt_start_node(iseries_dt, "chosen");
|
||||
dt_prop_str(iseries_dt, "bootargs", cmd_line);
|
||||
dt_initrd(iseries_dt);
|
||||
dt_end_node(iseries_dt);
|
||||
|
||||
dt_cpus(iseries_dt);
|
||||
|
||||
dt_vdevices(iseries_dt);
|
||||
dt_pci_devices(iseries_dt);
|
||||
|
||||
dt_end_node(iseries_dt);
|
||||
|
||||
dt_finish(iseries_dt);
|
||||
|
||||
return iseries_dt;
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
/*
|
||||
* Low level routines for legacy iSeries support.
|
||||
*
|
||||
* Extracted from head_64.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 64bit PowerPC by Dave Engebretsen, Peter Bergner, and
|
||||
* Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com
|
||||
*
|
||||
* This file contains the low-level support and setup for the
|
||||
* PowerPC-64 platform, including trap and interrupt dispatch.
|
||||
*
|
||||
* 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 <asm/reg.h>
|
||||
#include <asm/ppc_asm.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
.text
|
||||
|
||||
.globl system_reset_iSeries
|
||||
system_reset_iSeries:
|
||||
bl .relative_toc
|
||||
mfspr r13,SPRN_SPRG3 /* Get alpaca address */
|
||||
LOAD_REG_ADDR(r23, alpaca)
|
||||
li r0,ALPACA_SIZE
|
||||
sub r23,r13,r23
|
||||
divdu r24,r23,r0 /* r24 has cpu number */
|
||||
cmpwi 0,r24,0 /* Are we processor 0? */
|
||||
bne 1f
|
||||
LOAD_REG_ADDR(r13, boot_paca)
|
||||
mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */
|
||||
mfmsr r23
|
||||
ori r23,r23,MSR_RI
|
||||
mtmsrd r23 /* RI on */
|
||||
b .__start_initialization_iSeries /* Start up the first processor */
|
||||
1: mfspr r4,SPRN_CTRLF
|
||||
li r5,CTRL_RUNLATCH /* Turn off the run light */
|
||||
andc r4,r4,r5
|
||||
mtspr SPRN_CTRLT,r4
|
||||
|
||||
/* Spin on __secondary_hold_spinloop until it is updated by the boot cpu. */
|
||||
/* In the UP case we'll yield() later, and we will not access the paca anyway */
|
||||
#ifdef CONFIG_SMP
|
||||
iSeries_secondary_wait_paca:
|
||||
HMT_LOW
|
||||
LOAD_REG_ADDR(r23, __secondary_hold_spinloop)
|
||||
ld r23,0(r23)
|
||||
|
||||
cmpdi 0,r23,0
|
||||
bne 2f /* go on when the master is ready */
|
||||
|
||||
/* Keep poking the Hypervisor until we're released */
|
||||
/* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
|
||||
lis r3,0x8002
|
||||
rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
|
||||
li r0,-1 /* r0=-1 indicates a Hypervisor call */
|
||||
sc /* Invoke the hypervisor via a system call */
|
||||
b iSeries_secondary_wait_paca
|
||||
|
||||
2:
|
||||
HMT_MEDIUM
|
||||
sync
|
||||
|
||||
LOAD_REG_ADDR(r3, nr_cpu_ids) /* get number of pacas allocated */
|
||||
lwz r3,0(r3) /* nr_cpus= or NR_CPUS can limit */
|
||||
cmpld 0,r24,r3 /* is our cpu number allocated? */
|
||||
bge iSeries_secondary_yield /* no, yield forever */
|
||||
|
||||
/* Load our paca now that it's been allocated */
|
||||
LOAD_REG_ADDR(r13, paca)
|
||||
ld r13,0(r13)
|
||||
mulli r0,r24,PACA_SIZE
|
||||
add r13,r13,r0
|
||||
mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */
|
||||
mfmsr r23
|
||||
ori r23,r23,MSR_RI
|
||||
mtmsrd r23 /* RI on */
|
||||
|
||||
iSeries_secondary_smp_loop:
|
||||
lbz r23,PACAPROCSTART(r13) /* Test if this processor
|
||||
* should start */
|
||||
cmpwi 0,r23,0
|
||||
bne 3f /* go on when we are told */
|
||||
|
||||
HMT_LOW
|
||||
/* Let the Hypervisor know we are alive */
|
||||
/* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */
|
||||
lis r3,0x8002
|
||||
rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */
|
||||
li r0,-1 /* r0=-1 indicates a Hypervisor call */
|
||||
sc /* Invoke the hypervisor via a system call */
|
||||
mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */
|
||||
b iSeries_secondary_smp_loop /* wait for signal to start */
|
||||
|
||||
3:
|
||||
HMT_MEDIUM
|
||||
sync
|
||||
LOAD_REG_ADDR(r3,current_set)
|
||||
sldi r28,r24,3 /* get current_set[cpu#] */
|
||||
ldx r3,r3,r28
|
||||
addi r1,r3,THREAD_SIZE
|
||||
subi r1,r1,STACK_FRAME_OVERHEAD
|
||||
|
||||
b __secondary_start /* Loop until told to go */
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
iSeries_secondary_yield:
|
||||
/* Yield the processor. This is required for non-SMP kernels
|
||||
which are running on multi-threaded machines. */
|
||||
HMT_LOW
|
||||
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" */
|
||||
li r0,-1 /* r0=-1 indicates a Hypervisor call */
|
||||
sc /* Invoke the hypervisor via a system call */
|
||||
mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */
|
||||
b iSeries_secondary_yield /* If SMP not configured, secondaries
|
||||
* loop forever */
|
||||
|
||||
/*** ISeries-LPAR interrupt handlers ***/
|
||||
|
||||
STD_EXCEPTION_ISERIES(machine_check, PACA_EXMC)
|
||||
|
||||
.globl data_access_iSeries
|
||||
data_access_iSeries:
|
||||
mtspr SPRN_SPRG_SCRATCH0,r13
|
||||
BEGIN_FTR_SECTION
|
||||
mfspr r13,SPRN_SPRG_PACA
|
||||
std r9,PACA_EXSLB+EX_R9(r13)
|
||||
std r10,PACA_EXSLB+EX_R10(r13)
|
||||
mfspr r10,SPRN_DAR
|
||||
mfspr r9,SPRN_DSISR
|
||||
srdi r10,r10,60
|
||||
rlwimi r10,r9,16,0x20
|
||||
mfcr r9
|
||||
cmpwi r10,0x2c
|
||||
beq .do_stab_bolted_iSeries
|
||||
ld r10,PACA_EXSLB+EX_R10(r13)
|
||||
std r11,PACA_EXGEN+EX_R11(r13)
|
||||
ld r11,PACA_EXSLB+EX_R9(r13)
|
||||
std r12,PACA_EXGEN+EX_R12(r13)
|
||||
mfspr r12,SPRN_SPRG_SCRATCH0
|
||||
std r10,PACA_EXGEN+EX_R10(r13)
|
||||
std r11,PACA_EXGEN+EX_R9(r13)
|
||||
std r12,PACA_EXGEN+EX_R13(r13)
|
||||
EXCEPTION_PROLOG_ISERIES_1
|
||||
FTR_SECTION_ELSE
|
||||
EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0)
|
||||
EXCEPTION_PROLOG_ISERIES_1
|
||||
ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_SLB)
|
||||
b data_access_common
|
||||
|
||||
.do_stab_bolted_iSeries:
|
||||
std r11,PACA_EXSLB+EX_R11(r13)
|
||||
std r12,PACA_EXSLB+EX_R12(r13)
|
||||
mfspr r10,SPRN_SPRG_SCRATCH0
|
||||
std r10,PACA_EXSLB+EX_R13(r13)
|
||||
EXCEPTION_PROLOG_ISERIES_1
|
||||
b .do_stab_bolted
|
||||
|
||||
.globl data_access_slb_iSeries
|
||||
data_access_slb_iSeries:
|
||||
mtspr SPRN_SPRG_SCRATCH0,r13 /* save r13 */
|
||||
mfspr r13,SPRN_SPRG_PACA /* get paca address into r13 */
|
||||
std r3,PACA_EXSLB+EX_R3(r13)
|
||||
mfspr r3,SPRN_DAR
|
||||
std r9,PACA_EXSLB+EX_R9(r13)
|
||||
mfcr r9
|
||||
#ifdef __DISABLED__
|
||||
cmpdi r3,0
|
||||
bge slb_miss_user_iseries
|
||||
#endif
|
||||
std r10,PACA_EXSLB+EX_R10(r13)
|
||||
std r11,PACA_EXSLB+EX_R11(r13)
|
||||
std r12,PACA_EXSLB+EX_R12(r13)
|
||||
mfspr r10,SPRN_SPRG_SCRATCH0
|
||||
std r10,PACA_EXSLB+EX_R13(r13)
|
||||
ld r12,PACALPPACAPTR(r13)
|
||||
ld r12,LPPACASRR1(r12)
|
||||
b .slb_miss_realmode
|
||||
|
||||
STD_EXCEPTION_ISERIES(instruction_access, PACA_EXGEN)
|
||||
|
||||
.globl instruction_access_slb_iSeries
|
||||
instruction_access_slb_iSeries:
|
||||
mtspr SPRN_SPRG_SCRATCH0,r13 /* save r13 */
|
||||
mfspr r13,SPRN_SPRG_PACA /* get paca address into r13 */
|
||||
std r3,PACA_EXSLB+EX_R3(r13)
|
||||
ld r3,PACALPPACAPTR(r13)
|
||||
ld r3,LPPACASRR0(r3) /* get SRR0 value */
|
||||
std r9,PACA_EXSLB+EX_R9(r13)
|
||||
mfcr r9
|
||||
#ifdef __DISABLED__
|
||||
cmpdi r3,0
|
||||
bge slb_miss_user_iseries
|
||||
#endif
|
||||
std r10,PACA_EXSLB+EX_R10(r13)
|
||||
std r11,PACA_EXSLB+EX_R11(r13)
|
||||
std r12,PACA_EXSLB+EX_R12(r13)
|
||||
mfspr r10,SPRN_SPRG_SCRATCH0
|
||||
std r10,PACA_EXSLB+EX_R13(r13)
|
||||
ld r12,PACALPPACAPTR(r13)
|
||||
ld r12,LPPACASRR1(r12)
|
||||
b .slb_miss_realmode
|
||||
|
||||
#ifdef __DISABLED__
|
||||
slb_miss_user_iseries:
|
||||
std r10,PACA_EXGEN+EX_R10(r13)
|
||||
std r11,PACA_EXGEN+EX_R11(r13)
|
||||
std r12,PACA_EXGEN+EX_R12(r13)
|
||||
mfspr r10,SPRG_SCRATCH0
|
||||
ld r11,PACA_EXSLB+EX_R9(r13)
|
||||
ld r12,PACA_EXSLB+EX_R3(r13)
|
||||
std r10,PACA_EXGEN+EX_R13(r13)
|
||||
std r11,PACA_EXGEN+EX_R9(r13)
|
||||
std r12,PACA_EXGEN+EX_R3(r13)
|
||||
EXCEPTION_PROLOG_ISERIES_1
|
||||
b slb_miss_user_common
|
||||
#endif
|
||||
|
||||
MASKABLE_EXCEPTION_ISERIES(hardware_interrupt)
|
||||
STD_EXCEPTION_ISERIES(alignment, PACA_EXGEN)
|
||||
STD_EXCEPTION_ISERIES(program_check, PACA_EXGEN)
|
||||
STD_EXCEPTION_ISERIES(fp_unavailable, PACA_EXGEN)
|
||||
MASKABLE_EXCEPTION_ISERIES(decrementer)
|
||||
STD_EXCEPTION_ISERIES(trap_0a, PACA_EXGEN)
|
||||
STD_EXCEPTION_ISERIES(trap_0b, PACA_EXGEN)
|
||||
|
||||
.globl system_call_iSeries
|
||||
system_call_iSeries:
|
||||
mr r9,r13
|
||||
mfspr r13,SPRN_SPRG_PACA
|
||||
EXCEPTION_PROLOG_ISERIES_1
|
||||
b system_call_common
|
||||
|
||||
STD_EXCEPTION_ISERIES(single_step, PACA_EXGEN)
|
||||
STD_EXCEPTION_ISERIES(trap_0e, PACA_EXGEN)
|
||||
STD_EXCEPTION_ISERIES(performance_monitor, PACA_EXGEN)
|
||||
|
||||
decrementer_iSeries_masked:
|
||||
/* We may not have a valid TOC pointer in here. */
|
||||
li r11,1
|
||||
ld r12,PACALPPACAPTR(r13)
|
||||
stb r11,LPPACADECRINT(r12)
|
||||
li r12,-1
|
||||
clrldi r12,r12,33 /* set DEC to 0x7fffffff */
|
||||
mtspr SPRN_DEC,r12
|
||||
/* fall through */
|
||||
|
||||
hardware_interrupt_iSeries_masked:
|
||||
mtcrf 0x80,r9 /* Restore regs */
|
||||
ld r12,PACALPPACAPTR(r13)
|
||||
ld r11,LPPACASRR0(r12)
|
||||
ld r12,LPPACASRR1(r12)
|
||||
mtspr SPRN_SRR0,r11
|
||||
mtspr SPRN_SRR1,r12
|
||||
ld r9,PACA_EXGEN+EX_R9(r13)
|
||||
ld r10,PACA_EXGEN+EX_R10(r13)
|
||||
ld r11,PACA_EXGEN+EX_R11(r13)
|
||||
ld r12,PACA_EXGEN+EX_R12(r13)
|
||||
ld r13,PACA_EXGEN+EX_R13(r13)
|
||||
rfid
|
||||
b . /* prevent speculative execution */
|
||||
|
||||
_INIT_STATIC(__start_initialization_iSeries)
|
||||
/* Clear out the BSS */
|
||||
LOAD_REG_ADDR(r11,__bss_stop)
|
||||
LOAD_REG_ADDR(r8,__bss_start)
|
||||
sub r11,r11,r8 /* bss size */
|
||||
addi r11,r11,7 /* round up to an even double word */
|
||||
rldicl. r11,r11,61,3 /* shift right by 3 */
|
||||
beq 4f
|
||||
addi r8,r8,-8
|
||||
li r0,0
|
||||
mtctr r11 /* zero this many doublewords */
|
||||
3: stdu r0,8(r8)
|
||||
bdnz 3b
|
||||
4:
|
||||
LOAD_REG_ADDR(r1,init_thread_union)
|
||||
addi r1,r1,THREAD_SIZE
|
||||
li r0,0
|
||||
stdu r0,-STACK_FRAME_OVERHEAD(r1)
|
||||
|
||||
bl .iSeries_early_setup
|
||||
bl .early_setup
|
||||
|
||||
/* relocation is on at this point */
|
||||
|
||||
b .start_here_common
|
|
@ -1,58 +0,0 @@
|
|||
#ifndef _ASM_POWERPC_ISERIES_EXCEPTION_H
|
||||
#define _ASM_POWERPC_ISERIES_EXCEPTION_H
|
||||
/*
|
||||
* Extracted from head_64.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 64bit PowerPC by Dave Engebretsen, Peter Bergner, and
|
||||
* Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com
|
||||
*
|
||||
* This file contains the low-level support and setup for the
|
||||
* PowerPC-64 platform, including trap and interrupt dispatch.
|
||||
*
|
||||
* 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 <asm/exception-64s.h>
|
||||
|
||||
#define EXCEPTION_PROLOG_ISERIES_1 \
|
||||
mfmsr r10; \
|
||||
ld r12,PACALPPACAPTR(r13); \
|
||||
ld r11,LPPACASRR0(r12); \
|
||||
ld r12,LPPACASRR1(r12); \
|
||||
ori r10,r10,MSR_RI; \
|
||||
mtmsrd r10,1
|
||||
|
||||
#define STD_EXCEPTION_ISERIES(label, area) \
|
||||
.globl label##_iSeries; \
|
||||
label##_iSeries: \
|
||||
HMT_MEDIUM; \
|
||||
mtspr SPRN_SPRG_SCRATCH0,r13; /* save r13 */ \
|
||||
EXCEPTION_PROLOG_1(area, NOTEST, 0); \
|
||||
EXCEPTION_PROLOG_ISERIES_1; \
|
||||
b label##_common
|
||||
|
||||
#define MASKABLE_EXCEPTION_ISERIES(label) \
|
||||
.globl label##_iSeries; \
|
||||
label##_iSeries: \
|
||||
HMT_MEDIUM; \
|
||||
mtspr SPRN_SPRG_SCRATCH0,r13; /* save r13 */ \
|
||||
EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0); \
|
||||
lbz r10,PACASOFTIRQEN(r13); \
|
||||
cmpwi 0,r10,0; \
|
||||
beq- label##_iSeries_masked; \
|
||||
EXCEPTION_PROLOG_ISERIES_1; \
|
||||
b label##_common; \
|
||||
|
||||
#endif /* _ASM_POWERPC_ISERIES_EXCEPTION_H */
|
|
@ -1,257 +0,0 @@
|
|||
/*
|
||||
* iSeries hashtable management.
|
||||
* Derived from pSeries_htab.c
|
||||
*
|
||||
* SMP scalability work:
|
||||
* Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
|
||||
*
|
||||
* 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 <asm/machdep.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "call_hpt.h"
|
||||
|
||||
static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp;
|
||||
|
||||
/*
|
||||
* Very primitive algorithm for picking up a lock
|
||||
*/
|
||||
static inline void iSeries_hlock(unsigned long slot)
|
||||
{
|
||||
if (slot & 0x8)
|
||||
slot = ~slot;
|
||||
spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
|
||||
}
|
||||
|
||||
static inline void iSeries_hunlock(unsigned long slot)
|
||||
{
|
||||
if (slot & 0x8)
|
||||
slot = ~slot;
|
||||
spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
|
||||
}
|
||||
|
||||
static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
|
||||
unsigned long pa, unsigned long rflags,
|
||||
unsigned long vflags, int psize, int ssize)
|
||||
{
|
||||
long slot;
|
||||
struct hash_pte lhpte;
|
||||
int secondary = 0;
|
||||
|
||||
BUG_ON(psize != MMU_PAGE_4K);
|
||||
|
||||
/*
|
||||
* The hypervisor tries both primary and secondary.
|
||||
* If we are being called to insert in the secondary,
|
||||
* it means we have already tried both primary and secondary,
|
||||
* so we return failure immediately.
|
||||
*/
|
||||
if (vflags & HPTE_V_SECONDARY)
|
||||
return -1;
|
||||
|
||||
iSeries_hlock(hpte_group);
|
||||
|
||||
slot = HvCallHpt_findValid(&lhpte, va >> HW_PAGE_SHIFT);
|
||||
if (unlikely(lhpte.v & HPTE_V_VALID)) {
|
||||
if (vflags & HPTE_V_BOLTED) {
|
||||
HvCallHpt_setSwBits(slot, 0x10, 0);
|
||||
HvCallHpt_setPp(slot, PP_RWXX);
|
||||
iSeries_hunlock(hpte_group);
|
||||
if (slot < 0)
|
||||
return 0x8 | (slot & 7);
|
||||
else
|
||||
return slot & 7;
|
||||
}
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (slot == -1) { /* No available entry found in either group */
|
||||
iSeries_hunlock(hpte_group);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slot < 0) { /* MSB set means secondary group */
|
||||
vflags |= HPTE_V_SECONDARY;
|
||||
secondary = 1;
|
||||
slot &= 0x7fffffffffffffff;
|
||||
}
|
||||
|
||||
|
||||
lhpte.v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M) |
|
||||
vflags | HPTE_V_VALID;
|
||||
lhpte.r = hpte_encode_r(phys_to_abs(pa), MMU_PAGE_4K) | rflags;
|
||||
|
||||
/* Now fill in the actual HPTE */
|
||||
HvCallHpt_addValidate(slot, secondary, &lhpte);
|
||||
|
||||
iSeries_hunlock(hpte_group);
|
||||
|
||||
return (secondary << 3) | (slot & 7);
|
||||
}
|
||||
|
||||
static unsigned long iSeries_hpte_getword0(unsigned long slot)
|
||||
{
|
||||
struct hash_pte hpte;
|
||||
|
||||
HvCallHpt_get(&hpte, slot);
|
||||
return hpte.v;
|
||||
}
|
||||
|
||||
static long iSeries_hpte_remove(unsigned long hpte_group)
|
||||
{
|
||||
unsigned long slot_offset;
|
||||
int i;
|
||||
unsigned long hpte_v;
|
||||
|
||||
/* Pick a random slot to start at */
|
||||
slot_offset = mftb() & 0x7;
|
||||
|
||||
iSeries_hlock(hpte_group);
|
||||
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset);
|
||||
|
||||
if (! (hpte_v & HPTE_V_BOLTED)) {
|
||||
HvCallHpt_invalidateSetSwBitsGet(hpte_group +
|
||||
slot_offset, 0, 0);
|
||||
iSeries_hunlock(hpte_group);
|
||||
return i;
|
||||
}
|
||||
|
||||
slot_offset++;
|
||||
slot_offset &= 0x7;
|
||||
}
|
||||
|
||||
iSeries_hunlock(hpte_group);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The HyperVisor expects the "flags" argument in this form:
|
||||
* bits 0..59 : reserved
|
||||
* bit 60 : N
|
||||
* bits 61..63 : PP2,PP1,PP0
|
||||
*/
|
||||
static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp,
|
||||
unsigned long va, int psize, int ssize, int local)
|
||||
{
|
||||
struct hash_pte hpte;
|
||||
unsigned long want_v;
|
||||
|
||||
iSeries_hlock(slot);
|
||||
|
||||
HvCallHpt_get(&hpte, slot);
|
||||
want_v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M);
|
||||
|
||||
if (HPTE_V_COMPARE(hpte.v, want_v) && (hpte.v & HPTE_V_VALID)) {
|
||||
/*
|
||||
* Hypervisor expects bits as NPPP, which is
|
||||
* different from how they are mapped in our PP.
|
||||
*/
|
||||
HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
|
||||
iSeries_hunlock(slot);
|
||||
return 0;
|
||||
}
|
||||
iSeries_hunlock(slot);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions used to find the PTE for a particular virtual address.
|
||||
* Only used during boot when bolting pages.
|
||||
*
|
||||
* Input : vpn : virtual page number
|
||||
* Output: PTE index within the page table of the entry
|
||||
* -1 on failure
|
||||
*/
|
||||
static long iSeries_hpte_find(unsigned long vpn)
|
||||
{
|
||||
struct hash_pte hpte;
|
||||
long slot;
|
||||
|
||||
/*
|
||||
* The HvCallHpt_findValid interface is as follows:
|
||||
* 0xffffffffffffffff : No entry found.
|
||||
* 0x00000000xxxxxxxx : Entry found in primary group, slot x
|
||||
* 0x80000000xxxxxxxx : Entry found in secondary group, slot x
|
||||
*/
|
||||
slot = HvCallHpt_findValid(&hpte, vpn);
|
||||
if (hpte.v & HPTE_V_VALID) {
|
||||
if (slot < 0) {
|
||||
slot &= 0x7fffffffffffffff;
|
||||
slot = -slot;
|
||||
}
|
||||
} else
|
||||
slot = -1;
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the page protection bits. Intended to be used to create
|
||||
* guard pages for kernel data structures on pages which are bolted
|
||||
* in the HPT. Assumes pages being operated on will not be stolen.
|
||||
* Does not work on large pages.
|
||||
*
|
||||
* No need to lock here because we should be the only user.
|
||||
*/
|
||||
static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
|
||||
int psize, int ssize)
|
||||
{
|
||||
unsigned long vsid,va,vpn;
|
||||
long slot;
|
||||
|
||||
BUG_ON(psize != MMU_PAGE_4K);
|
||||
|
||||
vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M);
|
||||
va = (vsid << 28) | (ea & 0x0fffffff);
|
||||
vpn = va >> HW_PAGE_SHIFT;
|
||||
slot = iSeries_hpte_find(vpn);
|
||||
if (slot == -1)
|
||||
panic("updateboltedpp: Could not find page to bolt\n");
|
||||
HvCallHpt_setPp(slot, newpp);
|
||||
}
|
||||
|
||||
static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va,
|
||||
int psize, int ssize, int local)
|
||||
{
|
||||
unsigned long hpte_v;
|
||||
unsigned long avpn = va >> 23;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
iSeries_hlock(slot);
|
||||
|
||||
hpte_v = iSeries_hpte_getword0(slot);
|
||||
|
||||
if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID))
|
||||
HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0);
|
||||
|
||||
iSeries_hunlock(slot);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void __init hpte_init_iSeries(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iSeries_hlocks); i++)
|
||||
spin_lock_init(&iSeries_hlocks[i]);
|
||||
|
||||
ppc_md.hpte_invalidate = iSeries_hpte_invalidate;
|
||||
ppc_md.hpte_updatepp = iSeries_hpte_updatepp;
|
||||
ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp;
|
||||
ppc_md.hpte_insert = iSeries_hpte_insert;
|
||||
ppc_md.hpte_remove = iSeries_hpte_remove;
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* This file contains the code to perform calls to the
|
||||
* iSeries LPAR hypervisor
|
||||
*
|
||||
* 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 <asm/ppc_asm.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h> /* XXX for STACK_FRAME_OVERHEAD */
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* Hypervisor call
|
||||
*
|
||||
* Invoke the iSeries hypervisor via the System Call instruction
|
||||
* Parameters are passed to this routine in registers r3 - r10
|
||||
*
|
||||
* r3 contains the HV function to be called
|
||||
* r4-r10 contain the operands to the hypervisor function
|
||||
*
|
||||
*/
|
||||
|
||||
_GLOBAL(HvCall)
|
||||
_GLOBAL(HvCall0)
|
||||
_GLOBAL(HvCall1)
|
||||
_GLOBAL(HvCall2)
|
||||
_GLOBAL(HvCall3)
|
||||
_GLOBAL(HvCall4)
|
||||
_GLOBAL(HvCall5)
|
||||
_GLOBAL(HvCall6)
|
||||
_GLOBAL(HvCall7)
|
||||
|
||||
|
||||
mfcr r0
|
||||
std r0,-8(r1)
|
||||
stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1)
|
||||
|
||||
/* r0 = 0xffffffffffffffff indicates a hypervisor call */
|
||||
|
||||
li r0,-1
|
||||
|
||||
/* Invoke the hypervisor */
|
||||
|
||||
sc
|
||||
|
||||
ld r1,0(r1)
|
||||
ld r0,-8(r1)
|
||||
mtcrf 0xff,r0
|
||||
|
||||
/* return to caller, return value in r3 */
|
||||
|
||||
blr
|
||||
|
||||
_GLOBAL(HvCall0Ret16)
|
||||
_GLOBAL(HvCall1Ret16)
|
||||
_GLOBAL(HvCall2Ret16)
|
||||
_GLOBAL(HvCall3Ret16)
|
||||
_GLOBAL(HvCall4Ret16)
|
||||
_GLOBAL(HvCall5Ret16)
|
||||
_GLOBAL(HvCall6Ret16)
|
||||
_GLOBAL(HvCall7Ret16)
|
||||
|
||||
mfcr r0
|
||||
std r0,-8(r1)
|
||||
std r31,-16(r1)
|
||||
stdu r1,-(STACK_FRAME_OVERHEAD+32)(r1)
|
||||
|
||||
mr r31,r4
|
||||
li r0,-1
|
||||
mr r4,r5
|
||||
mr r5,r6
|
||||
mr r6,r7
|
||||
mr r7,r8
|
||||
mr r8,r9
|
||||
mr r9,r10
|
||||
|
||||
sc
|
||||
|
||||
std r3,0(r31)
|
||||
std r4,8(r31)
|
||||
|
||||
mr r3,r5
|
||||
|
||||
ld r1,0(r1)
|
||||
ld r0,-8(r1)
|
||||
mtcrf 0xff,r0
|
||||
ld r31,-16(r1)
|
||||
|
||||
blr
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/iseries/hv_call.h>
|
||||
#include <asm/iseries/hv_call_sc.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
|
||||
|
||||
void HvCall_writeLogBuffer(const void *buffer, u64 len)
|
||||
{
|
||||
struct HvLpBufferList hv_buf;
|
||||
u64 left_this_page;
|
||||
u64 cur = virt_to_abs(buffer);
|
||||
|
||||
while (len) {
|
||||
hv_buf.addr = cur;
|
||||
left_this_page = ((cur & HW_PAGE_MASK) + HW_PAGE_SIZE) - cur;
|
||||
if (left_this_page > len)
|
||||
left_this_page = len;
|
||||
hv_buf.len = left_this_page;
|
||||
len -= left_this_page;
|
||||
HvCall2(HvCallBaseWriteLogBuffer,
|
||||
virt_to_abs(&hv_buf),
|
||||
left_this_page);
|
||||
cur = (cur & HW_PAGE_MASK) + HW_PAGE_SIZE;
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <asm/iseries/hv_lp_config.h>
|
||||
#include "it_lp_naca.h"
|
||||
|
||||
HvLpIndex HvLpConfig_getLpIndex_outline(void)
|
||||
{
|
||||
return HvLpConfig_getLpIndex();
|
||||
}
|
||||
EXPORT_SYMBOL(HvLpConfig_getLpIndex_outline);
|
||||
|
||||
HvLpIndex HvLpConfig_getLpIndex(void)
|
||||
{
|
||||
return itLpNaca.xLpIndex;
|
||||
}
|
||||
EXPORT_SYMBOL(HvLpConfig_getLpIndex);
|
||||
|
||||
HvLpIndex HvLpConfig_getPrimaryLpIndex(void)
|
||||
{
|
||||
return itLpNaca.xPrimaryLpIndex;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(HvLpConfig_getPrimaryLpIndex);
|
|
@ -1,260 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation
|
||||
*
|
||||
* Rewrite, cleanup:
|
||||
*
|
||||
* Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation
|
||||
* Copyright (C) 2006 Olof Johansson <olof@lixom.net>
|
||||
*
|
||||
* Dynamic DMA mapping support, iSeries-specific parts.
|
||||
*
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/tce.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/iseries/hv_call_event.h>
|
||||
#include <asm/iseries/iommu.h>
|
||||
|
||||
static int tce_build_iSeries(struct iommu_table *tbl, long index, long npages,
|
||||
unsigned long uaddr, enum dma_data_direction direction,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
u64 rc;
|
||||
u64 tce, rpn;
|
||||
|
||||
while (npages--) {
|
||||
rpn = virt_to_abs(uaddr) >> TCE_SHIFT;
|
||||
tce = (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
|
||||
|
||||
if (tbl->it_type == TCE_VB) {
|
||||
/* Virtual Bus */
|
||||
tce |= TCE_VALID|TCE_ALLIO;
|
||||
if (direction != DMA_TO_DEVICE)
|
||||
tce |= TCE_VB_WRITE;
|
||||
} else {
|
||||
/* PCI Bus */
|
||||
tce |= TCE_PCI_READ; /* Read allowed */
|
||||
if (direction != DMA_TO_DEVICE)
|
||||
tce |= TCE_PCI_WRITE;
|
||||
}
|
||||
|
||||
rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, tce);
|
||||
if (rc)
|
||||
panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n",
|
||||
rc);
|
||||
index++;
|
||||
uaddr += TCE_PAGE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages)
|
||||
{
|
||||
u64 rc;
|
||||
|
||||
while (npages--) {
|
||||
rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, 0);
|
||||
if (rc)
|
||||
panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n",
|
||||
rc);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure passed to HvCallXm_getTceTableParms
|
||||
*/
|
||||
struct iommu_table_cb {
|
||||
unsigned long itc_busno; /* Bus number for this tce table */
|
||||
unsigned long itc_start; /* Will be NULL for secondary */
|
||||
unsigned long itc_totalsize; /* Size (in pages) of whole table */
|
||||
unsigned long itc_offset; /* Index into real tce table of the
|
||||
start of our section */
|
||||
unsigned long itc_size; /* Size (in pages) of our section */
|
||||
unsigned long itc_index; /* Index of this tce table */
|
||||
unsigned short itc_maxtables; /* Max num of tables for partition */
|
||||
unsigned char itc_virtbus; /* Flag to indicate virtual bus */
|
||||
unsigned char itc_slotno; /* IOA Tce Slot Index */
|
||||
unsigned char itc_rsvd[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Call Hv with the architected data structure to get TCE table info.
|
||||
* info. Put the returned data into the Linux representation of the
|
||||
* TCE table data.
|
||||
* The Hardware Tce table comes in three flavors.
|
||||
* 1. TCE table shared between Buses.
|
||||
* 2. TCE table per Bus.
|
||||
* 3. TCE Table per IOA.
|
||||
*/
|
||||
void iommu_table_getparms_iSeries(unsigned long busno,
|
||||
unsigned char slotno,
|
||||
unsigned char virtbus,
|
||||
struct iommu_table* tbl)
|
||||
{
|
||||
struct iommu_table_cb *parms;
|
||||
|
||||
parms = kzalloc(sizeof(*parms), GFP_KERNEL);
|
||||
if (parms == NULL)
|
||||
panic("PCI_DMA: TCE Table Allocation failed.");
|
||||
|
||||
parms->itc_busno = busno;
|
||||
parms->itc_slotno = slotno;
|
||||
parms->itc_virtbus = virtbus;
|
||||
|
||||
HvCallXm_getTceTableParms(iseries_hv_addr(parms));
|
||||
|
||||
if (parms->itc_size == 0)
|
||||
panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms);
|
||||
|
||||
/* itc_size is in pages worth of table, it_size is in # of entries */
|
||||
tbl->it_size = (parms->itc_size * TCE_PAGE_SIZE) / TCE_ENTRY_SIZE;
|
||||
tbl->it_busno = parms->itc_busno;
|
||||
tbl->it_offset = parms->itc_offset;
|
||||
tbl->it_index = parms->itc_index;
|
||||
tbl->it_blocksize = 1;
|
||||
tbl->it_type = virtbus ? TCE_VB : TCE_PCI;
|
||||
|
||||
kfree(parms);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/*
|
||||
* This function compares the known tables to find an iommu_table
|
||||
* that has already been built for hardware TCEs.
|
||||
*/
|
||||
static struct iommu_table *iommu_table_find(struct iommu_table * tbl)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
for (node = NULL; (node = of_find_all_nodes(node)); ) {
|
||||
struct pci_dn *pdn = PCI_DN(node);
|
||||
struct iommu_table *it;
|
||||
|
||||
if (pdn == NULL)
|
||||
continue;
|
||||
it = pdn->iommu_table;
|
||||
if ((it != NULL) &&
|
||||
(it->it_type == TCE_PCI) &&
|
||||
(it->it_offset == tbl->it_offset) &&
|
||||
(it->it_index == tbl->it_index) &&
|
||||
(it->it_size == tbl->it_size)) {
|
||||
of_node_put(node);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void pci_dma_dev_setup_iseries(struct pci_dev *pdev)
|
||||
{
|
||||
struct iommu_table *tbl;
|
||||
struct device_node *dn = pci_device_to_OF_node(pdev);
|
||||
struct pci_dn *pdn = PCI_DN(dn);
|
||||
const u32 *lsn = of_get_property(dn, "linux,logical-slot-number", NULL);
|
||||
|
||||
BUG_ON(lsn == NULL);
|
||||
|
||||
tbl = kzalloc(sizeof(struct iommu_table), GFP_KERNEL);
|
||||
|
||||
iommu_table_getparms_iSeries(pdn->busno, *lsn, 0, tbl);
|
||||
|
||||
/* Look for existing tce table */
|
||||
pdn->iommu_table = iommu_table_find(tbl);
|
||||
if (pdn->iommu_table == NULL)
|
||||
pdn->iommu_table = iommu_init_table(tbl, -1);
|
||||
else
|
||||
kfree(tbl);
|
||||
set_iommu_table_base(&pdev->dev, pdn->iommu_table);
|
||||
}
|
||||
#else
|
||||
#define pci_dma_dev_setup_iseries NULL
|
||||
#endif
|
||||
|
||||
static struct iommu_table veth_iommu_table;
|
||||
static struct iommu_table vio_iommu_table;
|
||||
|
||||
void *iseries_hv_alloc(size_t size, dma_addr_t *dma_handle, gfp_t flag)
|
||||
{
|
||||
return iommu_alloc_coherent(NULL, &vio_iommu_table, size, dma_handle,
|
||||
DMA_BIT_MASK(32), flag, -1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iseries_hv_alloc);
|
||||
|
||||
void iseries_hv_free(size_t size, void *vaddr, dma_addr_t dma_handle)
|
||||
{
|
||||
iommu_free_coherent(&vio_iommu_table, size, vaddr, dma_handle);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iseries_hv_free);
|
||||
|
||||
dma_addr_t iseries_hv_map(void *vaddr, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
return iommu_map_page(NULL, &vio_iommu_table, virt_to_page(vaddr),
|
||||
(unsigned long)vaddr % PAGE_SIZE, size,
|
||||
DMA_BIT_MASK(32), direction, NULL);
|
||||
}
|
||||
|
||||
void iseries_hv_unmap(dma_addr_t dma_handle, size_t size,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
iommu_unmap_page(&vio_iommu_table, dma_handle, size, direction, NULL);
|
||||
}
|
||||
|
||||
void __init iommu_vio_init(void)
|
||||
{
|
||||
iommu_table_getparms_iSeries(255, 0, 0xff, &veth_iommu_table);
|
||||
veth_iommu_table.it_size /= 2;
|
||||
vio_iommu_table = veth_iommu_table;
|
||||
vio_iommu_table.it_offset += veth_iommu_table.it_size;
|
||||
|
||||
if (!iommu_init_table(&veth_iommu_table, -1))
|
||||
printk("Virtual Bus VETH TCE table failed.\n");
|
||||
if (!iommu_init_table(&vio_iommu_table, -1))
|
||||
printk("Virtual Bus VIO TCE table failed.\n");
|
||||
}
|
||||
|
||||
struct iommu_table *vio_build_iommu_table_iseries(struct vio_dev *dev)
|
||||
{
|
||||
if (strcmp(dev->type, "network") == 0)
|
||||
return &veth_iommu_table;
|
||||
return &vio_iommu_table;
|
||||
}
|
||||
|
||||
void iommu_init_early_iSeries(void)
|
||||
{
|
||||
ppc_md.tce_build = tce_build_iSeries;
|
||||
ppc_md.tce_free = tce_free_iSeries;
|
||||
|
||||
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_iseries;
|
||||
set_pci_dma_ops(&dma_iommu_ops);
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* 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_IPL_PARMS_H
|
||||
#define _ISERIES_IPL_PARMS_H
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <asm/types.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 initiatedx18-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 /* _ISERIES_IPL_PARMS_H */
|
|
@ -1,400 +0,0 @@
|
|||
/*
|
||||
* This module supports the iSeries PCI bus interrupt handling
|
||||
* Copyright (C) 20yy <Robert L Holtorf> <IBM Corp>
|
||||
* Copyright (C) 2004-2005 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 13, 2000 by Wayne Holm
|
||||
* End Change Activity
|
||||
*/
|
||||
#include <linux/pci.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/irq.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <asm/paca.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "pci.h"
|
||||
#include "call_pci.h"
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
enum pci_event_type {
|
||||
pe_bus_created = 0, /* PHB has been created */
|
||||
pe_bus_error = 1, /* PHB has failed */
|
||||
pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */
|
||||
pe_node_failed = 4, /* Multi-adapter bridge has failed */
|
||||
pe_node_recovered = 5, /* Multi-adapter bridge has recovered */
|
||||
pe_bus_recovered = 12, /* PHB has been recovered */
|
||||
pe_unquiese_bus = 18, /* Secondary bus unqiescing */
|
||||
pe_bridge_error = 21, /* Bridge Error */
|
||||
pe_slot_interrupt = 22 /* Slot interrupt */
|
||||
};
|
||||
|
||||
struct pci_event {
|
||||
struct HvLpEvent event;
|
||||
union {
|
||||
u64 __align; /* Align on an 8-byte boundary */
|
||||
struct {
|
||||
u32 fisr;
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
HvAgentId dev_id;
|
||||
} slot;
|
||||
struct {
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
} bus;
|
||||
struct {
|
||||
HvBusNumber bus_number;
|
||||
HvSubBusNumber sub_bus_number;
|
||||
HvAgentId dev_id;
|
||||
} node;
|
||||
} data;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(pending_irqs_lock);
|
||||
static int num_pending_irqs;
|
||||
static int pending_irqs[NR_IRQS];
|
||||
|
||||
static void int_received(struct pci_event *event)
|
||||
{
|
||||
int irq;
|
||||
|
||||
switch (event->event.xSubtype) {
|
||||
case pe_slot_interrupt:
|
||||
irq = event->event.xCorrelationToken;
|
||||
if (irq < NR_IRQS) {
|
||||
spin_lock(&pending_irqs_lock);
|
||||
pending_irqs[irq]++;
|
||||
num_pending_irqs++;
|
||||
spin_unlock(&pending_irqs_lock);
|
||||
} else {
|
||||
printk(KERN_WARNING "int_received: bad irq number %d\n",
|
||||
irq);
|
||||
HvCallPci_eoi(event->data.slot.bus_number,
|
||||
event->data.slot.sub_bus_number,
|
||||
event->data.slot.dev_id);
|
||||
}
|
||||
break;
|
||||
/* Ignore error recovery events for now */
|
||||
case pe_bus_created:
|
||||
printk(KERN_INFO "int_received: system bus %d created\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case pe_bus_error:
|
||||
case pe_bus_failed:
|
||||
printk(KERN_INFO "int_received: system bus %d failed\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case pe_bus_recovered:
|
||||
case pe_unquiese_bus:
|
||||
printk(KERN_INFO "int_received: system bus %d recovered\n",
|
||||
event->data.bus.bus_number);
|
||||
break;
|
||||
case pe_node_failed:
|
||||
case pe_bridge_error:
|
||||
printk(KERN_INFO
|
||||
"int_received: multi-adapter bridge %d/%d/%d failed\n",
|
||||
event->data.node.bus_number,
|
||||
event->data.node.sub_bus_number,
|
||||
event->data.node.dev_id);
|
||||
break;
|
||||
case pe_node_recovered:
|
||||
printk(KERN_INFO
|
||||
"int_received: multi-adapter bridge %d/%d/%d recovered\n",
|
||||
event->data.node.bus_number,
|
||||
event->data.node.sub_bus_number,
|
||||
event->data.node.dev_id);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"int_received: unrecognized event subtype 0x%x\n",
|
||||
event->event.xSubtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_event_handler(struct HvLpEvent *event)
|
||||
{
|
||||
if (event && (event->xType == HvLpEvent_Type_PciIo)) {
|
||||
if (hvlpevent_is_int(event))
|
||||
int_received((struct pci_event *)event);
|
||||
else
|
||||
printk(KERN_ERR
|
||||
"pci_event_handler: unexpected ack received\n");
|
||||
} else if (event)
|
||||
printk(KERN_ERR
|
||||
"pci_event_handler: Unrecognized PCI event type 0x%x\n",
|
||||
(int)event->xType);
|
||||
else
|
||||
printk(KERN_ERR "pci_event_handler: NULL event received\n");
|
||||
}
|
||||
|
||||
#define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff)
|
||||
#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1)
|
||||
#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1)
|
||||
#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7)
|
||||
|
||||
/*
|
||||
* This will be called by device drivers (via enable_IRQ)
|
||||
* to enable INTA in the bridge interrupt status register.
|
||||
*/
|
||||
static void iseries_enable_IRQ(struct irq_data *d)
|
||||
{
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
/* The IRQ has already been locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Unmask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
/* This is called by iseries_activate_IRQs */
|
||||
static unsigned int iseries_startup_IRQ(struct irq_data *d)
|
||||
{
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Link the IRQ number to the bridge */
|
||||
HvCallXm_connectBusUnit(bus, sub_bus, dev_id, d->irq);
|
||||
|
||||
/* Unmask bridge interrupts in the FISR */
|
||||
mask = 0x01010000 << function;
|
||||
HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask);
|
||||
iseries_enable_IRQ(d);
|
||||
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_each_irq (irq) {
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irq_chip *chip;
|
||||
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
chip = irq_desc_get_chip(desc);
|
||||
if (chip && chip->irq_startup) {
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
chip->irq_startup(&desc->irq_data);
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* this is not called anywhere currently */
|
||||
static void iseries_shutdown_IRQ(struct irq_data *d)
|
||||
{
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
/* irq should be locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Invalidate the IRQ number in the bridge */
|
||||
HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0);
|
||||
|
||||
/* Mask bridge interrupts in the FISR */
|
||||
mask = 0x01010000 << function;
|
||||
HvCallPci_maskFisr(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* This will be called by device drivers (via disable_IRQ)
|
||||
* to disable INTA in the bridge interrupt status register.
|
||||
*/
|
||||
static void iseries_disable_IRQ(struct irq_data *d)
|
||||
{
|
||||
u32 bus, dev_id, function, mask;
|
||||
const u32 sub_bus = 0;
|
||||
unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
/* The IRQ has already been locked by the caller */
|
||||
bus = REAL_IRQ_TO_BUS(rirq);
|
||||
function = REAL_IRQ_TO_FUNC(rirq);
|
||||
dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function;
|
||||
|
||||
/* Mask secondary INTA */
|
||||
mask = 0x80000000;
|
||||
HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask);
|
||||
}
|
||||
|
||||
static void iseries_end_IRQ(struct irq_data *d)
|
||||
{
|
||||
unsigned int rirq = (unsigned int)irqd_to_hwirq(d);
|
||||
|
||||
HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq),
|
||||
(REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq));
|
||||
}
|
||||
|
||||
static struct irq_chip iseries_pic = {
|
||||
.name = "iSeries",
|
||||
.irq_startup = iseries_startup_IRQ,
|
||||
.irq_shutdown = iseries_shutdown_IRQ,
|
||||
.irq_unmask = iseries_enable_IRQ,
|
||||
.irq_mask = iseries_disable_IRQ,
|
||||
.irq_eoi = iseries_end_IRQ
|
||||
};
|
||||
|
||||
/*
|
||||
* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot
|
||||
* It calculates the irq value for the slot.
|
||||
* Note that sub_bus is always 0 (at the moment at least).
|
||||
*/
|
||||
int __init iSeries_allocate_IRQ(HvBusNumber bus,
|
||||
HvSubBusNumber sub_bus, u32 bsubbus)
|
||||
{
|
||||
unsigned int realirq;
|
||||
u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus);
|
||||
u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus);
|
||||
|
||||
realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3)
|
||||
+ function;
|
||||
|
||||
return irq_create_mapping(NULL, realirq);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
/*
|
||||
* Get the next pending IRQ.
|
||||
*/
|
||||
unsigned int iSeries_get_irq(void)
|
||||
{
|
||||
int irq = NO_IRQ_IGNORE;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (get_lppaca()->int_dword.fields.ipi_cnt) {
|
||||
get_lppaca()->int_dword.fields.ipi_cnt = 0;
|
||||
smp_ipi_demux();
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
if (hvlpevent_is_pending())
|
||||
process_hvlpevents();
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (num_pending_irqs) {
|
||||
spin_lock(&pending_irqs_lock);
|
||||
for (irq = 0; irq < NR_IRQS; irq++) {
|
||||
if (pending_irqs[irq]) {
|
||||
pending_irqs[irq]--;
|
||||
num_pending_irqs--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pending_irqs_lock);
|
||||
if (irq >= NR_IRQS)
|
||||
irq = NO_IRQ_IGNORE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
static int iseries_irq_host_map(struct irq_host *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iseries_irq_host_match(struct irq_host *h, struct device_node *np)
|
||||
{
|
||||
/* Match all */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct irq_host_ops iseries_irq_host_ops = {
|
||||
.map = iseries_irq_host_map,
|
||||
.match = iseries_irq_host_match,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c
|
||||
* It must be called before the bus walk.
|
||||
*/
|
||||
void __init iSeries_init_IRQ(void)
|
||||
{
|
||||
/* Register PCI event handler and open an event path */
|
||||
struct irq_host *host;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The Hypervisor only allows us up to 256 interrupt
|
||||
* sources (the irq number is passed in a u8).
|
||||
*/
|
||||
irq_set_virq_count(256);
|
||||
|
||||
/* Create irq host. No need for a revmap since HV will give us
|
||||
* back our virtual irq number
|
||||
*/
|
||||
host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0,
|
||||
&iseries_irq_host_ops, 0);
|
||||
BUG_ON(host == NULL);
|
||||
irq_set_default_host(host);
|
||||
|
||||
ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo,
|
||||
&pci_event_handler);
|
||||
if (ret == 0) {
|
||||
ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0);
|
||||
if (ret != 0)
|
||||
printk(KERN_ERR "iseries_init_IRQ: open event path "
|
||||
"failed with rc 0x%x\n", ret);
|
||||
} else
|
||||
printk(KERN_ERR "iseries_init_IRQ: register handler "
|
||||
"failed with rc 0x%x\n", ret);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCI */
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef _ISERIES_IRQ_H
|
||||
#define _ISERIES_IRQ_H
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
extern void iSeries_init_IRQ(void);
|
||||
extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, u32);
|
||||
extern void iSeries_activate_IRQs(void);
|
||||
#else
|
||||
#define iSeries_init_IRQ NULL
|
||||
#endif
|
||||
extern unsigned int iSeries_get_irq(void);
|
||||
|
||||
#endif /* _ISERIES_IRQ_H */
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Dave Boutcher 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 _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H
|
||||
#define _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H
|
||||
|
||||
/*
|
||||
* This struct maps the panel information
|
||||
*
|
||||
* Warning:
|
||||
* This data must match the architecture for the panel information
|
||||
*/
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
struct ItExtVpdPanel {
|
||||
/* Definition of the Extended Vpd On Panel Data Area */
|
||||
char systemSerial[8];
|
||||
char mfgID[4];
|
||||
char reserved1[24];
|
||||
char machineType[4];
|
||||
char systemID[6];
|
||||
char somUniqueCnt[4];
|
||||
char serialNumberCount;
|
||||
char reserved2[7];
|
||||
u16 bbu3;
|
||||
u16 bbu2;
|
||||
u16 bbu1;
|
||||
char xLocationLabel[8];
|
||||
u8 xRsvd1[6];
|
||||
u16 xFrameId;
|
||||
u8 xRsvd2[48];
|
||||
};
|
||||
|
||||
extern struct ItExtVpdPanel xItExtVpdPanel;
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H */
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* 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 _PLATFORMS_ISERIES_IT_LP_NACA_H
|
||||
#define _PLATFORMS_ISERIES_IT_LP_NACA_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* This control block contains the data that is shared between the
|
||||
* hypervisor (PLIC) and the OS.
|
||||
*/
|
||||
|
||||
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; // Piranha or hardware x10-x10
|
||||
u8 xPirConsoleMode; // Piranha console indicator x11-x11
|
||||
u8 xPirDasdMode; // Piranha dasd indicator x12-x12
|
||||
u8 xRsvd1_0[5]; // Reserved for Piranha related x13-x17
|
||||
u8 flags; // flags, see below x18-x1F
|
||||
u8 xSpVpdFormat; // VPD areas are in CSP format ...
|
||||
u8 xIntProcRatio; // 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 compatible OS VRM index x26-x27
|
||||
u64 xLoadAreaAddr; // ER address of load area x28-x2F
|
||||
u32 xLoadAreaChunks; // Chunks for the load area x30-x33
|
||||
u32 xPaseSysCallCRMask; // Mask used to test CR before x34-x37
|
||||
// doing an ASR switch on PASE
|
||||
// system call.
|
||||
u64 xSlicSegmentTablePtr; // Pointer to Slic seg table. x38-x3f
|
||||
u8 xRsvd1_4[64]; // x40-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 xInterruptHdlr 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
|
||||
};
|
||||
|
||||
extern struct ItLpNaca itLpNaca;
|
||||
|
||||
#define ITLPNACA_LPAR 0x80 /* Is LPAR installed on the system */
|
||||
#define ITLPNACA_PARTITIONED 0x40 /* Is the system partitioned */
|
||||
#define ITLPNACA_HWSYNCEDTBS 0x20 /* Hardware synced TBs */
|
||||
#define ITLPNACA_HMTINT 0x10 /* Utilize MHT for interrupts */
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_IT_LP_NACA_H */
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* (C) 2001-2005 PPC 64 Team, 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/export.h>
|
||||
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/iseries/hv_call_sc.h>
|
||||
|
||||
EXPORT_SYMBOL(HvCall0);
|
||||
EXPORT_SYMBOL(HvCall1);
|
||||
EXPORT_SYMBOL(HvCall2);
|
||||
EXPORT_SYMBOL(HvCall3);
|
||||
EXPORT_SYMBOL(HvCall4);
|
||||
EXPORT_SYMBOL(HvCall5);
|
||||
EXPORT_SYMBOL(HvCall6);
|
||||
EXPORT_SYMBOL(HvCall7);
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
* Copyright 2001 Mike Corrigan, 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/types.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/lppaca.h>
|
||||
#include <asm/paca.h>
|
||||
#include <asm/iseries/lpar_map.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
#include <asm/iseries/alpaca.h>
|
||||
|
||||
#include "naca.h"
|
||||
#include "vpd_areas.h"
|
||||
#include "spcomm_area.h"
|
||||
#include "ipl_parms.h"
|
||||
#include "processor_vpd.h"
|
||||
#include "release_data.h"
|
||||
#include "it_exp_vpd_panel.h"
|
||||
#include "it_lp_naca.h"
|
||||
|
||||
/* The HvReleaseData is the root of the information shared between
|
||||
* the hypervisor and Linux.
|
||||
*/
|
||||
const struct HvReleaseData hvReleaseData = {
|
||||
.xDesc = 0xc8a5d9c4, /* "HvRD" ebcdic */
|
||||
.xSize = sizeof(struct HvReleaseData),
|
||||
.xVpdAreasPtrOffset = offsetof(struct naca_struct, xItVpdAreas),
|
||||
.xSlicNacaAddr = &naca, /* 64-bit Naca address */
|
||||
.xMsNucDataOffset = LPARMAP_PHYS,
|
||||
.xFlags = HVREL_TAGSINACTIVE /* tags inactive */
|
||||
/* 64 bit */
|
||||
/* shared processors */
|
||||
/* HMT allowed */
|
||||
| 6, /* TEMP: This allows non-GA driver */
|
||||
.xVrmIndex = 4, /* We are v5r2m0 */
|
||||
.xMinSupportedPlicVrmIndex = 3, /* v5r1m0 */
|
||||
.xMinCompatablePlicVrmIndex = 3, /* v5r1m0 */
|
||||
.xVrmName = { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4.64" ebcdic */
|
||||
0xa7, 0x40, 0xf2, 0x4b,
|
||||
0xf4, 0x4b, 0xf6, 0xf4 },
|
||||
};
|
||||
|
||||
/*
|
||||
* The NACA. The first dword of the naca is required by the iSeries
|
||||
* hypervisor to point to itVpdAreas. The hypervisor finds the NACA
|
||||
* through the pointer in hvReleaseData.
|
||||
*/
|
||||
struct naca_struct naca = {
|
||||
.xItVpdAreas = &itVpdAreas,
|
||||
.xRamDisk = 0,
|
||||
.xRamDiskSize = 0,
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
extern void system_reset_iSeries(void);
|
||||
extern void machine_check_iSeries(void);
|
||||
extern void data_access_iSeries(void);
|
||||
extern void instruction_access_iSeries(void);
|
||||
extern void hardware_interrupt_iSeries(void);
|
||||
extern void alignment_iSeries(void);
|
||||
extern void program_check_iSeries(void);
|
||||
extern void fp_unavailable_iSeries(void);
|
||||
extern void decrementer_iSeries(void);
|
||||
extern void trap_0a_iSeries(void);
|
||||
extern void trap_0b_iSeries(void);
|
||||
extern void system_call_iSeries(void);
|
||||
extern void single_step_iSeries(void);
|
||||
extern void trap_0e_iSeries(void);
|
||||
extern void performance_monitor_iSeries(void);
|
||||
extern void data_access_slb_iSeries(void);
|
||||
extern void instruction_access_slb_iSeries(void);
|
||||
|
||||
struct ItLpNaca itLpNaca = {
|
||||
.xDesc = 0xd397d581, /* "LpNa" ebcdic */
|
||||
.xSize = 0x0400, /* size of ItLpNaca */
|
||||
.xIntHdlrOffset = 0x0300, /* offset to int array */
|
||||
.xMaxIntHdlrEntries = 19, /* # ents */
|
||||
.xPrimaryLpIndex = 0, /* Part # of primary */
|
||||
.xServiceLpIndex = 0, /* Part # of serv */
|
||||
.xLpIndex = 0, /* Part # of me */
|
||||
.xMaxLpQueues = 0, /* # of LP queues */
|
||||
.xLpQueueOffset = 0x100, /* offset of start of LP queues */
|
||||
.xPirEnvironMode = 0, /* Piranha stuff */
|
||||
.xPirConsoleMode = 0,
|
||||
.xPirDasdMode = 0,
|
||||
.flags = 0,
|
||||
.xSpVpdFormat = 0,
|
||||
.xIntProcRatio = 0,
|
||||
.xPlicVrmIndex = 0, /* VRM index of PLIC */
|
||||
.xMinSupportedSlicVrmInd = 0, /* min supported SLIC */
|
||||
.xMinCompatableSlicVrmInd = 0, /* min compat SLIC */
|
||||
.xLoadAreaAddr = 0, /* 64-bit addr of load area */
|
||||
.xLoadAreaChunks = 0, /* chunks for load area */
|
||||
.xPaseSysCallCRMask = 0, /* PASE mask */
|
||||
.xSlicSegmentTablePtr = 0, /* seg table */
|
||||
.xOldLpQueue = { 0 }, /* Old LP Queue */
|
||||
.xInterruptHdlr = {
|
||||
(u64)system_reset_iSeries, /* 0x100 System Reset */
|
||||
(u64)machine_check_iSeries, /* 0x200 Machine Check */
|
||||
(u64)data_access_iSeries, /* 0x300 Data Access */
|
||||
(u64)instruction_access_iSeries, /* 0x400 Instruction Access */
|
||||
(u64)hardware_interrupt_iSeries, /* 0x500 External */
|
||||
(u64)alignment_iSeries, /* 0x600 Alignment */
|
||||
(u64)program_check_iSeries, /* 0x700 Program Check */
|
||||
(u64)fp_unavailable_iSeries, /* 0x800 FP Unavailable */
|
||||
(u64)decrementer_iSeries, /* 0x900 Decrementer */
|
||||
(u64)trap_0a_iSeries, /* 0xa00 Trap 0A */
|
||||
(u64)trap_0b_iSeries, /* 0xb00 Trap 0B */
|
||||
(u64)system_call_iSeries, /* 0xc00 System Call */
|
||||
(u64)single_step_iSeries, /* 0xd00 Single Step */
|
||||
(u64)trap_0e_iSeries, /* 0xe00 Trap 0E */
|
||||
(u64)performance_monitor_iSeries,/* 0xf00 Performance Monitor */
|
||||
0, /* int 0x1000 */
|
||||
0, /* int 0x1010 */
|
||||
0, /* int 0x1020 CPU ctls */
|
||||
(u64)hardware_interrupt_iSeries, /* SC Ret Hdlr */
|
||||
(u64)data_access_slb_iSeries, /* 0x380 D-SLB */
|
||||
(u64)instruction_access_slb_iSeries /* 0x480 I-SLB */
|
||||
}
|
||||
};
|
||||
|
||||
/* May be filled in by the hypervisor so cannot end up in the BSS */
|
||||
static struct ItIplParmsReal xItIplParmsReal __attribute__((__section__(".data")));
|
||||
|
||||
/* May be filled in by the hypervisor so cannot end up in the BSS */
|
||||
struct ItExtVpdPanel xItExtVpdPanel __attribute__((__section__(".data")));
|
||||
|
||||
#define maxPhysicalProcessors 32
|
||||
|
||||
struct IoHriProcessorVpd xIoHriProcessorVpd[maxPhysicalProcessors] = {
|
||||
{
|
||||
.xInstCacheOperandSize = 32,
|
||||
.xDataCacheOperandSize = 32,
|
||||
.xProcFreq = 50000000,
|
||||
.xTimeBaseFreq = 50000000,
|
||||
.xPVR = 0x3600
|
||||
}
|
||||
};
|
||||
|
||||
/* Space for Main Store Vpd 27,200 bytes */
|
||||
/* May be filled in by the hypervisor so cannot end up in the BSS */
|
||||
u64 xMsVpd[3400] __attribute__((__section__(".data")));
|
||||
|
||||
/* Space for Recovery Log Buffer */
|
||||
/* May be filled in by the hypervisor so cannot end up in the BSS */
|
||||
static u64 xRecoveryLogBuffer[32] __attribute__((__section__(".data")));
|
||||
|
||||
static const struct SpCommArea xSpCommArea = {
|
||||
.xDesc = 0xE2D7C3C2,
|
||||
.xFormat = 1,
|
||||
};
|
||||
|
||||
static const struct ItLpRegSave iseries_reg_save[] = {
|
||||
[0 ... (NR_CPUS-1)] = {
|
||||
.xDesc = 0xd397d9e2, /* "LpRS" */
|
||||
.xSize = sizeof(struct ItLpRegSave),
|
||||
},
|
||||
};
|
||||
|
||||
#define ALPACA_INIT(number) \
|
||||
{ \
|
||||
.lppaca_ptr = &lppaca[number], \
|
||||
.reg_save_ptr = &iseries_reg_save[number], \
|
||||
}
|
||||
|
||||
const struct alpaca alpaca[] = {
|
||||
ALPACA_INIT( 0),
|
||||
#if NR_CPUS > 1
|
||||
ALPACA_INIT( 1), ALPACA_INIT( 2), ALPACA_INIT( 3),
|
||||
#if NR_CPUS > 4
|
||||
ALPACA_INIT( 4), ALPACA_INIT( 5), ALPACA_INIT( 6), ALPACA_INIT( 7),
|
||||
#if NR_CPUS > 8
|
||||
ALPACA_INIT( 8), ALPACA_INIT( 9), ALPACA_INIT(10), ALPACA_INIT(11),
|
||||
ALPACA_INIT(12), ALPACA_INIT(13), ALPACA_INIT(14), ALPACA_INIT(15),
|
||||
ALPACA_INIT(16), ALPACA_INIT(17), ALPACA_INIT(18), ALPACA_INIT(19),
|
||||
ALPACA_INIT(20), ALPACA_INIT(21), ALPACA_INIT(22), ALPACA_INIT(23),
|
||||
ALPACA_INIT(24), ALPACA_INIT(25), ALPACA_INIT(26), ALPACA_INIT(27),
|
||||
ALPACA_INIT(28), ALPACA_INIT(29), ALPACA_INIT(30), ALPACA_INIT(31),
|
||||
#if NR_CPUS > 32
|
||||
ALPACA_INIT(32), ALPACA_INIT(33), ALPACA_INIT(34), ALPACA_INIT(35),
|
||||
ALPACA_INIT(36), ALPACA_INIT(37), ALPACA_INIT(38), ALPACA_INIT(39),
|
||||
ALPACA_INIT(40), ALPACA_INIT(41), ALPACA_INIT(42), ALPACA_INIT(43),
|
||||
ALPACA_INIT(44), ALPACA_INIT(45), ALPACA_INIT(46), ALPACA_INIT(47),
|
||||
ALPACA_INIT(48), ALPACA_INIT(49), ALPACA_INIT(50), ALPACA_INIT(51),
|
||||
ALPACA_INIT(52), ALPACA_INIT(53), ALPACA_INIT(54), ALPACA_INIT(55),
|
||||
ALPACA_INIT(56), ALPACA_INIT(57), ALPACA_INIT(58), ALPACA_INIT(59),
|
||||
ALPACA_INIT(60), ALPACA_INIT(61), ALPACA_INIT(62), ALPACA_INIT(63),
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
/* The LparMap data is now located at offset 0x6000 in head.S
|
||||
* It was put there so that the HvReleaseData could address it
|
||||
* with a 32-bit offset as required by the iSeries hypervisor
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
const struct ItVpdAreas itVpdAreas = {
|
||||
.xSlicDesc = 0xc9a3e5c1, /* "ItVA" */
|
||||
.xSlicSize = sizeof(struct ItVpdAreas),
|
||||
.xSlicVpdEntries = ItVpdMaxEntries, /* # VPD array entries */
|
||||
.xSlicDmaEntries = ItDmaMaxEntries, /* # DMA array entries */
|
||||
.xSlicMaxLogicalProcs = NR_CPUS * 2, /* Max logical procs */
|
||||
.xSlicMaxPhysicalProcs = maxPhysicalProcessors, /* Max physical procs */
|
||||
.xSlicDmaToksOffset = offsetof(struct ItVpdAreas, xPlicDmaToks),
|
||||
.xSlicVpdAdrsOffset = offsetof(struct ItVpdAreas, xSlicVpdAdrs),
|
||||
.xSlicDmaLensOffset = offsetof(struct ItVpdAreas, xPlicDmaLens),
|
||||
.xSlicVpdLensOffset = offsetof(struct ItVpdAreas, xSlicVpdLens),
|
||||
.xSlicMaxSlotLabels = 0, /* max slot labels */
|
||||
.xSlicMaxLpQueues = 1, /* max LP queues */
|
||||
.xPlicDmaLens = { 0 }, /* DMA lengths */
|
||||
.xPlicDmaToks = { 0 }, /* DMA tokens */
|
||||
.xSlicVpdLens = { /* VPD lengths */
|
||||
0,0,0, /* 0 - 2 */
|
||||
sizeof(xItExtVpdPanel), /* 3 Extended VPD */
|
||||
sizeof(struct alpaca), /* 4 length of (fake) 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 hvlpevent_queue), /* 23 length of Lp Queue */
|
||||
0,0 /* 24 - 25 */
|
||||
},
|
||||
.xSlicVpdAdrs = { /* VPD addresses */
|
||||
0,0,0, /* 0 - 2 */
|
||||
&xItExtVpdPanel, /* 3 Extended VPD */
|
||||
&alpaca[0], /* 4 first (fake) Paca */
|
||||
0, /* 5 */
|
||||
&xItIplParmsReal, /* 6 IPL parms */
|
||||
&xMsVpd, /* 7 MS Vpd */
|
||||
0, /* 8 */
|
||||
&itLpNaca, /* 9 LpNaca */
|
||||
0, /* 10 */
|
||||
&xRecoveryLogBuffer, /* 11 Recovery Log Buffer */
|
||||
&xSpCommArea, /* 12 SP Comm Area */
|
||||
0,0,0, /* 13 - 15 */
|
||||
&xIoHriProcessorVpd, /* 16 Proc Vpd */
|
||||
0,0,0,0,0,0, /* 17 - 22 */
|
||||
&hvlpevent_queue, /* 23 Lp Queue */
|
||||
0,0
|
||||
}
|
||||
};
|
|
@ -1,341 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/paca.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_call_event.h>
|
||||
#include "it_lp_naca.h"
|
||||
|
||||
/*
|
||||
* The LpQueue is used to pass event data from the hypervisor to
|
||||
* the partition. This is where I/O interrupt events are communicated.
|
||||
*
|
||||
* It is written to by the hypervisor so cannot end up in the BSS.
|
||||
*/
|
||||
struct hvlpevent_queue hvlpevent_queue __attribute__((__section__(".data")));
|
||||
|
||||
DEFINE_PER_CPU(unsigned long[HvLpEvent_Type_NumTypes], hvlpevent_counts);
|
||||
|
||||
static char *event_types[HvLpEvent_Type_NumTypes] = {
|
||||
"Hypervisor",
|
||||
"Machine Facilities",
|
||||
"Session Manager",
|
||||
"SPD I/O",
|
||||
"Virtual Bus",
|
||||
"PCI I/O",
|
||||
"RIO I/O",
|
||||
"Virtual Lan",
|
||||
"Virtual I/O"
|
||||
};
|
||||
|
||||
/* Array of LpEvent handler functions */
|
||||
static LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes];
|
||||
static unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes];
|
||||
|
||||
static struct HvLpEvent * get_next_hvlpevent(void)
|
||||
{
|
||||
struct HvLpEvent * event;
|
||||
event = (struct HvLpEvent *)hvlpevent_queue.hq_current_event;
|
||||
|
||||
if (hvlpevent_is_valid(event)) {
|
||||
/* rmb() needed only for weakly consistent machines (regatta) */
|
||||
rmb();
|
||||
/* Set pointer to next potential event */
|
||||
hvlpevent_queue.hq_current_event += ((event->xSizeMinus1 +
|
||||
IT_LP_EVENT_ALIGN) / IT_LP_EVENT_ALIGN) *
|
||||
IT_LP_EVENT_ALIGN;
|
||||
|
||||
/* Wrap to beginning if no room at end */
|
||||
if (hvlpevent_queue.hq_current_event >
|
||||
hvlpevent_queue.hq_last_event) {
|
||||
hvlpevent_queue.hq_current_event =
|
||||
hvlpevent_queue.hq_event_stack;
|
||||
}
|
||||
} else {
|
||||
event = NULL;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
static unsigned long spread_lpevents = NR_CPUS;
|
||||
|
||||
int hvlpevent_is_pending(void)
|
||||
{
|
||||
struct HvLpEvent *next_event;
|
||||
|
||||
if (smp_processor_id() >= spread_lpevents)
|
||||
return 0;
|
||||
|
||||
next_event = (struct HvLpEvent *)hvlpevent_queue.hq_current_event;
|
||||
|
||||
return hvlpevent_is_valid(next_event) ||
|
||||
hvlpevent_queue.hq_overflow_pending;
|
||||
}
|
||||
|
||||
static void hvlpevent_clear_valid(struct HvLpEvent * event)
|
||||
{
|
||||
/* Tell the Hypervisor that we're done with this event.
|
||||
* Also clear bits within this event that might look like valid bits.
|
||||
* ie. on 64-byte boundaries.
|
||||
*/
|
||||
struct HvLpEvent *tmp;
|
||||
unsigned extra = ((event->xSizeMinus1 + IT_LP_EVENT_ALIGN) /
|
||||
IT_LP_EVENT_ALIGN) - 1;
|
||||
|
||||
switch (extra) {
|
||||
case 3:
|
||||
tmp = (struct HvLpEvent*)((char*)event + 3 * IT_LP_EVENT_ALIGN);
|
||||
hvlpevent_invalidate(tmp);
|
||||
case 2:
|
||||
tmp = (struct HvLpEvent*)((char*)event + 2 * IT_LP_EVENT_ALIGN);
|
||||
hvlpevent_invalidate(tmp);
|
||||
case 1:
|
||||
tmp = (struct HvLpEvent*)((char*)event + 1 * IT_LP_EVENT_ALIGN);
|
||||
hvlpevent_invalidate(tmp);
|
||||
}
|
||||
|
||||
mb();
|
||||
|
||||
hvlpevent_invalidate(event);
|
||||
}
|
||||
|
||||
void process_hvlpevents(void)
|
||||
{
|
||||
struct HvLpEvent * event;
|
||||
|
||||
restart:
|
||||
/* If we have recursed, just return */
|
||||
if (!spin_trylock(&hvlpevent_queue.hq_lock))
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
event = get_next_hvlpevent();
|
||||
if (event) {
|
||||
/* 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 (event->xType < HvLpEvent_Type_NumTypes)
|
||||
__get_cpu_var(hvlpevent_counts)[event->xType]++;
|
||||
if (event->xType < HvLpEvent_Type_NumTypes &&
|
||||
lpEventHandler[event->xType])
|
||||
lpEventHandler[event->xType](event);
|
||||
else {
|
||||
u8 type = event->xType;
|
||||
|
||||
/*
|
||||
* Don't printk in the spinlock as printk
|
||||
* may require ack events form the HV to send
|
||||
* any characters there.
|
||||
*/
|
||||
hvlpevent_clear_valid(event);
|
||||
spin_unlock(&hvlpevent_queue.hq_lock);
|
||||
printk(KERN_INFO
|
||||
"Unexpected Lp Event type=%d\n", type);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
hvlpevent_clear_valid(event);
|
||||
} else if (hvlpevent_queue.hq_overflow_pending)
|
||||
/*
|
||||
* No more valid events. If overflow events are
|
||||
* pending process them
|
||||
*/
|
||||
HvCallEvent_getOverflowLpEvents(hvlpevent_queue.hq_index);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&hvlpevent_queue.hq_lock);
|
||||
}
|
||||
|
||||
static int set_spread_lpevents(char *str)
|
||||
{
|
||||
unsigned long val = simple_strtoul(str, NULL, 0);
|
||||
|
||||
/*
|
||||
* The parameter is the number of processors to share in processing
|
||||
* lp events.
|
||||
*/
|
||||
if (( val > 0) && (val <= NR_CPUS)) {
|
||||
spread_lpevents = val;
|
||||
printk("lpevent processing spread over %ld processors\n", val);
|
||||
} else {
|
||||
printk("invalid spread_lpevents %ld\n", val);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("spread_lpevents=", set_spread_lpevents);
|
||||
|
||||
void __init setup_hvlpevent_queue(void)
|
||||
{
|
||||
void *eventStack;
|
||||
|
||||
spin_lock_init(&hvlpevent_queue.hq_lock);
|
||||
|
||||
/* Allocate a page for the Event Stack. */
|
||||
eventStack = alloc_bootmem_pages(IT_LP_EVENT_STACK_SIZE);
|
||||
memset(eventStack, 0, IT_LP_EVENT_STACK_SIZE);
|
||||
|
||||
/* Invoke the hypervisor to initialize the event stack */
|
||||
HvCallEvent_setLpEventStack(0, eventStack, IT_LP_EVENT_STACK_SIZE);
|
||||
|
||||
hvlpevent_queue.hq_event_stack = eventStack;
|
||||
hvlpevent_queue.hq_current_event = eventStack;
|
||||
hvlpevent_queue.hq_last_event = (char *)eventStack +
|
||||
(IT_LP_EVENT_STACK_SIZE - IT_LP_EVENT_MAX_SIZE);
|
||||
hvlpevent_queue.hq_index = 0;
|
||||
}
|
||||
|
||||
/* Register a handler for an LpEvent type */
|
||||
int HvLpEvent_registerHandler(HvLpEvent_Type eventType, LpEventHandler handler)
|
||||
{
|
||||
if (eventType < HvLpEvent_Type_NumTypes) {
|
||||
lpEventHandler[eventType] = handler;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(HvLpEvent_registerHandler);
|
||||
|
||||
int HvLpEvent_unregisterHandler(HvLpEvent_Type eventType)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
if (eventType < HvLpEvent_Type_NumTypes) {
|
||||
if (!lpEventHandlerPaths[eventType]) {
|
||||
lpEventHandler[eventType] = NULL;
|
||||
/*
|
||||
* We now sleep until all other CPUs have scheduled.
|
||||
* This ensures that the deletion is seen by all
|
||||
* other CPUs, and that the deleted handler isn't
|
||||
* still running on another CPU when we return.
|
||||
*/
|
||||
synchronize_sched();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(HvLpEvent_unregisterHandler);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
if ((eventType < HvLpEvent_Type_NumTypes) &&
|
||||
lpEventHandler[eventType]) {
|
||||
if (lpIndex == 0)
|
||||
lpIndex = itLpNaca.xLpIndex;
|
||||
HvCallEvent_openLpEventPath(lpIndex, eventType);
|
||||
++lpEventHandlerPaths[eventType];
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int HvLpEvent_closePath(HvLpEvent_Type eventType, HvLpIndex lpIndex)
|
||||
{
|
||||
if ((eventType < HvLpEvent_Type_NumTypes) &&
|
||||
lpEventHandler[eventType] &&
|
||||
lpEventHandlerPaths[eventType]) {
|
||||
if (lpIndex == 0)
|
||||
lpIndex = itLpNaca.xLpIndex;
|
||||
HvCallEvent_closeLpEventPath(lpIndex, eventType);
|
||||
--lpEventHandlerPaths[eventType];
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int proc_lpevents_show(struct seq_file *m, void *v)
|
||||
{
|
||||
int cpu, i;
|
||||
unsigned long sum;
|
||||
static unsigned long cpu_totals[NR_CPUS];
|
||||
|
||||
/* FIXME: do we care that there's no locking here? */
|
||||
sum = 0;
|
||||
for_each_online_cpu(cpu) {
|
||||
cpu_totals[cpu] = 0;
|
||||
for (i = 0; i < HvLpEvent_Type_NumTypes; i++) {
|
||||
cpu_totals[cpu] += per_cpu(hvlpevent_counts, cpu)[i];
|
||||
}
|
||||
sum += cpu_totals[cpu];
|
||||
}
|
||||
|
||||
seq_printf(m, "LpEventQueue 0\n");
|
||||
seq_printf(m, " events processed:\t%lu\n", sum);
|
||||
|
||||
for (i = 0; i < HvLpEvent_Type_NumTypes; ++i) {
|
||||
sum = 0;
|
||||
for_each_online_cpu(cpu) {
|
||||
sum += per_cpu(hvlpevent_counts, cpu)[i];
|
||||
}
|
||||
|
||||
seq_printf(m, " %-20s %10lu\n", event_types[i], sum);
|
||||
}
|
||||
|
||||
seq_printf(m, "\n events processed by processor:\n");
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
seq_printf(m, " CPU%02d %10lu\n", cpu, cpu_totals[cpu]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_lpevents_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, proc_lpevents_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_lpevents_operations = {
|
||||
.open = proc_lpevents_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init proc_lpevents_init(void)
|
||||
{
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
return 0;
|
||||
|
||||
proc_create("iSeries/lpevents", S_IFREG|S_IRUGO, NULL,
|
||||
&proc_lpevents_operations);
|
||||
return 0;
|
||||
}
|
||||
__initcall(proc_lpevents_init);
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* 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_MAIN_STORE_H
|
||||
#define _ISERIES_MAIN_STORE_H
|
||||
|
||||
/* Main Store Vpd for Condor,iStar,sStar */
|
||||
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;
|
||||
};
|
||||
|
||||
/* Main Store VPD for Power4 */
|
||||
struct __attribute((packed)) IoHriMainStoreChipInfo1 {
|
||||
u32 chipMfgID;
|
||||
char chipECLevel[4];
|
||||
};
|
||||
|
||||
struct IoHriMainStoreVpdIdData {
|
||||
char typeNumber[4];
|
||||
char modelNumber[4];
|
||||
char partNumber[12];
|
||||
char serialNumber[12];
|
||||
};
|
||||
|
||||
struct __attribute((packed)) IoHriMainStoreVpdFruData {
|
||||
char fruLabel[8];
|
||||
u8 numberOfSlots;
|
||||
u8 pluggingType;
|
||||
u16 slotMapIndex;
|
||||
};
|
||||
|
||||
struct __attribute((packed)) IoHriMainStoreAdrRangeBlock {
|
||||
void *blockStart;
|
||||
void *blockEnd;
|
||||
u32 blockProcChipId;
|
||||
};
|
||||
|
||||
#define MaxAreaAdrRangeBlocks 4
|
||||
|
||||
struct __attribute((packed)) IoHriMainStoreArea4 {
|
||||
u32 msVpdFormat;
|
||||
u8 containedVpdType;
|
||||
u8 reserved1;
|
||||
u16 reserved2;
|
||||
|
||||
u64 msExists;
|
||||
u64 msFunctional;
|
||||
|
||||
u32 memorySize;
|
||||
u32 procNodeId;
|
||||
|
||||
u32 numAdrRangeBlocks;
|
||||
struct IoHriMainStoreAdrRangeBlock xAdrRangeBlock[MaxAreaAdrRangeBlocks];
|
||||
|
||||
struct IoHriMainStoreChipInfo1 chipInfo0;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo1;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo2;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo3;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo4;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo5;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo6;
|
||||
struct IoHriMainStoreChipInfo1 chipInfo7;
|
||||
|
||||
void *msRamAreaArray;
|
||||
u32 msRamAreaArrayNumEntries;
|
||||
u32 msRamAreaArrayEntrySize;
|
||||
|
||||
u32 numaDimmExists;
|
||||
u32 numaDimmFunctional;
|
||||
void *numaDimmArray;
|
||||
u32 numaDimmArrayNumEntries;
|
||||
u32 numaDimmArrayEntrySize;
|
||||
|
||||
struct IoHriMainStoreVpdIdData idData;
|
||||
|
||||
u64 powerData;
|
||||
u64 cardAssemblyPartNum;
|
||||
u64 chipSerialNum;
|
||||
|
||||
u64 reserved3;
|
||||
char reserved4[16];
|
||||
|
||||
struct IoHriMainStoreVpdFruData fruData;
|
||||
|
||||
u8 vpdPortNum;
|
||||
u8 reserved5;
|
||||
u8 frameId;
|
||||
u8 rackUnit;
|
||||
char asciiKeywordVpd[256];
|
||||
u32 reserved6;
|
||||
};
|
||||
|
||||
|
||||
struct IoHriMainStoreSegment5 {
|
||||
u16 reserved1;
|
||||
u8 reserved2;
|
||||
u8 msVpdFormat;
|
||||
|
||||
u32 totalMainStore;
|
||||
u64 maxConfiguredMsAdr;
|
||||
|
||||
struct IoHriMainStoreArea4 *msAreaArray;
|
||||
u32 msAreaArrayNumEntries;
|
||||
u32 msAreaArrayEntrySize;
|
||||
|
||||
u32 msAreaExists;
|
||||
u32 msAreaFunctional;
|
||||
|
||||
u64 reserved3;
|
||||
};
|
||||
|
||||
extern u64 xMsVpd[];
|
||||
|
||||
#endif /* _ISERIES_MAIN_STORE_H */
|
File diff suppressed because it is too large
Load diff
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* This file contains miscellaneous low-level functions.
|
||||
* Copyright (C) 1995-2005 IBM Corp
|
||||
*
|
||||
* Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
|
||||
* and Paul Mackerras.
|
||||
* Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com)
|
||||
* PPC64 updates by Dave Engebretsen (engebret@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 <asm/processor.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/ppc_asm.h>
|
||||
|
||||
.text
|
||||
|
||||
/* Handle pending interrupts in interrupt context */
|
||||
_GLOBAL(iseries_handle_interrupts)
|
||||
li r0,0x5555
|
||||
sc
|
||||
blr
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef _PLATFORMS_ISERIES_NACA_H
|
||||
#define _PLATFORMS_ISERIES_NACA_H
|
||||
|
||||
/*
|
||||
* c 2001 PPC 64 Team, 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 <asm/types.h>
|
||||
|
||||
struct naca_struct {
|
||||
/* Kernel only data - undefined for user space */
|
||||
const void *xItVpdAreas; /* VPD Data 0x00 */
|
||||
void *xRamDisk; /* iSeries ramdisk 0x08 */
|
||||
u64 xRamDiskSize; /* In pages 0x10 */
|
||||
};
|
||||
|
||||
extern struct naca_struct naca;
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_NACA_H */
|
|
@ -1,919 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Allan Trautman, IBM Corporation
|
||||
* Copyright (C) 2005,2007 Stephen Rothwell, IBM Corp
|
||||
*
|
||||
* iSeries specific routines for PCI.
|
||||
*
|
||||
* Based on code from pci.c and iSeries_pci.c 32bit
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/firmware.h>
|
||||
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/iseries/mf.h>
|
||||
#include <asm/iseries/iommu.h>
|
||||
|
||||
#include <asm/ppc-pci.h>
|
||||
|
||||
#include "irq.h"
|
||||
#include "pci.h"
|
||||
#include "call_pci.h"
|
||||
|
||||
#define PCI_RETRY_MAX 3
|
||||
static int limit_pci_retries = 1; /* Set Retry Error on. */
|
||||
|
||||
/*
|
||||
* Table defines
|
||||
* Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space.
|
||||
*/
|
||||
#define IOMM_TABLE_MAX_ENTRIES 1024
|
||||
#define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL
|
||||
#define BASE_IO_MEMORY 0xE000000000000000UL
|
||||
#define END_IO_MEMORY 0xEFFFFFFFFFFFFFFFUL
|
||||
|
||||
static unsigned long max_io_memory = BASE_IO_MEMORY;
|
||||
static long current_iomm_table_entry;
|
||||
|
||||
/*
|
||||
* Lookup Tables.
|
||||
*/
|
||||
static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES];
|
||||
static u64 ds_addr_table[IOMM_TABLE_MAX_ENTRIES];
|
||||
|
||||
static DEFINE_SPINLOCK(iomm_table_lock);
|
||||
|
||||
/*
|
||||
* Generate a Direct Select Address for the Hypervisor
|
||||
*/
|
||||
static inline u64 iseries_ds_addr(struct device_node *node)
|
||||
{
|
||||
struct pci_dn *pdn = PCI_DN(node);
|
||||
const u32 *sbp = of_get_property(node, "linux,subbus", NULL);
|
||||
|
||||
return ((u64)pdn->busno << 48) + ((u64)(sbp ? *sbp : 0) << 40)
|
||||
+ ((u64)0x10 << 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Size of Bus VPD data
|
||||
*/
|
||||
#define BUS_VPDSIZE 1024
|
||||
|
||||
/*
|
||||
* Bus Vpd Tags
|
||||
*/
|
||||
#define VPD_END_OF_AREA 0x79
|
||||
#define VPD_ID_STRING 0x82
|
||||
#define VPD_VENDOR_AREA 0x84
|
||||
|
||||
/*
|
||||
* Mfg Area Tags
|
||||
*/
|
||||
#define VPD_FRU_FRAME_ID 0x4649 /* "FI" */
|
||||
#define VPD_SLOT_MAP_FORMAT 0x4D46 /* "MF" */
|
||||
#define VPD_SLOT_MAP 0x534D /* "SM" */
|
||||
|
||||
/*
|
||||
* Structures of the areas
|
||||
*/
|
||||
struct mfg_vpd_area {
|
||||
u16 tag;
|
||||
u8 length;
|
||||
u8 data1;
|
||||
u8 data2;
|
||||
};
|
||||
#define MFG_ENTRY_SIZE 3
|
||||
|
||||
struct slot_map {
|
||||
u8 agent;
|
||||
u8 secondary_agent;
|
||||
u8 phb;
|
||||
char card_location[3];
|
||||
char parms[8];
|
||||
char reserved[2];
|
||||
};
|
||||
#define SLOT_ENTRY_SIZE 16
|
||||
|
||||
/*
|
||||
* Parse the Slot Area
|
||||
*/
|
||||
static void __init iseries_parse_slot_area(struct slot_map *map, int len,
|
||||
HvAgentId agent, u8 *phb, char card[4])
|
||||
{
|
||||
/*
|
||||
* Parse Slot label until we find the one requested
|
||||
*/
|
||||
while (len > 0) {
|
||||
if (map->agent == agent) {
|
||||
/*
|
||||
* If Phb wasn't found, grab the entry first one found.
|
||||
*/
|
||||
if (*phb == 0xff)
|
||||
*phb = map->phb;
|
||||
/* Found it, extract the data. */
|
||||
if (map->phb == *phb) {
|
||||
memcpy(card, &map->card_location, 3);
|
||||
card[3] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Point to the next Slot */
|
||||
map = (struct slot_map *)((char *)map + SLOT_ENTRY_SIZE);
|
||||
len -= SLOT_ENTRY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the Mfg Area
|
||||
*/
|
||||
static void __init iseries_parse_mfg_area(struct mfg_vpd_area *area, int len,
|
||||
HvAgentId agent, u8 *phb, u8 *frame, char card[4])
|
||||
{
|
||||
u16 slot_map_fmt = 0;
|
||||
|
||||
/* Parse Mfg Data */
|
||||
while (len > 0) {
|
||||
int mfg_tag_len = area->length;
|
||||
/* Frame ID (FI 4649020310 ) */
|
||||
if (area->tag == VPD_FRU_FRAME_ID)
|
||||
*frame = area->data1;
|
||||
/* Slot Map Format (MF 4D46020004 ) */
|
||||
else if (area->tag == VPD_SLOT_MAP_FORMAT)
|
||||
slot_map_fmt = (area->data1 * 256)
|
||||
+ area->data2;
|
||||
/* Slot Map (SM 534D90 */
|
||||
else if (area->tag == VPD_SLOT_MAP) {
|
||||
struct slot_map *slot_map;
|
||||
|
||||
if (slot_map_fmt == 0x1004)
|
||||
slot_map = (struct slot_map *)((char *)area
|
||||
+ MFG_ENTRY_SIZE + 1);
|
||||
else
|
||||
slot_map = (struct slot_map *)((char *)area
|
||||
+ MFG_ENTRY_SIZE);
|
||||
iseries_parse_slot_area(slot_map, mfg_tag_len,
|
||||
agent, phb, card);
|
||||
}
|
||||
/*
|
||||
* Point to the next Mfg Area
|
||||
* Use defined size, sizeof give wrong answer
|
||||
*/
|
||||
area = (struct mfg_vpd_area *)((char *)area + mfg_tag_len
|
||||
+ MFG_ENTRY_SIZE);
|
||||
len -= (mfg_tag_len + MFG_ENTRY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for "BUS".. Data is not Null terminated.
|
||||
* PHBID of 0xFF indicates PHB was not found in VPD Data.
|
||||
*/
|
||||
static u8 __init iseries_parse_phbid(u8 *area, int len)
|
||||
{
|
||||
while (len > 0) {
|
||||
if ((*area == 'B') && (*(area + 1) == 'U')
|
||||
&& (*(area + 2) == 'S')) {
|
||||
area += 3;
|
||||
while (*area == ' ')
|
||||
area++;
|
||||
return *area & 0x0F;
|
||||
}
|
||||
area++;
|
||||
len--;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse out the VPD Areas
|
||||
*/
|
||||
static void __init iseries_parse_vpd(u8 *data, int data_len,
|
||||
HvAgentId agent, u8 *frame, char card[4])
|
||||
{
|
||||
u8 phb = 0xff;
|
||||
|
||||
while (data_len > 0) {
|
||||
int len;
|
||||
u8 tag = *data;
|
||||
|
||||
if (tag == VPD_END_OF_AREA)
|
||||
break;
|
||||
len = *(data + 1) + (*(data + 2) * 256);
|
||||
data += 3;
|
||||
data_len -= 3;
|
||||
if (tag == VPD_ID_STRING)
|
||||
phb = iseries_parse_phbid(data, len);
|
||||
else if (tag == VPD_VENDOR_AREA)
|
||||
iseries_parse_mfg_area((struct mfg_vpd_area *)data, len,
|
||||
agent, &phb, frame, card);
|
||||
/* Point to next Area. */
|
||||
data += len;
|
||||
data_len -= len;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init iseries_get_location_code(u16 bus, HvAgentId agent,
|
||||
u8 *frame, char card[4])
|
||||
{
|
||||
int status = 0;
|
||||
int bus_vpd_len = 0;
|
||||
u8 *bus_vpd = kmalloc(BUS_VPDSIZE, GFP_KERNEL);
|
||||
|
||||
if (bus_vpd == NULL) {
|
||||
printk("PCI: Bus VPD Buffer allocation failure.\n");
|
||||
return 0;
|
||||
}
|
||||
bus_vpd_len = HvCallPci_getBusVpd(bus, iseries_hv_addr(bus_vpd),
|
||||
BUS_VPDSIZE);
|
||||
if (bus_vpd_len == 0) {
|
||||
printk("PCI: Bus VPD Buffer zero length.\n");
|
||||
goto out_free;
|
||||
}
|
||||
/* printk("PCI: bus_vpd: %p, %d\n",bus_vpd, bus_vpd_len); */
|
||||
/* Make sure this is what I think it is */
|
||||
if (*bus_vpd != VPD_ID_STRING) {
|
||||
printk("PCI: Bus VPD Buffer missing starting tag.\n");
|
||||
goto out_free;
|
||||
}
|
||||
iseries_parse_vpd(bus_vpd, bus_vpd_len, agent, frame, card);
|
||||
status = 1;
|
||||
out_free:
|
||||
kfree(bus_vpd);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints the device information.
|
||||
* - Pass in pci_dev* pointer to the device.
|
||||
* - Pass in the device count
|
||||
*
|
||||
* Format:
|
||||
* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet
|
||||
* controller
|
||||
*/
|
||||
static void __init iseries_device_information(struct pci_dev *pdev,
|
||||
u16 bus, HvSubBusNumber subbus)
|
||||
{
|
||||
u8 frame = 0;
|
||||
char card[4];
|
||||
HvAgentId agent;
|
||||
|
||||
agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus),
|
||||
ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus));
|
||||
|
||||
if (iseries_get_location_code(bus, agent, &frame, card)) {
|
||||
printk(KERN_INFO "PCI: %s, Vendor %04X Frame%3d, "
|
||||
"Card %4s 0x%04X\n", pci_name(pdev), pdev->vendor,
|
||||
frame, card, (int)(pdev->class >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* iomm_table_allocate_entry
|
||||
*
|
||||
* Adds pci_dev entry in address translation table
|
||||
*
|
||||
* - Allocates the number of entries required in table base on BAR
|
||||
* size.
|
||||
* - Allocates starting at BASE_IO_MEMORY and increases.
|
||||
* - The size is round up to be a multiple of entry size.
|
||||
* - CurrentIndex is incremented to keep track of the last entry.
|
||||
* - Builds the resource entry for allocated BARs.
|
||||
*/
|
||||
static void __init iomm_table_allocate_entry(struct pci_dev *dev, int bar_num)
|
||||
{
|
||||
struct resource *bar_res = &dev->resource[bar_num];
|
||||
long bar_size = pci_resource_len(dev, bar_num);
|
||||
struct device_node *dn = pci_device_to_OF_node(dev);
|
||||
|
||||
/*
|
||||
* No space to allocate, quick exit, skip Allocation.
|
||||
*/
|
||||
if (bar_size == 0)
|
||||
return;
|
||||
/*
|
||||
* Set Resource values.
|
||||
*/
|
||||
spin_lock(&iomm_table_lock);
|
||||
bar_res->start = BASE_IO_MEMORY +
|
||||
IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry;
|
||||
bar_res->end = bar_res->start + bar_size - 1;
|
||||
/*
|
||||
* Allocate the number of table entries needed for BAR.
|
||||
*/
|
||||
while (bar_size > 0 ) {
|
||||
iomm_table[current_iomm_table_entry] = dn;
|
||||
ds_addr_table[current_iomm_table_entry] =
|
||||
iseries_ds_addr(dn) | (bar_num << 24);
|
||||
bar_size -= IOMM_TABLE_ENTRY_SIZE;
|
||||
++current_iomm_table_entry;
|
||||
}
|
||||
max_io_memory = BASE_IO_MEMORY +
|
||||
IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry;
|
||||
spin_unlock(&iomm_table_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate_device_bars
|
||||
*
|
||||
* - 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 iomm_table_allocate_entry to allocate
|
||||
* each entry.
|
||||
* - Loops through The Bar resources(0 - 5) including the ROM
|
||||
* is resource(6).
|
||||
*/
|
||||
static void __init allocate_device_bars(struct pci_dev *dev)
|
||||
{
|
||||
int bar_num;
|
||||
|
||||
for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num)
|
||||
iomm_table_allocate_entry(dev, bar_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log error information to system console.
|
||||
* Filter out the device not there errors.
|
||||
* PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx
|
||||
* PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx
|
||||
* PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx
|
||||
*/
|
||||
static void pci_log_error(char *error, int bus, int subbus,
|
||||
int agent, int hv_res)
|
||||
{
|
||||
if (hv_res == 0x0302)
|
||||
return;
|
||||
printk(KERN_ERR "PCI: %s Failed: 0x%02X.%02X.%02X Rc: 0x%04X",
|
||||
error, bus, subbus, agent, hv_res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look down the chain to find the matching Device Device
|
||||
*/
|
||||
static struct device_node *find_device_node(int bus, int devfn)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
for (node = NULL; (node = of_find_all_nodes(node)); ) {
|
||||
struct pci_dn *pdn = PCI_DN(node);
|
||||
|
||||
if (pdn && (bus == pdn->busno) && (devfn == pdn->devfn))
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* iSeries_pcibios_fixup_resources
|
||||
*
|
||||
* Fixes up all resources for devices
|
||||
*/
|
||||
void __init iSeries_pcibios_fixup_resources(struct pci_dev *pdev)
|
||||
{
|
||||
const u32 *agent;
|
||||
const u32 *sub_bus;
|
||||
unsigned char bus = pdev->bus->number;
|
||||
struct device_node *node;
|
||||
int i;
|
||||
|
||||
node = pci_device_to_OF_node(pdev);
|
||||
pr_debug("PCI: iSeries %s, pdev %p, node %p\n",
|
||||
pci_name(pdev), pdev, node);
|
||||
if (!node) {
|
||||
printk("PCI: %s disabled, device tree entry not found !\n",
|
||||
pci_name(pdev));
|
||||
for (i = 0; i <= PCI_ROM_RESOURCE; i++)
|
||||
pdev->resource[i].flags = 0;
|
||||
return;
|
||||
}
|
||||
sub_bus = of_get_property(node, "linux,subbus", NULL);
|
||||
agent = of_get_property(node, "linux,agent-id", NULL);
|
||||
if (agent && sub_bus) {
|
||||
u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus);
|
||||
int err;
|
||||
|
||||
err = HvCallXm_connectBusUnit(bus, *sub_bus, *agent, irq);
|
||||
if (err)
|
||||
pci_log_error("Connect Bus Unit",
|
||||
bus, *sub_bus, *agent, err);
|
||||
else {
|
||||
err = HvCallPci_configStore8(bus, *sub_bus,
|
||||
*agent, PCI_INTERRUPT_LINE, irq);
|
||||
if (err)
|
||||
pci_log_error("PciCfgStore Irq Failed!",
|
||||
bus, *sub_bus, *agent, err);
|
||||
else
|
||||
pdev->irq = irq;
|
||||
}
|
||||
}
|
||||
|
||||
allocate_device_bars(pdev);
|
||||
if (likely(sub_bus))
|
||||
iseries_device_information(pdev, bus, *sub_bus);
|
||||
else
|
||||
printk(KERN_ERR "PCI: Device node %s has missing or invalid "
|
||||
"linux,subbus property\n", node->full_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* iSeries_pci_final_fixup(void)
|
||||
*/
|
||||
void __init iSeries_pci_final_fixup(void)
|
||||
{
|
||||
/* Fix up at the device node and pci_dev relationship */
|
||||
mf_display_src(0xC9000100);
|
||||
iSeries_activate_IRQs();
|
||||
mf_display_src(0xC9000200);
|
||||
}
|
||||
|
||||
/*
|
||||
* Config space read and write functions.
|
||||
* For now at least, we look for the device node for the bus and devfn
|
||||
* that we are asked to access. It may be possible to translate the devfn
|
||||
* to a subbus and deviceid more directly.
|
||||
*/
|
||||
static u64 hv_cfg_read_func[4] = {
|
||||
HvCallPciConfigLoad8, HvCallPciConfigLoad16,
|
||||
HvCallPciConfigLoad32, HvCallPciConfigLoad32
|
||||
};
|
||||
|
||||
static u64 hv_cfg_write_func[4] = {
|
||||
HvCallPciConfigStore8, HvCallPciConfigStore16,
|
||||
HvCallPciConfigStore32, HvCallPciConfigStore32
|
||||
};
|
||||
|
||||
/*
|
||||
* Read PCI config space
|
||||
*/
|
||||
static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int offset, int size, u32 *val)
|
||||
{
|
||||
struct device_node *node = find_device_node(bus->number, devfn);
|
||||
u64 fn;
|
||||
struct HvCallPci_LoadReturn ret;
|
||||
|
||||
if (node == NULL)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (offset > 255) {
|
||||
*val = ~0;
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
}
|
||||
|
||||
fn = hv_cfg_read_func[(size - 1) & 3];
|
||||
HvCall3Ret16(fn, &ret, iseries_ds_addr(node), offset, 0);
|
||||
|
||||
if (ret.rc != 0) {
|
||||
*val = ~0;
|
||||
return PCIBIOS_DEVICE_NOT_FOUND; /* or something */
|
||||
}
|
||||
|
||||
*val = ret.value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write PCI config space
|
||||
*/
|
||||
|
||||
static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn,
|
||||
int offset, int size, u32 val)
|
||||
{
|
||||
struct device_node *node = find_device_node(bus->number, devfn);
|
||||
u64 fn;
|
||||
u64 ret;
|
||||
|
||||
if (node == NULL)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (offset > 255)
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
fn = hv_cfg_write_func[(size - 1) & 3];
|
||||
ret = HvCall4(fn, iseries_ds_addr(node), offset, val, 0);
|
||||
|
||||
if (ret != 0)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_ops iSeries_pci_ops = {
|
||||
.read = iSeries_pci_read_config,
|
||||
.write = iSeries_pci_write_config
|
||||
};
|
||||
|
||||
/*
|
||||
* Check Return Code
|
||||
* -> On Failure, print and log information.
|
||||
* Increment Retry Count, if exceeds max, panic partition.
|
||||
*
|
||||
* PCI: Device 23.90 ReadL I/O Error( 0): 0x1234
|
||||
* PCI: Device 23.90 ReadL Retry( 1)
|
||||
* PCI: Device 23.90 ReadL Retry Successful(1)
|
||||
*/
|
||||
static int check_return_code(char *type, struct device_node *dn,
|
||||
int *retry, u64 ret)
|
||||
{
|
||||
if (ret != 0) {
|
||||
struct pci_dn *pdn = PCI_DN(dn);
|
||||
|
||||
(*retry)++;
|
||||
printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n",
|
||||
type, pdn->busno, pdn->devfn,
|
||||
*retry, (int)ret);
|
||||
/*
|
||||
* Bump the retry and check for retry count exceeded.
|
||||
* If, Exceeded, panic the system.
|
||||
*/
|
||||
if (((*retry) > PCI_RETRY_MAX) &&
|
||||
(limit_pci_retries > 0)) {
|
||||
mf_display_src(0xB6000103);
|
||||
panic_timeout = 0;
|
||||
panic("PCI: Hardware I/O Error, SRC B6000103, "
|
||||
"Automatic Reboot Disabled.\n");
|
||||
}
|
||||
return -1; /* Retry Try */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate the I/O Address into a device node, bar, and bar offset.
|
||||
* Note: Make sure the passed variable end up on the stack to avoid
|
||||
* the exposure of being device global.
|
||||
*/
|
||||
static inline struct device_node *xlate_iomm_address(
|
||||
const volatile void __iomem *addr,
|
||||
u64 *dsaptr, u64 *bar_offset, const char *func)
|
||||
{
|
||||
unsigned long orig_addr;
|
||||
unsigned long base_addr;
|
||||
unsigned long ind;
|
||||
struct device_node *dn;
|
||||
|
||||
orig_addr = (unsigned long __force)addr;
|
||||
if ((orig_addr < BASE_IO_MEMORY) || (orig_addr >= max_io_memory)) {
|
||||
static DEFINE_RATELIMIT_STATE(ratelimit, 60 * HZ, 10);
|
||||
|
||||
if (__ratelimit(&ratelimit))
|
||||
printk(KERN_ERR
|
||||
"iSeries_%s: invalid access at IO address %p\n",
|
||||
func, addr);
|
||||
return NULL;
|
||||
}
|
||||
base_addr = orig_addr - BASE_IO_MEMORY;
|
||||
ind = base_addr / IOMM_TABLE_ENTRY_SIZE;
|
||||
dn = iomm_table[ind];
|
||||
|
||||
if (dn != NULL) {
|
||||
*dsaptr = ds_addr_table[ind];
|
||||
*bar_offset = base_addr % IOMM_TABLE_ENTRY_SIZE;
|
||||
} else
|
||||
panic("PCI: Invalid PCI IO address detected!\n");
|
||||
return dn;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static u8 iseries_readb(const volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
struct HvCallPci_LoadReturn ret;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "read_byte");
|
||||
|
||||
if (dn == NULL)
|
||||
return 0xff;
|
||||
do {
|
||||
HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, bar_offset, 0);
|
||||
} while (check_return_code("RDB", dn, &retry, ret.rc) != 0);
|
||||
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
static u16 iseries_readw_be(const volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
struct HvCallPci_LoadReturn ret;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "read_word");
|
||||
|
||||
if (dn == NULL)
|
||||
return 0xffff;
|
||||
do {
|
||||
HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa,
|
||||
bar_offset, 0);
|
||||
} while (check_return_code("RDW", dn, &retry, ret.rc) != 0);
|
||||
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
static u32 iseries_readl_be(const volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
struct HvCallPci_LoadReturn ret;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "read_long");
|
||||
|
||||
if (dn == NULL)
|
||||
return 0xffffffff;
|
||||
do {
|
||||
HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa,
|
||||
bar_offset, 0);
|
||||
} while (check_return_code("RDL", dn, &retry, ret.rc) != 0);
|
||||
|
||||
return ret.value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write MM I/O Instructions for the iSeries
|
||||
*
|
||||
*/
|
||||
static void iseries_writeb(u8 data, volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
u64 rc;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "write_byte");
|
||||
|
||||
if (dn == NULL)
|
||||
return;
|
||||
do {
|
||||
rc = HvCall4(HvCallPciBarStore8, dsa, bar_offset, data, 0);
|
||||
} while (check_return_code("WWB", dn, &retry, rc) != 0);
|
||||
}
|
||||
|
||||
static void iseries_writew_be(u16 data, volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
u64 rc;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "write_word");
|
||||
|
||||
if (dn == NULL)
|
||||
return;
|
||||
do {
|
||||
rc = HvCall4(HvCallPciBarStore16, dsa, bar_offset, data, 0);
|
||||
} while (check_return_code("WWW", dn, &retry, rc) != 0);
|
||||
}
|
||||
|
||||
static void iseries_writel_be(u32 data, volatile void __iomem *addr)
|
||||
{
|
||||
u64 bar_offset;
|
||||
u64 dsa;
|
||||
int retry = 0;
|
||||
u64 rc;
|
||||
struct device_node *dn =
|
||||
xlate_iomm_address(addr, &dsa, &bar_offset, "write_long");
|
||||
|
||||
if (dn == NULL)
|
||||
return;
|
||||
do {
|
||||
rc = HvCall4(HvCallPciBarStore32, dsa, bar_offset, data, 0);
|
||||
} while (check_return_code("WWL", dn, &retry, rc) != 0);
|
||||
}
|
||||
|
||||
static u16 iseries_readw(const volatile void __iomem *addr)
|
||||
{
|
||||
return le16_to_cpu(iseries_readw_be(addr));
|
||||
}
|
||||
|
||||
static u32 iseries_readl(const volatile void __iomem *addr)
|
||||
{
|
||||
return le32_to_cpu(iseries_readl_be(addr));
|
||||
}
|
||||
|
||||
static void iseries_writew(u16 data, volatile void __iomem *addr)
|
||||
{
|
||||
iseries_writew_be(cpu_to_le16(data), addr);
|
||||
}
|
||||
|
||||
static void iseries_writel(u32 data, volatile void __iomem *addr)
|
||||
{
|
||||
iseries_writel(cpu_to_le32(data), addr);
|
||||
}
|
||||
|
||||
static void iseries_readsb(const volatile void __iomem *addr, void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
u8 *dst = buf;
|
||||
while(count-- > 0)
|
||||
*(dst++) = iseries_readb(addr);
|
||||
}
|
||||
|
||||
static void iseries_readsw(const volatile void __iomem *addr, void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
u16 *dst = buf;
|
||||
while(count-- > 0)
|
||||
*(dst++) = iseries_readw_be(addr);
|
||||
}
|
||||
|
||||
static void iseries_readsl(const volatile void __iomem *addr, void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
u32 *dst = buf;
|
||||
while(count-- > 0)
|
||||
*(dst++) = iseries_readl_be(addr);
|
||||
}
|
||||
|
||||
static void iseries_writesb(volatile void __iomem *addr, const void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
const u8 *src = buf;
|
||||
while(count-- > 0)
|
||||
iseries_writeb(*(src++), addr);
|
||||
}
|
||||
|
||||
static void iseries_writesw(volatile void __iomem *addr, const void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
const u16 *src = buf;
|
||||
while(count-- > 0)
|
||||
iseries_writew_be(*(src++), addr);
|
||||
}
|
||||
|
||||
static void iseries_writesl(volatile void __iomem *addr, const void *buf,
|
||||
unsigned long count)
|
||||
{
|
||||
const u32 *src = buf;
|
||||
while(count-- > 0)
|
||||
iseries_writel_be(*(src++), addr);
|
||||
}
|
||||
|
||||
static void iseries_memset_io(volatile void __iomem *addr, int c,
|
||||
unsigned long n)
|
||||
{
|
||||
volatile char __iomem *d = addr;
|
||||
|
||||
while (n-- > 0)
|
||||
iseries_writeb(c, d++);
|
||||
}
|
||||
|
||||
static void iseries_memcpy_fromio(void *dest, const volatile void __iomem *src,
|
||||
unsigned long n)
|
||||
{
|
||||
char *d = dest;
|
||||
const volatile char __iomem *s = src;
|
||||
|
||||
while (n-- > 0)
|
||||
*d++ = iseries_readb(s++);
|
||||
}
|
||||
|
||||
static void iseries_memcpy_toio(volatile void __iomem *dest, const void *src,
|
||||
unsigned long n)
|
||||
{
|
||||
const char *s = src;
|
||||
volatile char __iomem *d = dest;
|
||||
|
||||
while (n-- > 0)
|
||||
iseries_writeb(*s++, d++);
|
||||
}
|
||||
|
||||
/* We only set MMIO ops. The default PIO ops will be default
|
||||
* to the MMIO ops + pci_io_base which is 0 on iSeries as
|
||||
* expected so both should work.
|
||||
*
|
||||
* Note that we don't implement the readq/writeq versions as
|
||||
* I don't know of an HV call for doing so. Thus, the default
|
||||
* operation will be used instead, which will fault a the value
|
||||
* return by iSeries for MMIO addresses always hits a non mapped
|
||||
* area. This is as good as the BUG() we used to have there.
|
||||
*/
|
||||
static struct ppc_pci_io __initdata iseries_pci_io = {
|
||||
.readb = iseries_readb,
|
||||
.readw = iseries_readw,
|
||||
.readl = iseries_readl,
|
||||
.readw_be = iseries_readw_be,
|
||||
.readl_be = iseries_readl_be,
|
||||
.writeb = iseries_writeb,
|
||||
.writew = iseries_writew,
|
||||
.writel = iseries_writel,
|
||||
.writew_be = iseries_writew_be,
|
||||
.writel_be = iseries_writel_be,
|
||||
.readsb = iseries_readsb,
|
||||
.readsw = iseries_readsw,
|
||||
.readsl = iseries_readsl,
|
||||
.writesb = iseries_writesb,
|
||||
.writesw = iseries_writesw,
|
||||
.writesl = iseries_writesl,
|
||||
.memset_io = iseries_memset_io,
|
||||
.memcpy_fromio = iseries_memcpy_fromio,
|
||||
.memcpy_toio = iseries_memcpy_toio,
|
||||
};
|
||||
|
||||
/*
|
||||
* iSeries_pcibios_init
|
||||
*
|
||||
* Description:
|
||||
* This function checks for all possible system PCI host bridges that connect
|
||||
* PCI buses. The system hypervisor is queried as to the guest partition
|
||||
* ownership status. A pci_controller is built for any bus which is partially
|
||||
* owned or fully owned by this guest partition.
|
||||
*/
|
||||
void __init iSeries_pcibios_init(void)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
struct device_node *node = NULL;
|
||||
|
||||
/* Install IO hooks */
|
||||
ppc_pci_io = iseries_pci_io;
|
||||
|
||||
pci_probe_only = 1;
|
||||
|
||||
/* iSeries has no IO space in the common sense, it needs to set
|
||||
* the IO base to 0
|
||||
*/
|
||||
pci_io_base = 0;
|
||||
|
||||
if (root == NULL) {
|
||||
printk(KERN_CRIT "iSeries_pcibios_init: can't find root "
|
||||
"of device tree\n");
|
||||
return;
|
||||
}
|
||||
while ((node = of_get_next_child(root, node)) != NULL) {
|
||||
HvBusNumber bus;
|
||||
const u32 *busp;
|
||||
|
||||
if ((node->type == NULL) || (strcmp(node->type, "pci") != 0))
|
||||
continue;
|
||||
|
||||
busp = of_get_property(node, "bus-range", NULL);
|
||||
if (busp == NULL)
|
||||
continue;
|
||||
bus = *busp;
|
||||
printk("bus %d appears to exist\n", bus);
|
||||
phb = pcibios_alloc_controller(node);
|
||||
if (phb == NULL)
|
||||
continue;
|
||||
/* All legacy iSeries PHBs are in domain zero */
|
||||
phb->global_number = 0;
|
||||
|
||||
phb->first_busno = bus;
|
||||
phb->last_busno = bus;
|
||||
phb->ops = &iSeries_pci_ops;
|
||||
phb->io_base_virt = (void __iomem *)_IO_BASE;
|
||||
phb->io_resource.flags = IORESOURCE_IO;
|
||||
phb->io_resource.start = BASE_IO_MEMORY;
|
||||
phb->io_resource.end = END_IO_MEMORY;
|
||||
phb->io_resource.name = "iSeries PCI IO";
|
||||
phb->mem_resources[0].flags = IORESOURCE_MEM;
|
||||
phb->mem_resources[0].start = BASE_IO_MEMORY;
|
||||
phb->mem_resources[0].end = END_IO_MEMORY;
|
||||
phb->mem_resources[0].name = "Series PCI MEM";
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
|
||||
pci_devs_phb_init();
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
#ifndef _PLATFORMS_ISERIES_PCI_H
|
||||
#define _PLATFORMS_ISERIES_PCI_H
|
||||
|
||||
/*
|
||||
* Created by Allan Trautman on Tue Feb 20, 2001.
|
||||
*
|
||||
* Define some useful macros for the iSeries pci routines.
|
||||
* Copyright (C) 2001 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
|
||||
* Ported to ppc64, May 25, 2001
|
||||
* End Change Activity
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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_PCI_AGENTID(idsel, func) \
|
||||
(((idsel & 0x0F) << 4) | (func & 0x07))
|
||||
#define ISERIES_ENCODE_DEVICE(agentid) \
|
||||
((0x10) | ((agentid & 0x20) >> 2) | (agentid & 0x07))
|
||||
|
||||
#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7)
|
||||
#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7)
|
||||
|
||||
struct pci_dev;
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
extern void iSeries_pcibios_init(void);
|
||||
extern void iSeries_pci_final_fixup(void);
|
||||
extern void iSeries_pcibios_fixup_resources(struct pci_dev *dev);
|
||||
#else
|
||||
static inline void iSeries_pcibios_init(void) { }
|
||||
static inline void iSeries_pci_final_fixup(void) { }
|
||||
static inline void iSeries_pcibios_fixup_resources(struct pci_dev *dev) {}
|
||||
#endif
|
||||
|
||||
#endif /* _PLATFORMS_ISERIES_PCI_H */
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Kyle A. Lucke IBM Corporation
|
||||
* Copyright (C) 2001 Mike Corrigan & Dave Engebretsen 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/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/param.h> /* for HZ */
|
||||
#include <asm/paca.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/lppaca.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
|
||||
#include "processor_vpd.h"
|
||||
#include "main_store.h"
|
||||
|
||||
static int __init iseries_proc_create(void)
|
||||
{
|
||||
struct proc_dir_entry *e;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
return 0;
|
||||
|
||||
e = proc_mkdir("iSeries", 0);
|
||||
if (!e)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(iseries_proc_create);
|
||||
|
||||
static unsigned long startTitan = 0;
|
||||
static unsigned long startTb = 0;
|
||||
|
||||
static int proc_titantod_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long tb0, titan_tod;
|
||||
|
||||
tb0 = get_tb();
|
||||
titan_tod = HvCallXm_loadTod();
|
||||
|
||||
seq_printf(m, "Titan\n" );
|
||||
seq_printf(m, " time base = %016lx\n", tb0);
|
||||
seq_printf(m, " titan tod = %016lx\n", titan_tod);
|
||||
seq_printf(m, " xProcFreq = %016x\n",
|
||||
xIoHriProcessorVpd[0].xProcFreq);
|
||||
seq_printf(m, " xTimeBaseFreq = %016x\n",
|
||||
xIoHriProcessorVpd[0].xTimeBaseFreq);
|
||||
seq_printf(m, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy);
|
||||
seq_printf(m, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec);
|
||||
|
||||
if (!startTitan) {
|
||||
startTitan = titan_tod;
|
||||
startTb = tb0;
|
||||
} else {
|
||||
unsigned long titan_usec = (titan_tod - startTitan) >> 12;
|
||||
unsigned long tb_ticks = (tb0 - startTb);
|
||||
unsigned long titan_jiffies = titan_usec / (1000000/HZ);
|
||||
unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ);
|
||||
unsigned long titan_jiff_rem_usec =
|
||||
titan_usec - titan_jiff_usec;
|
||||
unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy;
|
||||
unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy;
|
||||
unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks;
|
||||
unsigned long tb_jiff_rem_usec =
|
||||
tb_jiff_rem_ticks / tb_ticks_per_usec;
|
||||
unsigned long new_tb_ticks_per_jiffy =
|
||||
(tb_ticks * (1000000/HZ))/titan_usec;
|
||||
|
||||
seq_printf(m, " titan elapsed = %lu uSec\n", titan_usec);
|
||||
seq_printf(m, " tb elapsed = %lu ticks\n", tb_ticks);
|
||||
seq_printf(m, " titan jiffies = %lu.%04lu\n", titan_jiffies,
|
||||
titan_jiff_rem_usec);
|
||||
seq_printf(m, " tb jiffies = %lu.%04lu\n", tb_jiffies,
|
||||
tb_jiff_rem_usec);
|
||||
seq_printf(m, " new tb_ticks_per_jiffy = %lu\n",
|
||||
new_tb_ticks_per_jiffy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_titantod_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, proc_titantod_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_titantod_operations = {
|
||||
.open = proc_titantod_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init iseries_proc_init(void)
|
||||
{
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
return 0;
|
||||
|
||||
proc_create("iSeries/titanTod", S_IFREG|S_IRUGO, NULL,
|
||||
&proc_titantod_operations);
|
||||
return 0;
|
||||
}
|
||||
__initcall(iseries_proc_init);
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* 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_PROCESSOR_VPD_H
|
||||
#define _ISERIES_PROCESSOR_VPD_H
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
/*
|
||||
* This struct maps Processor Vpd that is DMAd to SLIC by CSP
|
||||
*/
|
||||
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
|
||||
};
|
||||
|
||||
extern struct IoHriProcessorVpd xIoHriProcessorVpd[];
|
||||
|
||||
#endif /* _ISERIES_PROCESSOR_VPD_H */
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* 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_RELEASE_DATA_H
|
||||
#define _ISERIES_RELEASE_DATA_H
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
#include <asm/types.h>
|
||||
#include "naca.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".
|
||||
*/
|
||||
|
||||
#define HVREL_TAGSINACTIVE 0x8000
|
||||
#define HVREL_32BIT 0x4000
|
||||
#define HVREL_NOSHAREDPROCS 0x2000
|
||||
#define HVREL_NOHMT 0x1000
|
||||
|
||||
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 */
|
||||
struct naca_struct *xSlicNacaAddr; /* Virt addr of SLIC NACA x08-x0F */
|
||||
u32 xMsNucDataOffset; /* Offset of Linux Mapping Data x10-x13 */
|
||||
u32 xRsvd1; /* Reserved x14-x17 */
|
||||
u16 xFlags;
|
||||
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 */
|
||||
};
|
||||
|
||||
extern const struct HvReleaseData hvReleaseData;
|
||||
|
||||
#endif /* _ISERIES_RELEASE_DATA_H */
|
|
@ -1,722 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
|
||||
* Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
|
||||
*
|
||||
* 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>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/paca.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/iseries/hv_lp_config.h>
|
||||
#include <asm/iseries/hv_call_event.h>
|
||||
#include <asm/iseries/hv_call_xm.h>
|
||||
#include <asm/iseries/it_lp_queue.h>
|
||||
#include <asm/iseries/mf.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/lpar_map.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include "naca.h"
|
||||
#include "setup.h"
|
||||
#include "irq.h"
|
||||
#include "vpd_areas.h"
|
||||
#include "processor_vpd.h"
|
||||
#include "it_lp_naca.h"
|
||||
#include "main_store.h"
|
||||
#include "call_sm.h"
|
||||
#include "call_hpt.h"
|
||||
#include "pci.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
/* Function Prototypes */
|
||||
static unsigned long build_iSeries_Memory_Map(void);
|
||||
static void iseries_shared_idle(void);
|
||||
static void iseries_dedicated_idle(void);
|
||||
|
||||
|
||||
struct MemoryBlock {
|
||||
unsigned long absStart;
|
||||
unsigned long absEnd;
|
||||
unsigned long logicalStart;
|
||||
unsigned long logicalEnd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Process the main store vpd to determine where the holes in memory are
|
||||
* and return the number of physical blocks and fill in the array of
|
||||
* block data.
|
||||
*/
|
||||
static unsigned long iSeries_process_Condor_mainstore_vpd(
|
||||
struct MemoryBlock *mb_array, unsigned long max_entries)
|
||||
{
|
||||
unsigned long holeFirstChunk, holeSizeChunks;
|
||||
unsigned long numMemoryBlocks = 1;
|
||||
struct IoHriMainStoreSegment4 *msVpd =
|
||||
(struct IoHriMainStoreSegment4 *)xMsVpd;
|
||||
unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr;
|
||||
unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr;
|
||||
unsigned long holeSize = holeEnd - holeStart;
|
||||
|
||||
printk("Mainstore_VPD: Condor\n");
|
||||
/*
|
||||
* Determine if absolute memory has any
|
||||
* holes so that we can interpret the
|
||||
* access map we get back from the hypervisor
|
||||
* correctly.
|
||||
*/
|
||||
mb_array[0].logicalStart = 0;
|
||||
mb_array[0].logicalEnd = 0x100000000UL;
|
||||
mb_array[0].absStart = 0;
|
||||
mb_array[0].absEnd = 0x100000000UL;
|
||||
|
||||
if (holeSize) {
|
||||
numMemoryBlocks = 2;
|
||||
holeStart = holeStart & 0x000fffffffffffffUL;
|
||||
holeStart = addr_to_chunk(holeStart);
|
||||
holeFirstChunk = holeStart;
|
||||
holeSize = addr_to_chunk(holeSize);
|
||||
holeSizeChunks = holeSize;
|
||||
printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n",
|
||||
holeFirstChunk, holeSizeChunks );
|
||||
mb_array[0].logicalEnd = holeFirstChunk;
|
||||
mb_array[0].absEnd = holeFirstChunk;
|
||||
mb_array[1].logicalStart = holeFirstChunk;
|
||||
mb_array[1].logicalEnd = 0x100000000UL - holeSizeChunks;
|
||||
mb_array[1].absStart = holeFirstChunk + holeSizeChunks;
|
||||
mb_array[1].absEnd = 0x100000000UL;
|
||||
}
|
||||
return numMemoryBlocks;
|
||||
}
|
||||
|
||||
#define MaxSegmentAreas 32
|
||||
#define MaxSegmentAdrRangeBlocks 128
|
||||
#define MaxAreaRangeBlocks 4
|
||||
|
||||
static unsigned long iSeries_process_Regatta_mainstore_vpd(
|
||||
struct MemoryBlock *mb_array, unsigned long max_entries)
|
||||
{
|
||||
struct IoHriMainStoreSegment5 *msVpdP =
|
||||
(struct IoHriMainStoreSegment5 *)xMsVpd;
|
||||
unsigned long numSegmentBlocks = 0;
|
||||
u32 existsBits = msVpdP->msAreaExists;
|
||||
unsigned long area_num;
|
||||
|
||||
printk("Mainstore_VPD: Regatta\n");
|
||||
|
||||
for (area_num = 0; area_num < MaxSegmentAreas; ++area_num ) {
|
||||
unsigned long numAreaBlocks;
|
||||
struct IoHriMainStoreArea4 *currentArea;
|
||||
|
||||
if (existsBits & 0x80000000) {
|
||||
unsigned long block_num;
|
||||
|
||||
currentArea = &msVpdP->msAreaArray[area_num];
|
||||
numAreaBlocks = currentArea->numAdrRangeBlocks;
|
||||
printk("ms_vpd: processing area %2ld blocks=%ld",
|
||||
area_num, numAreaBlocks);
|
||||
for (block_num = 0; block_num < numAreaBlocks;
|
||||
++block_num ) {
|
||||
/* Process an address range block */
|
||||
struct MemoryBlock tempBlock;
|
||||
unsigned long i;
|
||||
|
||||
tempBlock.absStart =
|
||||
(unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart;
|
||||
tempBlock.absEnd =
|
||||
(unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd;
|
||||
tempBlock.logicalStart = 0;
|
||||
tempBlock.logicalEnd = 0;
|
||||
printk("\n block %ld absStart=%016lx absEnd=%016lx",
|
||||
block_num, tempBlock.absStart,
|
||||
tempBlock.absEnd);
|
||||
|
||||
for (i = 0; i < numSegmentBlocks; ++i) {
|
||||
if (mb_array[i].absStart ==
|
||||
tempBlock.absStart)
|
||||
break;
|
||||
}
|
||||
if (i == numSegmentBlocks) {
|
||||
if (numSegmentBlocks == max_entries)
|
||||
panic("iSeries_process_mainstore_vpd: too many memory blocks");
|
||||
mb_array[numSegmentBlocks] = tempBlock;
|
||||
++numSegmentBlocks;
|
||||
} else
|
||||
printk(" (duplicate)");
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
existsBits <<= 1;
|
||||
}
|
||||
/* Now sort the blocks found into ascending sequence */
|
||||
if (numSegmentBlocks > 1) {
|
||||
unsigned long m, n;
|
||||
|
||||
for (m = 0; m < numSegmentBlocks - 1; ++m) {
|
||||
for (n = numSegmentBlocks - 1; m < n; --n) {
|
||||
if (mb_array[n].absStart <
|
||||
mb_array[n-1].absStart) {
|
||||
struct MemoryBlock tempBlock;
|
||||
|
||||
tempBlock = mb_array[n];
|
||||
mb_array[n] = mb_array[n-1];
|
||||
mb_array[n-1] = tempBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Assign "logical" addresses to each block. These
|
||||
* addresses correspond to the hypervisor "bitmap" space.
|
||||
* Convert all addresses into units of 256K chunks.
|
||||
*/
|
||||
{
|
||||
unsigned long i, nextBitmapAddress;
|
||||
|
||||
printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks);
|
||||
nextBitmapAddress = 0;
|
||||
for (i = 0; i < numSegmentBlocks; ++i) {
|
||||
unsigned long length = mb_array[i].absEnd -
|
||||
mb_array[i].absStart;
|
||||
|
||||
mb_array[i].logicalStart = nextBitmapAddress;
|
||||
mb_array[i].logicalEnd = nextBitmapAddress + length;
|
||||
nextBitmapAddress += length;
|
||||
printk(" Bitmap range: %016lx - %016lx\n"
|
||||
" Absolute range: %016lx - %016lx\n",
|
||||
mb_array[i].logicalStart,
|
||||
mb_array[i].logicalEnd,
|
||||
mb_array[i].absStart, mb_array[i].absEnd);
|
||||
mb_array[i].absStart = addr_to_chunk(mb_array[i].absStart &
|
||||
0x000fffffffffffffUL);
|
||||
mb_array[i].absEnd = addr_to_chunk(mb_array[i].absEnd &
|
||||
0x000fffffffffffffUL);
|
||||
mb_array[i].logicalStart =
|
||||
addr_to_chunk(mb_array[i].logicalStart);
|
||||
mb_array[i].logicalEnd = addr_to_chunk(mb_array[i].logicalEnd);
|
||||
}
|
||||
}
|
||||
|
||||
return numSegmentBlocks;
|
||||
}
|
||||
|
||||
static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array,
|
||||
unsigned long max_entries)
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned long mem_blocks = 0;
|
||||
|
||||
if (mmu_has_feature(MMU_FTR_SLB))
|
||||
mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array,
|
||||
max_entries);
|
||||
else
|
||||
mem_blocks = iSeries_process_Condor_mainstore_vpd(mb_array,
|
||||
max_entries);
|
||||
|
||||
printk("Mainstore_VPD: numMemoryBlocks = %ld\n", mem_blocks);
|
||||
for (i = 0; i < mem_blocks; ++i) {
|
||||
printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n"
|
||||
" abs chunks %016lx - %016lx\n",
|
||||
i, mb_array[i].logicalStart, mb_array[i].logicalEnd,
|
||||
mb_array[i].absStart, mb_array[i].absEnd);
|
||||
}
|
||||
return mem_blocks;
|
||||
}
|
||||
|
||||
static void __init iSeries_get_cmdline(void)
|
||||
{
|
||||
char *p, *q;
|
||||
|
||||
/* copy the command line parameter from the primary VSP */
|
||||
HvCallEvent_dmaToSp(cmd_line, 2 * 64* 1024, 256,
|
||||
HvLpDma_Direction_RemoteToLocal);
|
||||
|
||||
p = cmd_line;
|
||||
q = cmd_line + 255;
|
||||
while(p < q) {
|
||||
if (!*p || *p == '\n')
|
||||
break;
|
||||
++p;
|
||||
}
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
static void __init iSeries_init_early(void)
|
||||
{
|
||||
DBG(" -> iSeries_init_early()\n");
|
||||
|
||||
/* Snapshot the timebase, for use in later recalibration */
|
||||
iSeries_time_init_early();
|
||||
|
||||
/*
|
||||
* Initialize the DMA/TCE management
|
||||
*/
|
||||
iommu_init_early_iSeries();
|
||||
|
||||
/* Initialize machine-dependency vectors */
|
||||
#ifdef CONFIG_SMP
|
||||
smp_init_iSeries();
|
||||
#endif
|
||||
|
||||
/* Associate Lp Event Queue 0 with processor 0 */
|
||||
HvCallEvent_setLpEventQueueInterruptProc(0, 0);
|
||||
|
||||
mf_init();
|
||||
|
||||
DBG(" <- iSeries_init_early()\n");
|
||||
}
|
||||
|
||||
struct mschunks_map mschunks_map = {
|
||||
/* XXX We don't use these, but Piranha might need them. */
|
||||
.chunk_size = MSCHUNKS_CHUNK_SIZE,
|
||||
.chunk_shift = MSCHUNKS_CHUNK_SHIFT,
|
||||
.chunk_mask = MSCHUNKS_OFFSET_MASK,
|
||||
};
|
||||
EXPORT_SYMBOL(mschunks_map);
|
||||
|
||||
static void mschunks_alloc(unsigned long num_chunks)
|
||||
{
|
||||
klimit = _ALIGN(klimit, sizeof(u32));
|
||||
mschunks_map.mapping = (u32 *)klimit;
|
||||
klimit += num_chunks * sizeof(u32);
|
||||
mschunks_map.num_chunks = num_chunks;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 AS/400 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)
|
||||
*
|
||||
* Returns the physical memory size
|
||||
*/
|
||||
|
||||
static unsigned long __init build_iSeries_Memory_Map(void)
|
||||
{
|
||||
u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize;
|
||||
u32 nextPhysChunk;
|
||||
u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages;
|
||||
u32 totalChunks,moreChunks;
|
||||
u32 currChunk, thisChunk, absChunk;
|
||||
u32 currDword;
|
||||
u32 chunkBit;
|
||||
u64 map;
|
||||
struct MemoryBlock mb[32];
|
||||
unsigned long numMemoryBlocks, curBlock;
|
||||
|
||||
/* Chunk size on iSeries is 256K bytes */
|
||||
totalChunks = (u32)HvLpConfig_getMsChunks();
|
||||
mschunks_alloc(totalChunks);
|
||||
|
||||
/*
|
||||
* 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)addr_to_chunk(itLpNaca.xLoadAreaAddr);
|
||||
loadAreaSize = itLpNaca.xLoadAreaChunks;
|
||||
|
||||
/*
|
||||
* Only add the pages already mapped here.
|
||||
* Otherwise we might add the hpt pages
|
||||
* The rest of the pages of the load area
|
||||
* aren't in the HPT yet and can still
|
||||
* be assigned an arbitrary physical address
|
||||
*/
|
||||
if ((loadAreaSize * 64) > HvPagesToMap)
|
||||
loadAreaSize = HvPagesToMap / 64;
|
||||
|
||||
loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1;
|
||||
|
||||
/*
|
||||
* TODO Do we need to do something if the HPT is in the 64MB load area?
|
||||
* This would be required if the itLpNaca.xLoadAreaChunks includes
|
||||
* the HPT size
|
||||
*/
|
||||
|
||||
printk("Mapping load area - physical addr = 0000000000000000\n"
|
||||
" absolute addr = %016lx\n",
|
||||
chunk_to_addr(loadAreaFirstChunk));
|
||||
printk("Load area size %dK\n", loadAreaSize * 256);
|
||||
|
||||
for (nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk)
|
||||
mschunks_map.mapping[nextPhysChunk] =
|
||||
loadAreaFirstChunk + nextPhysChunk;
|
||||
|
||||
/*
|
||||
* Get absolute address of our HPT and remember it so
|
||||
* we won't map it to any physical address
|
||||
*/
|
||||
hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress());
|
||||
hptSizePages = (u32)HvCallHpt_getHptPages();
|
||||
hptSizeChunks = hptSizePages >>
|
||||
(MSCHUNKS_CHUNK_SHIFT - HW_PAGE_SHIFT);
|
||||
hptLastChunk = hptFirstChunk + hptSizeChunks - 1;
|
||||
|
||||
printk("HPT absolute addr = %016lx, size = %dK\n",
|
||||
chunk_to_addr(hptFirstChunk), hptSizeChunks * 256);
|
||||
|
||||
/*
|
||||
* Determine if absolute memory has any
|
||||
* holes so that we can interpret the
|
||||
* access map we get back from the hypervisor
|
||||
* correctly.
|
||||
*/
|
||||
numMemoryBlocks = iSeries_process_mainstore_vpd(mb, 32);
|
||||
|
||||
/*
|
||||
* Process the main store access map from the hypervisor
|
||||
* to build up our physical -> absolute translation table
|
||||
*/
|
||||
curBlock = 0;
|
||||
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;
|
||||
while (thisChunk >= mb[curBlock].logicalEnd) {
|
||||
++curBlock;
|
||||
if (curBlock >= numMemoryBlocks)
|
||||
panic("out of memory blocks");
|
||||
}
|
||||
if (thisChunk < mb[curBlock].logicalStart)
|
||||
panic("memory block error");
|
||||
|
||||
absChunk = mb[curBlock].absStart +
|
||||
(thisChunk - mb[curBlock].logicalStart);
|
||||
if (((absChunk < hptFirstChunk) ||
|
||||
(absChunk > hptLastChunk)) &&
|
||||
((absChunk < loadAreaFirstChunk) ||
|
||||
(absChunk > loadAreaLastChunk))) {
|
||||
mschunks_map.mapping[nextPhysChunk] =
|
||||
absChunk;
|
||||
++nextPhysChunk;
|
||||
}
|
||||
}
|
||||
++thisChunk;
|
||||
}
|
||||
++currDword;
|
||||
currChunk += 64;
|
||||
}
|
||||
|
||||
/*
|
||||
* main store size (in chunks) is
|
||||
* totalChunks - hptSizeChunks
|
||||
* which should be equal to
|
||||
* nextPhysChunk
|
||||
*/
|
||||
return chunk_to_addr(nextPhysChunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Document me.
|
||||
*/
|
||||
static void __init iSeries_setup_arch(void)
|
||||
{
|
||||
if (get_lppaca()->shared_proc) {
|
||||
ppc_md.idle_loop = iseries_shared_idle;
|
||||
printk(KERN_DEBUG "Using shared processor idle loop\n");
|
||||
} else {
|
||||
ppc_md.idle_loop = iseries_dedicated_idle;
|
||||
printk(KERN_DEBUG "Using dedicated idle loop\n");
|
||||
}
|
||||
|
||||
/* Setup the Lp Event Queue */
|
||||
setup_hvlpevent_queue();
|
||||
|
||||
printk("Max logical processors = %d\n",
|
||||
itVpdAreas.xSlicMaxLogicalProcs);
|
||||
printk("Max physical processors = %d\n",
|
||||
itVpdAreas.xSlicMaxPhysicalProcs);
|
||||
|
||||
iSeries_pcibios_init();
|
||||
}
|
||||
|
||||
static void iSeries_show_cpuinfo(struct seq_file *m)
|
||||
{
|
||||
seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
|
||||
}
|
||||
|
||||
static void __init iSeries_progress(char * st, unsigned short code)
|
||||
{
|
||||
printk("Progress: [%04x] - %s\n", (unsigned)code, st);
|
||||
mf_display_progress(code);
|
||||
}
|
||||
|
||||
static void __init iSeries_fixup_klimit(void)
|
||||
{
|
||||
/*
|
||||
* Change klimit to take into account any ram disk
|
||||
* that may be included
|
||||
*/
|
||||
if (naca.xRamDisk)
|
||||
klimit = KERNELBASE + (u64)naca.xRamDisk +
|
||||
(naca.xRamDiskSize * HW_PAGE_SIZE);
|
||||
}
|
||||
|
||||
static int __init iSeries_src_init(void)
|
||||
{
|
||||
/* clear the progress line */
|
||||
if (firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
ppc_md.progress(" ", 0xffff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(iSeries_src_init);
|
||||
|
||||
static inline void process_iSeries_events(void)
|
||||
{
|
||||
asm volatile ("li 0,0x5555; sc" : : : "r0", "r3");
|
||||
}
|
||||
|
||||
static void yield_shared_processor(void)
|
||||
{
|
||||
unsigned long tb;
|
||||
|
||||
HvCall_setEnabledInterrupts(HvCall_MaskIPI |
|
||||
HvCall_MaskLpEvent |
|
||||
HvCall_MaskLpProd |
|
||||
HvCall_MaskTimeout);
|
||||
|
||||
tb = get_tb();
|
||||
/* Compute future tb value when yield should expire */
|
||||
HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy);
|
||||
|
||||
/*
|
||||
* The decrementer stops during the yield. Force a fake decrementer
|
||||
* here and let the timer_interrupt code sort out the actual time.
|
||||
*/
|
||||
get_lppaca()->int_dword.fields.decr_int = 1;
|
||||
ppc64_runlatch_on();
|
||||
process_iSeries_events();
|
||||
}
|
||||
|
||||
static void iseries_shared_idle(void)
|
||||
{
|
||||
while (1) {
|
||||
tick_nohz_idle_enter();
|
||||
rcu_idle_enter();
|
||||
while (!need_resched() && !hvlpevent_is_pending()) {
|
||||
local_irq_disable();
|
||||
ppc64_runlatch_off();
|
||||
|
||||
/* Recheck with irqs off */
|
||||
if (!need_resched() && !hvlpevent_is_pending())
|
||||
yield_shared_processor();
|
||||
|
||||
HMT_medium();
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
ppc64_runlatch_on();
|
||||
rcu_idle_exit();
|
||||
tick_nohz_idle_exit();
|
||||
|
||||
if (hvlpevent_is_pending())
|
||||
process_iSeries_events();
|
||||
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
preempt_disable();
|
||||
}
|
||||
}
|
||||
|
||||
static void iseries_dedicated_idle(void)
|
||||
{
|
||||
set_thread_flag(TIF_POLLING_NRFLAG);
|
||||
|
||||
while (1) {
|
||||
tick_nohz_idle_enter();
|
||||
rcu_idle_enter();
|
||||
if (!need_resched()) {
|
||||
while (!need_resched()) {
|
||||
ppc64_runlatch_off();
|
||||
HMT_low();
|
||||
|
||||
if (hvlpevent_is_pending()) {
|
||||
HMT_medium();
|
||||
ppc64_runlatch_on();
|
||||
process_iSeries_events();
|
||||
}
|
||||
}
|
||||
|
||||
HMT_medium();
|
||||
}
|
||||
|
||||
ppc64_runlatch_on();
|
||||
rcu_idle_exit();
|
||||
tick_nohz_idle_exit();
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
preempt_disable();
|
||||
}
|
||||
}
|
||||
|
||||
static void __iomem *iseries_ioremap(phys_addr_t address, unsigned long size,
|
||||
unsigned long flags, void *caller)
|
||||
{
|
||||
return (void __iomem *)address;
|
||||
}
|
||||
|
||||
static void iseries_iounmap(volatile void __iomem *token)
|
||||
{
|
||||
}
|
||||
|
||||
static int __init iseries_probe(void)
|
||||
{
|
||||
unsigned long root = of_get_flat_dt_root();
|
||||
if (!of_flat_dt_is_compatible(root, "IBM,iSeries"))
|
||||
return 0;
|
||||
|
||||
hpte_init_iSeries();
|
||||
/* iSeries does not support 16M pages */
|
||||
cur_cpu_spec->mmu_features &= ~MMU_FTR_16M_PAGE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KEXEC
|
||||
static int iseries_kexec_prepare(struct kimage *image)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
define_machine(iseries) {
|
||||
.name = "iSeries",
|
||||
.setup_arch = iSeries_setup_arch,
|
||||
.show_cpuinfo = iSeries_show_cpuinfo,
|
||||
.init_IRQ = iSeries_init_IRQ,
|
||||
.get_irq = iSeries_get_irq,
|
||||
.init_early = iSeries_init_early,
|
||||
.pcibios_fixup = iSeries_pci_final_fixup,
|
||||
.pcibios_fixup_resources= iSeries_pcibios_fixup_resources,
|
||||
.restart = mf_reboot,
|
||||
.power_off = mf_power_off,
|
||||
.halt = mf_power_off,
|
||||
.get_boot_time = iSeries_get_boot_time,
|
||||
.set_rtc_time = iSeries_set_rtc_time,
|
||||
.get_rtc_time = iSeries_get_rtc_time,
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.progress = iSeries_progress,
|
||||
.probe = iseries_probe,
|
||||
.ioremap = iseries_ioremap,
|
||||
.iounmap = iseries_iounmap,
|
||||
#ifdef CONFIG_KEXEC
|
||||
.machine_kexec_prepare = iseries_kexec_prepare,
|
||||
#endif
|
||||
/* XXX Implement enable_pmcs for iSeries */
|
||||
};
|
||||
|
||||
void * __init iSeries_early_setup(void)
|
||||
{
|
||||
unsigned long phys_mem_size;
|
||||
|
||||
/* Identify CPU type. This is done again by the common code later
|
||||
* on but calling this function multiple times is fine.
|
||||
*/
|
||||
identify_cpu(0, mfspr(SPRN_PVR));
|
||||
initialise_paca(&boot_paca, 0);
|
||||
|
||||
powerpc_firmware_features |= FW_FEATURE_ISERIES;
|
||||
powerpc_firmware_features |= FW_FEATURE_LPAR;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* On iSeries we know we can never have more than 64 cpus */
|
||||
nr_cpu_ids = max(nr_cpu_ids, 64);
|
||||
#endif
|
||||
|
||||
iSeries_fixup_klimit();
|
||||
|
||||
/*
|
||||
* Initialize the table which translate Linux physical addresses to
|
||||
* AS/400 absolute addresses
|
||||
*/
|
||||
phys_mem_size = build_iSeries_Memory_Map();
|
||||
|
||||
iSeries_get_cmdline();
|
||||
|
||||
return (void *) __pa(build_flat_dt(phys_mem_size));
|
||||
}
|
||||
|
||||
static void hvputc(char c)
|
||||
{
|
||||
if (c == '\n')
|
||||
hvputc('\r');
|
||||
|
||||
HvCall_writeLogBuffer(&c, 1);
|
||||
}
|
||||
|
||||
void __init udbg_init_iseries(void)
|
||||
{
|
||||
udbg_putc = hvputc;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2000 Mike Corrigan <mikejc@us.ibm.com>
|
||||
* Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
|
||||
*
|
||||
* Description:
|
||||
* Architecture- / platform-specific boot-time initialization code for
|
||||
* the IBM AS/400 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.
|
||||
*/
|
||||
|
||||
#ifndef __ISERIES_SETUP_H__
|
||||
#define __ISERIES_SETUP_H__
|
||||
|
||||
extern void *iSeries_early_setup(void);
|
||||
extern unsigned long iSeries_get_boot_time(void);
|
||||
extern int iSeries_set_rtc_time(struct rtc_time *tm);
|
||||
extern void iSeries_get_rtc_time(struct rtc_time *tm);
|
||||
|
||||
extern void *build_flat_dt(unsigned long phys_mem_size);
|
||||
|
||||
#endif /* __ISERIES_SETUP_H__ */
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* SMP support for iSeries machines.
|
||||
*
|
||||
* Dave Engebretsen, Peter Bergner, and
|
||||
* Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
|
||||
*
|
||||
* Plus various changes from other IBM teams...
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/paca.h>
|
||||
#include <asm/iseries/hv_call.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/cputable.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
static void smp_iSeries_cause_ipi(int cpu, unsigned long data)
|
||||
{
|
||||
HvCall_sendIPI(&(paca[cpu]));
|
||||
}
|
||||
|
||||
static int smp_iSeries_probe(void)
|
||||
{
|
||||
return cpumask_weight(cpu_possible_mask);
|
||||
}
|
||||
|
||||
static int smp_iSeries_kick_cpu(int nr)
|
||||
{
|
||||
BUG_ON((nr < 0) || (nr >= NR_CPUS));
|
||||
|
||||
/* Verify that our partition has a processor nr */
|
||||
if (lppaca_of(nr).dyn_proc_status >= 2)
|
||||
return -ENOENT;
|
||||
|
||||
/* The processor is currently spinning, waiting
|
||||
* for the cpu_start field to become non-zero
|
||||
* After we set cpu_start, the processor will
|
||||
* continue on to secondary_start in iSeries_head.S
|
||||
*/
|
||||
paca[nr].cpu_start = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit smp_iSeries_setup_cpu(int nr)
|
||||
{
|
||||
}
|
||||
|
||||
static struct smp_ops_t iSeries_smp_ops = {
|
||||
.message_pass = NULL, /* Use smp_muxed_ipi_message_pass */
|
||||
.cause_ipi = smp_iSeries_cause_ipi,
|
||||
.probe = smp_iSeries_probe,
|
||||
.kick_cpu = smp_iSeries_kick_cpu,
|
||||
.setup_cpu = smp_iSeries_setup_cpu,
|
||||
};
|
||||
|
||||
/* This is called very early. */
|
||||
void __init smp_init_iSeries(void)
|
||||
{
|
||||
smp_ops = &iSeries_smp_ops;
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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_SPCOMM_AREA_H
|
||||
#define _ISERIES_SPCOMM_AREA_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
|
||||
};
|
||||
|
||||
#endif /* _ISERIES_SPCOMM_AREA_H */
|
|
@ -1,556 +0,0 @@
|
|||
/*
|
||||
* Legacy iSeries specific vio initialisation
|
||||
* that needs to be built in (not a module).
|
||||
*
|
||||
* © Copyright 2007 IBM Corporation
|
||||
* Author: Stephen Rothwell
|
||||
* Some parts collected from various other files
|
||||
*
|
||||
* 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/of.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/iseries/vio.h>
|
||||
#include <asm/iseries/iommu.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
|
||||
#define FIRST_VTY 0
|
||||
#define NUM_VTYS 1
|
||||
#define FIRST_VSCSI (FIRST_VTY + NUM_VTYS)
|
||||
#define NUM_VSCSIS 1
|
||||
#define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS)
|
||||
#define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS
|
||||
#define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS)
|
||||
#define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS
|
||||
#define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS)
|
||||
#define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS
|
||||
#define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS)
|
||||
#define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES
|
||||
|
||||
struct vio_waitevent {
|
||||
struct completion com;
|
||||
int rc;
|
||||
u16 sub_result;
|
||||
};
|
||||
|
||||
struct vio_resource {
|
||||
char rsrcname[10];
|
||||
char type[4];
|
||||
char model[3];
|
||||
};
|
||||
|
||||
static struct property *new_property(const char *name, int length,
|
||||
const void *value)
|
||||
{
|
||||
struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
np->name = (char *)(np + 1);
|
||||
np->value = np->name + strlen(name) + 1;
|
||||
strcpy(np->name, name);
|
||||
memcpy(np->value, value, length);
|
||||
np->length = length;
|
||||
return np;
|
||||
}
|
||||
|
||||
static void free_property(struct property *np)
|
||||
{
|
||||
kfree(np);
|
||||
}
|
||||
|
||||
static struct device_node *new_node(const char *path,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL);
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
np->full_name = kstrdup(path, GFP_KERNEL);
|
||||
if (!np->full_name) {
|
||||
kfree(np);
|
||||
return NULL;
|
||||
}
|
||||
of_node_set_flag(np, OF_DYNAMIC);
|
||||
kref_init(&np->kref);
|
||||
np->parent = of_node_get(parent);
|
||||
return np;
|
||||
}
|
||||
|
||||
static void free_node(struct device_node *np)
|
||||
{
|
||||
struct property *next;
|
||||
struct property *prop;
|
||||
|
||||
next = np->properties;
|
||||
while (next) {
|
||||
prop = next;
|
||||
next = prop->next;
|
||||
free_property(prop);
|
||||
}
|
||||
of_node_put(np->parent);
|
||||
kfree(np->full_name);
|
||||
kfree(np);
|
||||
}
|
||||
|
||||
static int add_string_property(struct device_node *np, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
struct property *nprop = new_property(name, strlen(value) + 1, value);
|
||||
|
||||
if (!nprop)
|
||||
return 0;
|
||||
prom_add_property(np, nprop);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_raw_property(struct device_node *np, const char *name,
|
||||
int length, const void *value)
|
||||
{
|
||||
struct property *nprop = new_property(name, length, value);
|
||||
|
||||
if (!nprop)
|
||||
return 0;
|
||||
prom_add_property(np, nprop);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct device_node *do_device_node(struct device_node *parent,
|
||||
const char *name, u32 reg, u32 unit, const char *type,
|
||||
const char *compat, struct vio_resource *res)
|
||||
{
|
||||
struct device_node *np;
|
||||
char path[32];
|
||||
|
||||
snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg);
|
||||
np = new_node(path, parent);
|
||||
if (!np)
|
||||
return NULL;
|
||||
if (!add_string_property(np, "name", name) ||
|
||||
!add_string_property(np, "device_type", type) ||
|
||||
!add_string_property(np, "compatible", compat) ||
|
||||
!add_raw_property(np, "reg", sizeof(reg), ®) ||
|
||||
!add_raw_property(np, "linux,unit_address",
|
||||
sizeof(unit), &unit)) {
|
||||
goto node_free;
|
||||
}
|
||||
if (res) {
|
||||
if (!add_raw_property(np, "linux,vio_rsrcname",
|
||||
sizeof(res->rsrcname), res->rsrcname) ||
|
||||
!add_raw_property(np, "linux,vio_type",
|
||||
sizeof(res->type), res->type) ||
|
||||
!add_raw_property(np, "linux,vio_model",
|
||||
sizeof(res->model), res->model))
|
||||
goto node_free;
|
||||
}
|
||||
np->name = of_get_property(np, "name", NULL);
|
||||
np->type = of_get_property(np, "device_type", NULL);
|
||||
of_attach_node(np);
|
||||
#ifdef CONFIG_PROC_DEVICETREE
|
||||
if (parent->pde) {
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde);
|
||||
if (ent)
|
||||
proc_device_tree_add_node(np, ent);
|
||||
}
|
||||
#endif
|
||||
return np;
|
||||
|
||||
node_free:
|
||||
free_node(np);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is here so that we can dynamically add viodasd
|
||||
* devices without exposing all the above infrastructure.
|
||||
*/
|
||||
struct vio_dev *vio_create_viodasd(u32 unit)
|
||||
{
|
||||
struct device_node *vio_root;
|
||||
struct device_node *np;
|
||||
struct vio_dev *vdev = NULL;
|
||||
|
||||
vio_root = of_find_node_by_path("/vdevice");
|
||||
if (!vio_root)
|
||||
return NULL;
|
||||
np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
|
||||
"block", "IBM,iSeries-viodasd", NULL);
|
||||
of_node_put(vio_root);
|
||||
if (np) {
|
||||
vdev = vio_register_device_node(np);
|
||||
if (!vdev)
|
||||
free_node(np);
|
||||
}
|
||||
return vdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vio_create_viodasd);
|
||||
|
||||
static void __init handle_block_event(struct HvLpEvent *event)
|
||||
{
|
||||
struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
|
||||
struct vio_waitevent *pwe;
|
||||
|
||||
if (event == NULL)
|
||||
/* Notification that a partition went away! */
|
||||
return;
|
||||
/* First, we should NEVER get an int here...only acks */
|
||||
if (hvlpevent_is_int(event)) {
|
||||
printk(KERN_WARNING "handle_viod_request: "
|
||||
"Yikes! got an int in viodasd event handler!\n");
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
|
||||
case vioblockopen:
|
||||
/*
|
||||
* Handle a response to an open request. We get all the
|
||||
* disk information in the response, so update it. The
|
||||
* correlation token contains a pointer to a waitevent
|
||||
* structure that has a completion in it. update the
|
||||
* return code in the waitevent structure and post the
|
||||
* completion to wake up the guy who sent the request
|
||||
*/
|
||||
pwe = (struct vio_waitevent *)event->xCorrelationToken;
|
||||
pwe->rc = event->xRc;
|
||||
pwe->sub_result = bevent->sub_result;
|
||||
complete(&pwe->com);
|
||||
break;
|
||||
case vioblockclose:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "handle_viod_request: unexpected subtype!");
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __init probe_disk(struct device_node *vio_root, u32 unit)
|
||||
{
|
||||
HvLpEvent_Rc hvrc;
|
||||
struct vio_waitevent we;
|
||||
u16 flags = 0;
|
||||
|
||||
retry:
|
||||
init_completion(&we.com);
|
||||
|
||||
/* Send the open event to OS/400 */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockopen,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)&we, VIOVERSION << 16,
|
||||
((u64)unit << 48) | ((u64)flags<< 32),
|
||||
0, 0, 0);
|
||||
if (hvrc != 0) {
|
||||
printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n",
|
||||
(int)hvrc);
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_completion(&we.com);
|
||||
|
||||
if (we.rc != 0) {
|
||||
if (flags != 0)
|
||||
return;
|
||||
/* try again with read only flag set */
|
||||
flags = vioblockflags_ro;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* Send the close event to OS/400. We DON'T expect a response */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockclose,
|
||||
HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
0, VIOVERSION << 16,
|
||||
((u64)unit << 48) | ((u64)flags << 32),
|
||||
0, 0, 0);
|
||||
if (hvrc != 0) {
|
||||
printk(KERN_WARNING "probe_disk: "
|
||||
"bad rc sending event to OS/400 %d\n", (int)hvrc);
|
||||
return;
|
||||
}
|
||||
|
||||
do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit,
|
||||
"block", "IBM,iSeries-viodasd", NULL);
|
||||
}
|
||||
|
||||
static void __init get_viodasd_info(struct device_node *vio_root)
|
||||
{
|
||||
int rc;
|
||||
u32 unit;
|
||||
|
||||
rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING "get_viodasd_info: "
|
||||
"error opening path to host partition %d\n",
|
||||
viopath_hostLp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize our request handler */
|
||||
vio_setHandler(viomajorsubtype_blockio, handle_block_event);
|
||||
|
||||
for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++)
|
||||
probe_disk(vio_root, unit);
|
||||
|
||||
vio_clearHandler(viomajorsubtype_blockio);
|
||||
viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2);
|
||||
}
|
||||
|
||||
static void __init handle_cd_event(struct HvLpEvent *event)
|
||||
{
|
||||
struct viocdlpevent *bevent;
|
||||
struct vio_waitevent *pwe;
|
||||
|
||||
if (!event)
|
||||
/* Notification that a partition went away! */
|
||||
return;
|
||||
|
||||
/* First, we should NEVER get an int here...only acks */
|
||||
if (hvlpevent_is_int(event)) {
|
||||
printk(KERN_WARNING "handle_cd_event: got an unexpected int\n");
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bevent = (struct viocdlpevent *)event;
|
||||
|
||||
switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
|
||||
case viocdgetinfo:
|
||||
pwe = (struct vio_waitevent *)event->xCorrelationToken;
|
||||
pwe->rc = event->xRc;
|
||||
pwe->sub_result = bevent->sub_result;
|
||||
complete(&pwe->com);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "handle_cd_event: "
|
||||
"message with unexpected subtype %0x04X!\n",
|
||||
event->xSubtype & VIOMINOR_SUBTYPE_MASK);
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __init get_viocd_info(struct device_node *vio_root)
|
||||
{
|
||||
HvLpEvent_Rc hvrc;
|
||||
u32 unit;
|
||||
struct vio_waitevent we;
|
||||
struct vio_resource *unitinfo;
|
||||
dma_addr_t unitinfo_dmaaddr;
|
||||
int ret;
|
||||
|
||||
ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING
|
||||
"get_viocd_info: error opening path to host partition %d\n",
|
||||
viopath_hostLp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize our request handler */
|
||||
vio_setHandler(viomajorsubtype_cdio, handle_cd_event);
|
||||
|
||||
unitinfo = iseries_hv_alloc(
|
||||
sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
|
||||
&unitinfo_dmaaddr, GFP_ATOMIC);
|
||||
if (!unitinfo) {
|
||||
printk(KERN_WARNING
|
||||
"get_viocd_info: error allocating unitinfo\n");
|
||||
goto clear_handler;
|
||||
}
|
||||
|
||||
memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS);
|
||||
|
||||
init_completion(&we.com);
|
||||
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_cdio | viocdgetinfo,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0,
|
||||
sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0);
|
||||
if (hvrc != HvLpEvent_Rc_Good) {
|
||||
printk(KERN_WARNING
|
||||
"get_viocd_info: cdrom error sending event. rc %d\n",
|
||||
(int)hvrc);
|
||||
goto hv_free;
|
||||
}
|
||||
|
||||
wait_for_completion(&we.com);
|
||||
|
||||
if (we.rc) {
|
||||
printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n",
|
||||
we.rc, we.sub_result);
|
||||
goto hv_free;
|
||||
}
|
||||
|
||||
for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) &&
|
||||
unitinfo[unit].rsrcname[0]; unit++) {
|
||||
if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit,
|
||||
"block", "IBM,iSeries-viocd", &unitinfo[unit]))
|
||||
break;
|
||||
}
|
||||
|
||||
hv_free:
|
||||
iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS,
|
||||
unitinfo, unitinfo_dmaaddr);
|
||||
clear_handler:
|
||||
vio_clearHandler(viomajorsubtype_cdio);
|
||||
viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2);
|
||||
}
|
||||
|
||||
/* Handle interrupt events for tape */
|
||||
static void __init handle_tape_event(struct HvLpEvent *event)
|
||||
{
|
||||
struct vio_waitevent *we;
|
||||
struct viotapelpevent *tevent = (struct viotapelpevent *)event;
|
||||
|
||||
if (event == NULL)
|
||||
/* Notification that a partition went away! */
|
||||
return;
|
||||
|
||||
we = (struct vio_waitevent *)event->xCorrelationToken;
|
||||
switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
|
||||
case viotapegetinfo:
|
||||
we->rc = tevent->sub_type_result;
|
||||
complete(&we->com);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "handle_tape_event: weird ack\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void __init get_viotape_info(struct device_node *vio_root)
|
||||
{
|
||||
HvLpEvent_Rc hvrc;
|
||||
u32 unit;
|
||||
struct vio_resource *unitinfo;
|
||||
dma_addr_t unitinfo_dmaaddr;
|
||||
size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES;
|
||||
struct vio_waitevent we;
|
||||
int ret;
|
||||
|
||||
init_completion(&we.com);
|
||||
|
||||
ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "get_viotape_info: "
|
||||
"error on viopath_open to hostlp %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
vio_setHandler(viomajorsubtype_tape, handle_tape_event);
|
||||
|
||||
unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC);
|
||||
if (!unitinfo)
|
||||
goto clear_handler;
|
||||
|
||||
memset(unitinfo, 0, len);
|
||||
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_tape | viotapegetinfo,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)&we, VIOVERSION << 16,
|
||||
unitinfo_dmaaddr, len, 0, 0);
|
||||
if (hvrc != HvLpEvent_Rc_Good) {
|
||||
printk(KERN_WARNING "get_viotape_info: hv error on op %d\n",
|
||||
(int)hvrc);
|
||||
goto hv_free;
|
||||
}
|
||||
|
||||
wait_for_completion(&we.com);
|
||||
|
||||
for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) &&
|
||||
unitinfo[unit].rsrcname[0]; unit++) {
|
||||
if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit,
|
||||
unit, "byte", "IBM,iSeries-viotape",
|
||||
&unitinfo[unit]))
|
||||
break;
|
||||
}
|
||||
|
||||
hv_free:
|
||||
iseries_hv_free(len, unitinfo, unitinfo_dmaaddr);
|
||||
clear_handler:
|
||||
vio_clearHandler(viomajorsubtype_tape);
|
||||
viopath_close(viopath_hostLp, viomajorsubtype_tape, 2);
|
||||
}
|
||||
|
||||
static int __init iseries_vio_init(void)
|
||||
{
|
||||
struct device_node *vio_root;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
goto out;
|
||||
|
||||
iommu_vio_init();
|
||||
|
||||
vio_root = of_find_node_by_path("/vdevice");
|
||||
if (!vio_root)
|
||||
goto out;
|
||||
|
||||
if (viopath_hostLp == HvLpIndexInvalid) {
|
||||
vio_set_hostlp();
|
||||
/* If we don't have a host, bail out */
|
||||
if (viopath_hostLp == HvLpIndexInvalid)
|
||||
goto put_node;
|
||||
}
|
||||
|
||||
get_viodasd_info(vio_root);
|
||||
get_viocd_info(vio_root);
|
||||
get_viotape_info(vio_root);
|
||||
|
||||
ret = 0;
|
||||
|
||||
put_node:
|
||||
of_node_put(vio_root);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
arch_initcall(iseries_vio_init);
|
|
@ -1,677 +0,0 @@
|
|||
/* -*- linux-c -*-
|
||||
*
|
||||
* iSeries Virtual I/O Message Path code
|
||||
*
|
||||
* Authors: Dave Boutcher <boutcher@us.ibm.com>
|
||||
* Ryan Arnold <ryanarn@us.ibm.com>
|
||||
* Colin Devilbiss <devilbis@us.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2000-2005 IBM Corporation
|
||||
*
|
||||
* This code is used by the iSeries virtual disk, cd,
|
||||
* tape, and console to communicate with OS/400 in another
|
||||
* partition.
|
||||
*
|
||||
* 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) anyu 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/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_lp_config.h>
|
||||
#include <asm/iseries/mf.h>
|
||||
#include <asm/iseries/vio.h>
|
||||
|
||||
/* Status of the path to each other partition in the system.
|
||||
* This is overkill, since we will only ever establish connections
|
||||
* to our hosting partition and the primary partition on the system.
|
||||
* But this allows for other support in the future.
|
||||
*/
|
||||
static struct viopathStatus {
|
||||
int isOpen; /* Did we open the path? */
|
||||
int isActive; /* Do we have a mon msg outstanding */
|
||||
int users[VIO_MAX_SUBTYPES];
|
||||
HvLpInstanceId mSourceInst;
|
||||
HvLpInstanceId mTargetInst;
|
||||
int numberAllocated;
|
||||
} viopathStatus[HVMAXARCHITECTEDLPS];
|
||||
|
||||
static DEFINE_SPINLOCK(statuslock);
|
||||
|
||||
/*
|
||||
* For each kind of event we allocate a buffer that is
|
||||
* guaranteed not to cross a page boundary
|
||||
*/
|
||||
static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256]
|
||||
__attribute__((__aligned__(4096)));
|
||||
static atomic_t event_buffer_available[VIO_MAX_SUBTYPES];
|
||||
static int event_buffer_initialised;
|
||||
|
||||
static void handleMonitorEvent(struct HvLpEvent *event);
|
||||
|
||||
/*
|
||||
* We use this structure to handle asynchronous responses. The caller
|
||||
* blocks on the semaphore and the handler posts the semaphore. However,
|
||||
* if system_state is not SYSTEM_RUNNING, then wait_atomic is used ...
|
||||
*/
|
||||
struct alloc_parms {
|
||||
struct completion done;
|
||||
int number;
|
||||
atomic_t wait_atomic;
|
||||
int used_wait_atomic;
|
||||
};
|
||||
|
||||
/* Put a sequence number in each mon msg. The value is not
|
||||
* important. Start at something other than 0 just for
|
||||
* readability. wrapping this is ok.
|
||||
*/
|
||||
static u8 viomonseq = 22;
|
||||
|
||||
/* Our hosting logical partition. We get this at startup
|
||||
* time, and different modules access this variable directly.
|
||||
*/
|
||||
HvLpIndex viopath_hostLp = HvLpIndexInvalid;
|
||||
EXPORT_SYMBOL(viopath_hostLp);
|
||||
HvLpIndex viopath_ourLp = HvLpIndexInvalid;
|
||||
EXPORT_SYMBOL(viopath_ourLp);
|
||||
|
||||
/* For each kind of incoming event we set a pointer to a
|
||||
* routine to call.
|
||||
*/
|
||||
static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES];
|
||||
|
||||
#define VIOPATH_KERN_WARN KERN_WARNING "viopath: "
|
||||
#define VIOPATH_KERN_INFO KERN_INFO "viopath: "
|
||||
|
||||
static int proc_viopath_show(struct seq_file *m, void *v)
|
||||
{
|
||||
char *buf;
|
||||
u16 vlanMap;
|
||||
dma_addr_t handle;
|
||||
HvLpEvent_Rc hvrc;
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
struct device_node *node;
|
||||
const char *sysid;
|
||||
|
||||
buf = kzalloc(HW_PAGE_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
handle = iseries_hv_map(buf, HW_PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_config | vioconfigget,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)&done, VIOVERSION << 16,
|
||||
((u64)handle) << 32, HW_PAGE_SIZE, 0, 0);
|
||||
|
||||
if (hvrc != HvLpEvent_Rc_Good)
|
||||
printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc);
|
||||
|
||||
wait_for_completion(&done);
|
||||
|
||||
vlanMap = HvLpConfig_getVirtualLanIndexMap();
|
||||
|
||||
buf[HW_PAGE_SIZE-1] = '\0';
|
||||
seq_printf(m, "%s", buf);
|
||||
|
||||
iseries_hv_unmap(handle, HW_PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
kfree(buf);
|
||||
|
||||
seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap);
|
||||
|
||||
node = of_find_node_by_path("/");
|
||||
sysid = NULL;
|
||||
if (node != NULL)
|
||||
sysid = of_get_property(node, "system-id", NULL);
|
||||
|
||||
if (sysid == NULL)
|
||||
seq_printf(m, "SRLNBR=<UNKNOWN>\n");
|
||||
else
|
||||
/* Skip "IBM," on front of serial number, see dt.c */
|
||||
seq_printf(m, "SRLNBR=%s\n", sysid + 4);
|
||||
|
||||
of_node_put(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_viopath_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, proc_viopath_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations proc_viopath_operations = {
|
||||
.open = proc_viopath_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init vio_proc_init(void)
|
||||
{
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES))
|
||||
return 0;
|
||||
|
||||
proc_create("iSeries/config", 0, NULL, &proc_viopath_operations);
|
||||
return 0;
|
||||
}
|
||||
__initcall(vio_proc_init);
|
||||
|
||||
/* See if a given LP is active. Allow for invalid lps to be passed in
|
||||
* and just return invalid
|
||||
*/
|
||||
int viopath_isactive(HvLpIndex lp)
|
||||
{
|
||||
if (lp == HvLpIndexInvalid)
|
||||
return 0;
|
||||
if (lp < HVMAXARCHITECTEDLPS)
|
||||
return viopathStatus[lp].isActive;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(viopath_isactive);
|
||||
|
||||
/*
|
||||
* We cache the source and target instance ids for each
|
||||
* partition.
|
||||
*/
|
||||
HvLpInstanceId viopath_sourceinst(HvLpIndex lp)
|
||||
{
|
||||
return viopathStatus[lp].mSourceInst;
|
||||
}
|
||||
EXPORT_SYMBOL(viopath_sourceinst);
|
||||
|
||||
HvLpInstanceId viopath_targetinst(HvLpIndex lp)
|
||||
{
|
||||
return viopathStatus[lp].mTargetInst;
|
||||
}
|
||||
EXPORT_SYMBOL(viopath_targetinst);
|
||||
|
||||
/*
|
||||
* Send a monitor message. This is a message with the acknowledge
|
||||
* bit on that the other side will NOT explicitly acknowledge. When
|
||||
* the other side goes down, the hypervisor will acknowledge any
|
||||
* outstanding messages....so we will know when the other side dies.
|
||||
*/
|
||||
static void sendMonMsg(HvLpIndex remoteLp)
|
||||
{
|
||||
HvLpEvent_Rc hvrc;
|
||||
|
||||
viopathStatus[remoteLp].mSourceInst =
|
||||
HvCallEvent_getSourceLpInstanceId(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
viopathStatus[remoteLp].mTargetInst =
|
||||
HvCallEvent_getTargetLpInstanceId(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
|
||||
/*
|
||||
* Deliberately ignore the return code here. if we call this
|
||||
* more than once, we don't care.
|
||||
*/
|
||||
vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent);
|
||||
|
||||
hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck,
|
||||
HvLpEvent_AckType_DeferredAck,
|
||||
viopathStatus[remoteLp].mSourceInst,
|
||||
viopathStatus[remoteLp].mTargetInst,
|
||||
viomonseq++, 0, 0, 0, 0, 0);
|
||||
|
||||
if (hvrc == HvLpEvent_Rc_Good)
|
||||
viopathStatus[remoteLp].isActive = 1;
|
||||
else {
|
||||
printk(VIOPATH_KERN_WARN "could not connect to partition %d\n",
|
||||
remoteLp);
|
||||
viopathStatus[remoteLp].isActive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleMonitorEvent(struct HvLpEvent *event)
|
||||
{
|
||||
HvLpIndex remoteLp;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* This handler is _also_ called as part of the loop
|
||||
* at the end of this routine, so it must be able to
|
||||
* ignore NULL events...
|
||||
*/
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
/*
|
||||
* First see if this is just a normal monitor message from the
|
||||
* other partition
|
||||
*/
|
||||
if (hvlpevent_is_int(event)) {
|
||||
remoteLp = event->xSourceLp;
|
||||
if (!viopathStatus[remoteLp].isActive)
|
||||
sendMonMsg(remoteLp);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This path is for an acknowledgement; the other partition
|
||||
* died
|
||||
*/
|
||||
remoteLp = event->xTargetLp;
|
||||
if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) ||
|
||||
(event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) {
|
||||
printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp);
|
||||
|
||||
viopathStatus[remoteLp].isActive = 0;
|
||||
|
||||
/*
|
||||
* For each active handler, pass them a NULL
|
||||
* message to indicate that the other partition
|
||||
* died
|
||||
*/
|
||||
for (i = 0; i < VIO_MAX_SUBTYPES; i++) {
|
||||
if (vio_handler[i] != NULL)
|
||||
(*vio_handler[i])(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int vio_setHandler(int subtype, vio_event_handler_t *beh)
|
||||
{
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
|
||||
return -EINVAL;
|
||||
if (vio_handler[subtype] != NULL)
|
||||
return -EBUSY;
|
||||
vio_handler[subtype] = beh;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(vio_setHandler);
|
||||
|
||||
int vio_clearHandler(int subtype)
|
||||
{
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
|
||||
return -EINVAL;
|
||||
if (vio_handler[subtype] == NULL)
|
||||
return -EAGAIN;
|
||||
vio_handler[subtype] = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(vio_clearHandler);
|
||||
|
||||
static void handleConfig(struct HvLpEvent *event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
if (hvlpevent_is_int(event)) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"unexpected config request from partition %d",
|
||||
event->xSourceLp);
|
||||
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
complete((struct completion *)event->xCorrelationToken);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization of the hosting partition
|
||||
*/
|
||||
void vio_set_hostlp(void)
|
||||
{
|
||||
/*
|
||||
* If this has already been set then we DON'T want to either change
|
||||
* it or re-register the proc file system
|
||||
*/
|
||||
if (viopath_hostLp != HvLpIndexInvalid)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Figure out our hosting partition. This isn't allowed to change
|
||||
* while we're active
|
||||
*/
|
||||
viopath_ourLp = HvLpConfig_getLpIndex();
|
||||
viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp);
|
||||
|
||||
if (viopath_hostLp != HvLpIndexInvalid)
|
||||
vio_setHandler(viomajorsubtype_config, handleConfig);
|
||||
}
|
||||
EXPORT_SYMBOL(vio_set_hostlp);
|
||||
|
||||
static void vio_handleEvent(struct HvLpEvent *event)
|
||||
{
|
||||
HvLpIndex remoteLp;
|
||||
int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK)
|
||||
>> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
|
||||
if (hvlpevent_is_int(event)) {
|
||||
remoteLp = event->xSourceLp;
|
||||
/*
|
||||
* The isActive is checked because if the hosting partition
|
||||
* went down and came back up it would not be active but it
|
||||
* would have different source and target instances, in which
|
||||
* case we'd want to reset them. This case really protects
|
||||
* against an unauthorized active partition sending interrupts
|
||||
* or acks to this linux partition.
|
||||
*/
|
||||
if (viopathStatus[remoteLp].isActive
|
||||
&& (event->xSourceInstanceId !=
|
||||
viopathStatus[remoteLp].mTargetInst)) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"message from invalid partition. "
|
||||
"int msg rcvd, source inst (%d) doesn't match (%d)\n",
|
||||
viopathStatus[remoteLp].mTargetInst,
|
||||
event->xSourceInstanceId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (viopathStatus[remoteLp].isActive
|
||||
&& (event->xTargetInstanceId !=
|
||||
viopathStatus[remoteLp].mSourceInst)) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"message from invalid partition. "
|
||||
"int msg rcvd, target inst (%d) doesn't match (%d)\n",
|
||||
viopathStatus[remoteLp].mSourceInst,
|
||||
event->xTargetInstanceId);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
remoteLp = event->xTargetLp;
|
||||
if (event->xSourceInstanceId !=
|
||||
viopathStatus[remoteLp].mSourceInst) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"message from invalid partition. "
|
||||
"ack msg rcvd, source inst (%d) doesn't match (%d)\n",
|
||||
viopathStatus[remoteLp].mSourceInst,
|
||||
event->xSourceInstanceId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->xTargetInstanceId !=
|
||||
viopathStatus[remoteLp].mTargetInst) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"message from invalid partition. "
|
||||
"viopath: ack msg rcvd, target inst (%d) doesn't match (%d)\n",
|
||||
viopathStatus[remoteLp].mTargetInst,
|
||||
event->xTargetInstanceId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (vio_handler[subtype] == NULL) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"unexpected virtual io event subtype %d from partition %d\n",
|
||||
event->xSubtype, remoteLp);
|
||||
/* No handler. Ack if necessary */
|
||||
if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* This innocuous little line is where all the real work happens */
|
||||
(*vio_handler[subtype])(event);
|
||||
}
|
||||
|
||||
static void viopath_donealloc(void *parm, int number)
|
||||
{
|
||||
struct alloc_parms *parmsp = parm;
|
||||
|
||||
parmsp->number = number;
|
||||
if (parmsp->used_wait_atomic)
|
||||
atomic_set(&parmsp->wait_atomic, 0);
|
||||
else
|
||||
complete(&parmsp->done);
|
||||
}
|
||||
|
||||
static int allocateEvents(HvLpIndex remoteLp, int numEvents)
|
||||
{
|
||||
struct alloc_parms parms;
|
||||
|
||||
if (system_state != SYSTEM_RUNNING) {
|
||||
parms.used_wait_atomic = 1;
|
||||
atomic_set(&parms.wait_atomic, 1);
|
||||
} else {
|
||||
parms.used_wait_atomic = 0;
|
||||
init_completion(&parms.done);
|
||||
}
|
||||
mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */
|
||||
numEvents, &viopath_donealloc, &parms);
|
||||
if (system_state != SYSTEM_RUNNING) {
|
||||
while (atomic_read(&parms.wait_atomic))
|
||||
mb();
|
||||
} else
|
||||
wait_for_completion(&parms.done);
|
||||
return parms.number;
|
||||
}
|
||||
|
||||
int viopath_open(HvLpIndex remoteLp, int subtype, int numReq)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
int tempNumAllocated;
|
||||
|
||||
if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
|
||||
return -EINVAL;
|
||||
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&statuslock, flags);
|
||||
|
||||
if (!event_buffer_initialised) {
|
||||
for (i = 0; i < VIO_MAX_SUBTYPES; i++)
|
||||
atomic_set(&event_buffer_available[i], 1);
|
||||
event_buffer_initialised = 1;
|
||||
}
|
||||
|
||||
viopathStatus[remoteLp].users[subtype]++;
|
||||
|
||||
if (!viopathStatus[remoteLp].isOpen) {
|
||||
viopathStatus[remoteLp].isOpen = 1;
|
||||
HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo);
|
||||
|
||||
/*
|
||||
* Don't hold the spinlock during an operation that
|
||||
* can sleep.
|
||||
*/
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
tempNumAllocated = allocateEvents(remoteLp, 1);
|
||||
spin_lock_irqsave(&statuslock, flags);
|
||||
|
||||
viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
|
||||
|
||||
if (viopathStatus[remoteLp].numberAllocated == 0) {
|
||||
HvCallEvent_closeLpEventPath(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
viopathStatus[remoteLp].mSourceInst =
|
||||
HvCallEvent_getSourceLpInstanceId(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
viopathStatus[remoteLp].mTargetInst =
|
||||
HvCallEvent_getTargetLpInstanceId(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo,
|
||||
&vio_handleEvent);
|
||||
sendMonMsg(remoteLp);
|
||||
printk(VIOPATH_KERN_INFO "opening connection to partition %d, "
|
||||
"setting sinst %d, tinst %d\n",
|
||||
remoteLp, viopathStatus[remoteLp].mSourceInst,
|
||||
viopathStatus[remoteLp].mTargetInst);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
tempNumAllocated = allocateEvents(remoteLp, numReq);
|
||||
spin_lock_irqsave(&statuslock, flags);
|
||||
viopathStatus[remoteLp].numberAllocated += tempNumAllocated;
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(viopath_open);
|
||||
|
||||
int viopath_close(HvLpIndex remoteLp, int subtype, int numReq)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
int numOpen;
|
||||
struct alloc_parms parms;
|
||||
|
||||
if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid))
|
||||
return -EINVAL;
|
||||
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&statuslock, flags);
|
||||
/*
|
||||
* If the viopath_close somehow gets called before a
|
||||
* viopath_open it could decrement to -1 which is a non
|
||||
* recoverable state so we'll prevent this from
|
||||
* happening.
|
||||
*/
|
||||
if (viopathStatus[remoteLp].users[subtype] > 0)
|
||||
viopathStatus[remoteLp].users[subtype]--;
|
||||
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
|
||||
parms.used_wait_atomic = 0;
|
||||
init_completion(&parms.done);
|
||||
mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo,
|
||||
numReq, &viopath_donealloc, &parms);
|
||||
wait_for_completion(&parms.done);
|
||||
|
||||
spin_lock_irqsave(&statuslock, flags);
|
||||
for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++)
|
||||
numOpen += viopathStatus[remoteLp].users[i];
|
||||
|
||||
if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) {
|
||||
printk(VIOPATH_KERN_INFO "closing connection to partition %d\n",
|
||||
remoteLp);
|
||||
|
||||
HvCallEvent_closeLpEventPath(remoteLp,
|
||||
HvLpEvent_Type_VirtualIo);
|
||||
viopathStatus[remoteLp].isOpen = 0;
|
||||
viopathStatus[remoteLp].isActive = 0;
|
||||
|
||||
for (i = 0; i < VIO_MAX_SUBTYPES; i++)
|
||||
atomic_set(&event_buffer_available[i], 0);
|
||||
event_buffer_initialised = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&statuslock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(viopath_close);
|
||||
|
||||
void *vio_get_event_buffer(int subtype)
|
||||
{
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES))
|
||||
return NULL;
|
||||
|
||||
if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0)
|
||||
return &event_buffer[subtype * 256];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(vio_get_event_buffer);
|
||||
|
||||
void vio_free_event_buffer(int subtype, void *buffer)
|
||||
{
|
||||
subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT;
|
||||
if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"unexpected subtype %d freeing event buffer\n", subtype);
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_read(&event_buffer_available[subtype]) != 0) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"freeing unallocated event buffer, subtype %d\n",
|
||||
subtype);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer != &event_buffer[subtype * 256]) {
|
||||
printk(VIOPATH_KERN_WARN
|
||||
"freeing invalid event buffer, subtype %d\n", subtype);
|
||||
}
|
||||
|
||||
atomic_set(&event_buffer_available[subtype], 1);
|
||||
}
|
||||
EXPORT_SYMBOL(vio_free_event_buffer);
|
||||
|
||||
static const struct vio_error_entry vio_no_error =
|
||||
{ 0, 0, "Non-VIO Error" };
|
||||
static const struct vio_error_entry vio_unknown_error =
|
||||
{ 0, EIO, "Unknown Error" };
|
||||
|
||||
static const struct vio_error_entry vio_default_errors[] = {
|
||||
{0x0001, EIO, "No Connection"},
|
||||
{0x0002, EIO, "No Receiver"},
|
||||
{0x0003, EIO, "No Buffer Available"},
|
||||
{0x0004, EBADRQC, "Invalid Message Type"},
|
||||
{0x0000, 0, NULL},
|
||||
};
|
||||
|
||||
const struct vio_error_entry *vio_lookup_rc(
|
||||
const struct vio_error_entry *local_table, u16 rc)
|
||||
{
|
||||
const struct vio_error_entry *cur;
|
||||
|
||||
if (!rc)
|
||||
return &vio_no_error;
|
||||
if (local_table)
|
||||
for (cur = local_table; cur->rc; ++cur)
|
||||
if (cur->rc == rc)
|
||||
return cur;
|
||||
for (cur = vio_default_errors; cur->rc; ++cur)
|
||||
if (cur->rc == rc)
|
||||
return cur;
|
||||
return &vio_unknown_error;
|
||||
}
|
||||
EXPORT_SYMBOL(vio_lookup_rc);
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* 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_VPD_AREAS_H
|
||||
#define _ISERIES_VPD_AREAS_H
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*/
|
||||
|
||||
#include <asm/types.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 ItVpdAreas {
|
||||
u32 xSlicDesc; // Descriptor 000-003
|
||||
u16 xSlicSize; // Size of this control block 004-005
|
||||
u16 xPlicAdjustVpdLens:1; // Flag to indicate new interface006-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 labels018-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
|
||||
const void *xSlicVpdAdrs[ItVpdMaxEntries];// Array of VPD buffers 130-1EF
|
||||
};
|
||||
|
||||
extern const struct ItVpdAreas itVpdAreas;
|
||||
|
||||
#endif /* _ISERIES_VPD_AREAS_H */
|
Loading…
Reference in a new issue