Skip to content

Commit

Permalink
[PATCH] powerpc: move iSeries PCI devices to the device tree
Browse files Browse the repository at this point in the history
Move the probing of PCI devices to setup.c and put them all into the
flattened device tree.  The later probing is now done by traversing the
device tree.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Stephen Rothwell authored and Paul Mackerras committed May 24, 2006
1 parent 66b3851 commit 0d177df
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 200 deletions.
7 changes: 4 additions & 3 deletions arch/powerpc/platforms/iseries/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <asm/iseries/it_lp_queue.h>

#include "irq.h"
#include "pci.h"
#include "call_pci.h"

#if defined(CONFIG_SMP)
Expand Down Expand Up @@ -312,12 +313,12 @@ static hw_irq_controller iSeries_IRQ_handler = {
* Note that sub_bus is always 0 (at the moment at least).
*/
int __init iSeries_allocate_IRQ(HvBusNumber bus,
HvSubBusNumber sub_bus, HvAgentId dev_id)
HvSubBusNumber sub_bus, u32 bsubbus)
{
int virtirq;
unsigned int realirq;
u8 idsel = (dev_id >> 4);
u8 function = dev_id & 7;
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;
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/platforms/iseries/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define _ISERIES_IRQ_H

extern void iSeries_init_IRQ(void);
extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, HvAgentId);
extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, u32);
extern void iSeries_activate_IRQs(void);
extern int iSeries_get_irq(struct pt_regs *);

Expand Down
273 changes: 77 additions & 196 deletions arch/powerpc/platforms/iseries/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,9 @@
* Forward declares of prototypes.
*/
static struct device_node *find_Device_Node(int bus, int devfn);
static void scan_PHB_slots(struct pci_controller *Phb);
static void scan_EADS_bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel);
static int scan_bridge_slot(HvBusNumber Bus, struct HvCallPci_BridgeInfo *Info);

LIST_HEAD(iSeries_Global_Device_List);

static int DeviceCount;

static int Pci_Retry_Max = 3; /* Only retry 3 times */
static int Pci_Error_Flag = 1; /* Set Retry Error on. */

Expand Down Expand Up @@ -161,32 +156,6 @@ static void pci_Log_Error(char *Error_Text, int Bus, int SubBus,
Error_Text, Bus, SubBus, AgentId, HvRc);
}

/*
* build_device_node(u16 Bus, int SubBus, u8 DevFn)
*/
static struct device_node *build_device_node(HvBusNumber Bus,
HvSubBusNumber SubBus, int AgentId, int Function)
{
struct device_node *node;
struct pci_dn *pdn;

node = kzalloc(sizeof(struct device_node), GFP_KERNEL);
if (node == NULL)
return NULL;
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
if (pdn == NULL) {
kfree(node);
return NULL;
}
node->data = pdn;
pdn->node = node;
list_add_tail(&pdn->Device_List, &iSeries_Global_Device_List);
pdn->busno = Bus;
pdn->bussubno = SubBus;
pdn->devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(AgentId), Function);
return node;
}

