]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/platforms/iseries/setup.c
[PATCH] powerpc: move iSeries PCI devices to the device tree
[karo-tx-linux.git] / arch / powerpc / platforms / iseries / setup.c
index 3ecc4a652d82e55425f6d3c3b8441266e389e316..d83f5ed4ec1fba94690781703aad50c9e273cf49 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/major.h>
 #include <linux/root_dev.h>
 #include <linux/kernel.h>
+#include <linux/if_ether.h>    /* ETH_ALEN */
 
 #include <asm/processor.h>
 #include <asm/machdep.h>
 #include <asm/cache.h>
 #include <asm/sections.h>
 #include <asm/abs_addr.h>
+#include <asm/iseries/hv_types.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/it_exp_vpd_panel.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"
@@ -62,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)
@@ -79,9 +85,6 @@ extern void iSeries_pci_final_fixup(void);
 static void iSeries_pci_final_fixup(void) { }
 #endif
 
-/* Global Variables */
-int piranha_simulator;
-
 extern int rd_size;            /* Defined in drivers/block/rd.c */
 extern unsigned long embedded_sysmap_start;
 extern unsigned long embedded_sysmap_end;
@@ -89,10 +92,6 @@ extern unsigned long embedded_sysmap_end;
 extern unsigned long iSeries_recal_tb;
 extern unsigned long iSeries_recal_titan;
 
