[PATCH] powerpc: move iSeries PCI devices to the device tree
authorStephen Rothwell <sfr@canb.auug.org.au>
Fri, 19 May 2006 06:46:28 +0000 (16:46 +1000)
committerPaul Mackerras <paulus@samba.org>
Wed, 24 May 2006 06:08:56 +0000 (16:08 +1000)
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>
arch/powerpc/platforms/iseries/irq.c
arch/powerpc/platforms/iseries/irq.h
arch/powerpc/platforms/iseries/pci.c
arch/powerpc/platforms/iseries/setup.c

index be3fbfc24e6cc2b1ecf947df77fd38f9c4f2b050..62bbbcf5ded3e27d19f5c2ed0f1d2e402171b87f 100644 (file)
@@ -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)
@@ -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;
index b9c801ba5a476d103a35d454ffd89c783d901b3c..188aa808abd7c8941c2712f365bbf1240a96b4b2 100644 (file)
@@ -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 *);
 
index 91a94747eda90280b6642edecae1117b5260ee6a..9d571e749098cadf518d255b769cb6d053186106 100644 (file)
  * 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. */
 
@@ -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
  *
@@ -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);
        }
 }
 
@@ -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
index fd6d0ebe8dddaffef66ab90812aa55849960c8eb..d83f5ed4ec1fba94690781703aad50c9e273cf49 100644 (file)
@@ -66,6 +66,8 @@
 #include "main_store.h"
 #include "call_sm.h"
 #include "call_hpt.h"
+#include "call_pci.h"
+#include "pci.h"
 
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
@@ -1000,6 +1002,207 @@ void dt_vdevices(struct iseries_flat_dt *dt)
        dt_end_node(dt);
 }
 
+/*
+ * This assumes that the node slot is always on the primary bus!
+ */
+static void 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);
+
+       /*
+        * Connect all functions of any device found.
+        */
+       for (id_sel = 1; id_sel <= bridge_info->maxAgents; id_sel++) {
+               for (function = 0; function < 8; function++) {
+                       u8 devfn;
+
+                       HvAgentId agent_id = ISERIES_PCI_AGENTID(id_sel,
+                                       function);
+                       err = HvCallXm_connectBusUnit(bus, sub_bus,
+                                       agent_id, 0);
+                       if (err) {
+                               if (err != 0x302)
+                                       printk(KERN_DEBUG
+                                               "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) {
+                               printk(KERN_DEBUG
+                                       "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) {
+                               printk(KERN_DEBUG
+                                       "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) {
+                               printk(KERN_DEBUG
+                                       "ReadClass(%x, %x, %x) == %x\n",
+                                       bus, sub_bus, agent_id, err);
+                               continue;
+                       }
+
+                       devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(eads_id_sel),
+                                       function);
+                       if (function == 0)
+                               snprintf(buf, sizeof(buf), "pci@%x",
+                                               PCI_SLOT(devfn));
+                       else
+                               snprintf(buf, sizeof(buf), "pci@%x,%d",
+                                               PCI_SLOT(devfn), function);
+                       dt_start_node(dt, buf);
+                       reg[0] = (bus << 18) | (devfn << 8);
+                       reg[1] = 0;
+                       reg[2] = 0;
+                       reg[3] = 0;
+                       reg[4] = 0;
+                       dt_prop_u32_list(dt, "reg", reg, 5);
+                       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 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)
+                               printk(KERN_DEBUG "connectBusUnit(%x, %x, %x) "
+                                               "== %x\n",
+                                               bus, sub_bus, agent_id, ret);
+                       continue;
+               }
+               printk("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;
+               printk("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
+                       printk("PCI: Invalid Bridge Configuration(0x%02X)",
+                               bridge_info.busUnitInfo.deviceType);
+       }
+}
+
+static void 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)
+                               printk(KERN_DEBUG "getDeviceInfo(%x, %x, %x) "
+                                               "== %x\n",
+                                               bus, sub_bus, id_sel, err);
+                       continue;
+               }
+               if (dev_info.deviceType != HvCallPci_NodeDevice) {
+                       printk(KERN_DEBUG "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 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)
+                               printk(KERN_ERR "Unexpected Return on Probe"
+                                               "(0x%02X): 0x%04X", bus, err);
+                       continue;
+               }
+               printk("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", "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++;
+       }
+}
+
 void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size)
 {
        u64 tmp[2];
@@ -1029,6 +1232,7 @@ void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size)
        dt_cpus(dt);
 
        dt_vdevices(dt);
+       dt_pci_devices(dt);
 
        dt_end_node(dt);