/*
* iSeries_pcibios_init
*
Expand All @@ -199,33 +168,86 @@ static struct device_node *build_device_node(HvBusNumber Bus,
void iSeries_pcibios_init(void)
{
struct pci_controller *phb;
HvBusNumber bus;

/* Check all possible buses. */
for (bus = 0; bus < 256; bus++) {
int ret = HvCallXm_testBus(bus);
if (ret == 0) {
printk("bus %d appears to exist\n", bus);

phb = pcibios_alloc_controller(NULL);
if (phb == NULL)
return -ENOMEM;
struct device_node *node;
struct device_node *dn;

for_each_node_by_type(node, "pci") {
HvBusNumber bus;
u32 *busp;

busp = (u32 *)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;

phb->pci_mem_offset = phb->local_number = bus;
phb->first_busno = bus;
phb->last_busno = bus;
phb->ops = &iSeries_pci_ops;

/* Find and connect the devices. */
for (dn = NULL; (dn = of_get_next_child(node, dn)) != NULL;) {
struct pci_dn *pdn;
u8 irq;
int err;
u32 *agent;
u32 *reg;
u32 *lsn;

reg = (u32 *)get_property(dn, "reg", NULL);
if (reg == NULL) {
printk(KERN_DEBUG "no reg property!\n");
continue;
}
busp = (u32 *)get_property(dn, "linux,subbus", NULL);
if (busp == NULL) {
printk(KERN_DEBUG "no subbus property!\n");
continue;
}
agent = (u32 *)get_property(dn, "linux,agent-id", NULL);
if (agent == NULL) {
printk(KERN_DEBUG "no agent-id\n");
continue;
}
lsn = (u32 *)get_property(dn,
"linux,logical-slot-number", NULL);
if (lsn == NULL) {
printk(KERN_DEBUG "no logical-slot-number\n");
continue;
}

phb->pci_mem_offset = phb->local_number = bus;
phb->first_busno = bus;
phb->last_busno = bus;
phb->ops = &iSeries_pci_ops;
irq = iSeries_allocate_IRQ(bus, 0, *busp);
err = HvCallXm_connectBusUnit(bus, *busp, *agent, irq);
if (err) {
pci_Log_Error("Connect Bus Unit",
bus, *busp, *agent, err);
continue;
}
err = HvCallPci_configStore8(bus, *busp, *agent,
PCI_INTERRUPT_LINE, irq);
if (err) {
pci_Log_Error("PciCfgStore Irq Failed!",
bus, *busp, *agent, err);
continue;
}

/* Find and connect the devices. */
scan_PHB_slots(phb);
pdn = kzalloc(sizeof(*pdn), GFP_KERNEL);
if (pdn == NULL)
return;
dn->data = pdn;
pdn->node = dn;
pdn->busno = bus;
pdn->devfn = (reg[0] >> 8) & 0xff;
pdn->bussubno = *busp;
pdn->Irq = irq;
pdn->LogicalSlot = *lsn;
list_add_tail(&pdn->Device_List,
&iSeries_Global_Device_List);
}
/*
* Check for Unexpected Return code, a clue that something
* has gone wrong.
*/
else if (ret != 0x0301)
printk(KERN_ERR "Unexpected Return on Probe(0x%04X): 0x%04X",
bus, ret);
}
}

Expand Down Expand Up @@ -271,147 +293,6 @@ void pcibios_fixup_resources(struct pci_dev *pdev)
{
}

/*
* Loop through each node function to find usable EADs bridges.
*/
static void scan_PHB_slots(struct pci_controller *Phb)
{
struct HvCallPci_DeviceInfo *DevInfo;
HvBusNumber bus = Phb->local_number; /* System Bus */
const HvSubBusNumber SubBus = 0; /* EADs is always 0. */
int HvRc = 0;
int IdSel;
const int MaxAgents = 8;

DevInfo = kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL);
if (DevInfo == NULL)
return;

/*
* Probe for EADs Bridges
*/
for (IdSel = 1; IdSel < MaxAgents; ++IdSel) {
HvRc = HvCallPci_getDeviceInfo(bus, SubBus, IdSel,
iseries_hv_addr(DevInfo),
sizeof(struct HvCallPci_DeviceInfo));
if (HvRc == 0) {
if (DevInfo->deviceType == HvCallPci_NodeDevice)
scan_EADS_bridge(bus, SubBus, IdSel);
else
printk("PCI: Invalid System Configuration(0x%02X)"
" for bus 0x%02x id 0x%02x.\n",
DevInfo->deviceType, bus, IdSel);
}
else
pci_Log_Error("getDeviceInfo", bus, SubBus, IdSel, HvRc);
}
kfree(DevInfo);
}