-static int mf_initialized;
-
-static unsigned long cmd_mem_limit;
-
 struct MemoryBlock {
        unsigned long absStart;
        unsigned long absEnd;
@@ -303,8 +302,6 @@ static void __init iSeries_init_early(void)
 {
        DBG(" -> iSeries_init_early()\n");
 
-       ppc64_firmware_features = FW_FEATURE_ISERIES;
-
        ppc64_interrupt_controller = IC_ISERIES;
 
 #if defined(CONFIG_BLK_DEV_INITRD)
@@ -342,15 +339,11 @@ static void __init iSeries_init_early(void)
 #ifdef CONFIG_SMP
        smp_init_iSeries();
 #endif
-       if (itLpNaca.xPirEnvironMode == 0)
-               piranha_simulator = 1;
 
        /* Associate Lp Event Queue 0 with processor 0 */
        HvCallEvent_setLpEventQueueInterruptProc(0, 0);
 
        mf_init();
-       mf_initialized = 1;
-       mb();
 
        /* If we were passed an initrd, set the ROOT_DEV properly if the values
         * look sensible. If not, clear initrd reference.
@@ -540,10 +533,10 @@ static void __init iSeries_setup_arch(void)
 {
        if (get_lppaca()->shared_proc) {
                ppc_md.idle_loop = iseries_shared_idle;
-               printk(KERN_INFO "Using shared processor idle loop\n");
+               printk(KERN_DEBUG "Using shared processor idle loop\n");
        } else {
                ppc_md.idle_loop = iseries_dedicated_idle;
-               printk(KERN_INFO "Using dedicated idle loop\n");
+               printk(KERN_DEBUG "Using dedicated idle loop\n");
        }
 
        /* Setup the Lp Event Queue */
@@ -560,39 +553,10 @@ static void iSeries_show_cpuinfo(struct seq_file *m)
        seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n");
 }
 
-/*
- * Document me.
- */
-static void iSeries_restart(char *cmd)
-{
-       mf_reboot();
-}
-
-/*
- * Document me.
- */
-static void iSeries_power_off(void)
-{
-       mf_power_off();
-}
-
-/*
- * Document me.
- */
-static void iSeries_halt(void)
-{
-       mf_power_off();
-}
-
 static void __init iSeries_progress(char * st, unsigned short code)
 {
        printk("Progress: [%04x] - %s\n", (unsigned)code, st);
-       if (!piranha_simulator && mf_initialized) {
-               if (code != 0xffff)
-                       mf_display_progress(code);
-               else
-                       mf_clear_src();
-       }
+       mf_display_progress(code);
 }
 
 static void __init iSeries_fixup_klimit(void)
@@ -709,21 +673,35 @@ static void iseries_dedicated_idle(void)
 void __init iSeries_init_IRQ(void) { }
 #endif
 
-static int __init iseries_probe(int platform)
+static int __init iseries_probe(void)
 {
-       return PLATFORM_ISERIES_LPAR == platform;
+       unsigned long root = of_get_flat_dt_root();
+       if (!of_flat_dt_is_compatible(root, "IBM,iSeries"))
+               return 0;
+
+       powerpc_firmware_features |= FW_FEATURE_ISERIES;
+       powerpc_firmware_features |= FW_FEATURE_LPAR;
+
+       /*
+        * The Hypervisor only allows us up to 256 interrupt
+        * sources (the irq number is passed in a u8).
+        */
+       virt_irq_max = 255;
+
+       return 1;
 }
 
-struct machdep_calls __initdata iseries_md = {
+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,
-       .restart        = iSeries_restart,
-       .power_off      = iSeries_power_off,
-       .halt           = iSeries_halt,
+       .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,
@@ -734,7 +712,7 @@ struct machdep_calls __initdata iseries_md = {
 };
 
 struct blob {
-       unsigned char data[PAGE_SIZE];
+       unsigned char data[PAGE_SIZE * 2];
        unsigned long next;
 };
 
@@ -917,6 +895,314 @@ void dt_cpus(struct iseries_flat_dt *dt)
        dt_end_node(dt);
 }
 
+void dt_model(struct iseries_flat_dt *dt)
+{
+       char buf[16] = "IBM,";
+
+       /* "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");
+}
+
+void dt_vdevices(struct iseries_flat_dt *dt)
+{
+       u32 reg = 0;
+       HvLpIndexMap vlan_map;
+       int i;
+       char buf[32];
+
+       dt_start_node(dt, "vdevice");
+       dt_prop_str(dt, "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);
+
+       snprintf(buf, sizeof(buf), "vty@%08x", reg);
+       dt_start_node(dt, buf);
+       dt_prop_str(dt, "device_type", "serial");
+       dt_prop_u32(dt, "reg", reg);
+       dt_end_node(dt);
+       reg++;
+
+       snprintf(buf, sizeof(buf), "v-scsi@%08x", reg);
+       dt_start_node(dt, buf);
+       dt_prop_str(dt, "device_type", "vscsi");
+       dt_prop_str(dt, "compatible", "IBM,v-scsi");
+       dt_prop_u32(dt, "reg", reg);
+       dt_end_node(dt);
+       reg++;
+
+       vlan_map = HvLpConfig_getVirtualLanIndexMap();
+       for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) {
+               unsigned char mac_addr[ETH_ALEN];
+
+               if ((vlan_map & (0x8000 >> i)) == 0)
+                       continue;
+               snprintf(buf, 32, "l-lan@%08x", reg + i);
+               dt_start_node(dt, buf);
+               dt_prop_str(dt, "device_type", "network");
+               dt_prop_str(dt, "compatible", "IBM,iSeries-l-lan");
+               dt_prop_u32(dt, "reg", reg + i);
+               dt_prop_u32(dt, "linux,unit_address", i);
+
+               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);
+       }
+       reg += HVMAXARCHITECTEDVIRTUALLANS;
+
+       for (i = 0; i < HVMAXARCHITECTEDVIRTUALDISKS; i++) {
+               snprintf(buf, 32, "viodasd@%08x", reg + i);
+               dt_start_node(dt, buf);
+               dt_prop_str(dt, "device_type", "block");
+               dt_prop_str(dt, "compatible", "IBM,iSeries-viodasd");
+               dt_prop_u32(dt, "reg", reg + i);
+               dt_prop_u32(dt, "linux,unit_address", i);
+               dt_end_node(dt);
+       }
+       reg += HVMAXARCHITECTEDVIRTUALDISKS;
+       for (i = 0; i < HVMAXARCHITECTEDVIRTUALCDROMS; i++) {
+               snprintf(buf, 32, "viocd@%08x", reg + i);
+               dt_start_node(dt, buf);
+               dt_prop_str(dt, "device_type", "block");
+               dt_prop_str(dt, "compatible", "IBM,iSeries-viocd");
+               dt_prop_u32(dt, "reg", reg + i);
+               dt_prop_u32(dt, "linux,unit_address", i);
+               dt_end_node(dt);
+       }
+       reg += HVMAXARCHITECTEDVIRTUALCDROMS;
+       for (i = 0; i < HVMAXARCHITECTEDVIRTUALTAPES; i++) {
+               snprintf(buf, 32, "viotape@%08x", reg + i);
+               dt_start_node(dt, buf);
+               dt_prop_str(dt, "device_type", "byte");
+               dt_prop_str(dt, "compatible", "IBM,iSeries-viotape");
+               dt_prop_u32(dt, "reg", reg + i);
+               dt_prop_u32(dt, "linux,unit_address", i);
+               dt_end_node(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];
@@ -927,6 +1213,7 @@ void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size)
 
        dt_prop_u32(dt, "#address-cells", 2);
        dt_prop_u32(dt, "#size-cells", 2);
+       dt_model(dt);
 
        /* /memory */
        dt_start_node(dt, "memory@0");
@@ -939,13 +1226,14 @@ void build_flat_dt(struct iseries_flat_dt *dt, unsigned long phys_mem_size)
 
        /* /chosen */
        dt_start_node(dt, "chosen");
-       dt_prop_u32(dt, "linux,platform", PLATFORM_ISERIES_LPAR);
-       if (cmd_mem_limit)
-               dt_prop_u64(dt, "linux,memory-limit", cmd_mem_limit);
+       dt_prop_str(dt, "bootargs", cmd_line);
        dt_end_node(dt);
 
        dt_cpus(dt);
 
+       dt_vdevices(dt);
+       dt_pci_devices(dt);
+
        dt_end_node(dt);
 
        dt_push_u32(dt, OF_DT_END);
@@ -965,29 +1253,11 @@ void * __init iSeries_early_setup(void)
 
        iSeries_get_cmdline();
 
-       /* Save unparsed command line copy for /proc/cmdline */
-       strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
-
-       /* Parse early parameters, in particular mem=x */
-       parse_early_param();
-
        build_flat_dt(&iseries_dt, phys_mem_size);
 
        return (void *) __pa(&iseries_dt);
 }
 
-/*
- * On iSeries we just parse the mem=X option from the command line.
- * On pSeries it's a bit more complicated, see prom_init_mem()
- */
-static int __init early_parsemem(char *p)
-{
-       if (p)
-               cmd_mem_limit = ALIGN(memparse(p, &p), PAGE_SIZE);
-       return 0;
-}
-early_param("mem", early_parsemem);
-
 static void hvputc(char c)
 {
        if (c == '\n')