static void scan_EADS_bridge(HvBusNumber bus, HvSubBusNumber SubBus,
int IdSel)
{
struct HvCallPci_BridgeInfo *BridgeInfo;
HvAgentId AgentId;
int Function;
int HvRc;

BridgeInfo = (struct HvCallPci_BridgeInfo *)
kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL);
if (BridgeInfo == NULL)
return;

/* Note: hvSubBus and irq is always be 0 at this level! */
for (Function = 0; Function < 8; ++Function) {
AgentId = ISERIES_PCI_AGENTID(IdSel, Function);
HvRc = HvCallXm_connectBusUnit(bus, SubBus, AgentId, 0);
if (HvRc == 0) {
printk("found device at bus %d idsel %d func %d (AgentId %x)\n",
bus, IdSel, Function, AgentId);
/* Connect EADs: 0x18.00.12 = 0x00 */
HvRc = HvCallPci_getBusUnitInfo(bus, SubBus, AgentId,
iseries_hv_addr(BridgeInfo),
sizeof(struct HvCallPci_BridgeInfo));
if (HvRc == 0) {
printk("bridge info: type %x subbus %x maxAgents %x maxsubbus %x logslot %x\n",
BridgeInfo->busUnitInfo.deviceType,
BridgeInfo->subBusNumber,
BridgeInfo->maxAgents,
BridgeInfo->maxSubBusNumber,
BridgeInfo->logicalSlotNumber);
if (BridgeInfo->busUnitInfo.deviceType ==
HvCallPci_BridgeDevice) {
/* Scan_Bridge_Slot...: 0x18.00.12 */
scan_bridge_slot(bus, BridgeInfo);
} else
printk("PCI: Invalid Bridge Configuration(0x%02X)",
BridgeInfo->busUnitInfo.deviceType);
}
} else if (HvRc != 0x000B)
pci_Log_Error("EADs Connect",
bus, SubBus, AgentId, HvRc);
}
kfree(BridgeInfo);
}

/*
* This assumes that the node slot is always on the primary bus!
*/
static int scan_bridge_slot(HvBusNumber Bus,
struct HvCallPci_BridgeInfo *BridgeInfo)
{
struct device_node *node;
HvSubBusNumber SubBus = BridgeInfo->subBusNumber;
u16 VendorId = 0;
int HvRc = 0;
u8 Irq = 0;
int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus);
int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus);
HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function);

/* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */
Irq = iSeries_allocate_IRQ(Bus, 0, EADsIdSel);

/*
* Connect all functions of any device found.
*/
for (IdSel = 1; IdSel <= BridgeInfo->maxAgents; ++IdSel) {
for (Function = 0; Function < 8; ++Function) {
HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function);
HvRc = HvCallXm_connectBusUnit(Bus, SubBus,
AgentId, Irq);
if (HvRc != 0) {
pci_Log_Error("Connect Bus Unit",
Bus, SubBus, AgentId, HvRc);
continue;
}

HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId,
PCI_VENDOR_ID, &VendorId);
if (HvRc != 0) {
pci_Log_Error("Read Vendor",
Bus, SubBus, AgentId, HvRc);
continue;
}
printk("read vendor ID: %x\n", VendorId);

/* FoundDevice: 0x18.28.10 = 0x12AE */
HvRc = HvCallPci_configStore8(Bus, SubBus, AgentId,
PCI_INTERRUPT_LINE, Irq);
if (HvRc != 0)
pci_Log_Error("PciCfgStore Irq Failed!",
Bus, SubBus, AgentId, HvRc);

++DeviceCount;
node = build_device_node(Bus, SubBus, EADsIdSel, Function);
PCI_DN(node)->Irq = Irq;
PCI_DN(node)->LogicalSlot = BridgeInfo->logicalSlotNumber;

} /* for (Function = 0; Function < 8; ++Function) */
} /* for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) */
return HvRc;
}

/*
* I/0 Memory copy MUST use mmio commands on iSeries
* To do; For performance, include the hv call directly
Expand Down
Loading

0 comments on commit 0d177df

Please sign in to comment.