op-pwrctl: Hold PCI resets until BootProgress passes BASE_INITIALIZATION state

When powering on host, hold PCI resets until hostboot passes stage
"BASE_INITIALIZATION"(0x14).

This fix applies to Firestone and Garrison.

Partially resolves openbmc/openbmc#315

Change-Id: Ic71c81406ac188b34df89569e2264ea0b94406f3
Signed-off-by: Yi Li <adamliyi@msn.com>
diff --git a/configs/Barreleye.py b/configs/Barreleye.py
index 8e7af2c..e8e8d57 100644
--- a/configs/Barreleye.py
+++ b/configs/Barreleye.py
@@ -558,14 +558,17 @@
 }
 
 POWER_CONFIG = {
-    'power_good_in' : 'PGOOD',
-    'power_up_outs' : [
+    'power_good_in': 'PGOOD',
+    'power_up_outs': [
         ('POWER_PIN', False),
     ],
-    'reset_outs' : [
-        ('PCIE_RESET', False),
+    'reset_outs': [
         ('USB_RESET', False),
     ],
+    'pci_reset_outs': [
+        # net name, polarity, reset hold
+        ('PCIE_RESET', False, False),
+    ],
 }
 
 # Miscellaneous non-poll sensor with system specific properties.
diff --git a/configs/Firestone.py b/configs/Firestone.py
index 0b7eb17..285c397 100644
--- a/configs/Firestone.py
+++ b/configs/Firestone.py
@@ -460,9 +460,13 @@
         ('CM1_OE_R_N', True),
         ('BMC_CP0_RESET_N', False),
         ('BMC_CFAM_RESET_N_R', False),
-        ('PEX8718_DEVICES_RESET_N', False),
-        ('CP0_DEVICES_RESET_N', False),
-        ('CP1_DEVICES_RESET_N', False),
+    ],
+    'pci_reset_outs' : [
+        # net name, polarity, reset hold
+        # if 'reset hold' set to 'True', BMC will hold pci reset until host CPU ready to drive PCI
+        ('PEX8718_DEVICES_RESET_N', False, True),
+        ('CP0_DEVICES_RESET_N', False, True),
+        ('CP1_DEVICES_RESET_N', False, True),
     ],
 }
 
diff --git a/configs/Garrison.py b/configs/Garrison.py
index 8d29750..aef0720 100644
--- a/configs/Garrison.py
+++ b/configs/Garrison.py
@@ -461,9 +461,13 @@
         ('CM1_OE_R_N', True),
         ('BMC_CP0_RESET_N', False),
         ('BMC_CFAM_RESET_N_R', False),
-        ('PEX8718_DEVICES_RESET_N', False),
-        ('CP0_DEVICES_RESET_N', False),
-        ('CP1_DEVICES_RESET_N', False),
+    ],
+    'pci_reset_outs' : [
+        # net name, polarity, reset hold
+        # if 'reset hold' set to 'True', BMC will hold pci reset until host CPU ready to drive PCI
+        ('PEX8718_DEVICES_RESET_N', False, True),
+        ('CP0_DEVICES_RESET_N', False, True),
+        ('CP1_DEVICES_RESET_N', False, True),
     ],
 }
 
diff --git a/configs/Palmetto.py b/configs/Palmetto.py
index 19037a1..7ed578c 100644
--- a/configs/Palmetto.py
+++ b/configs/Palmetto.py
@@ -131,14 +131,17 @@
 }
 
 POWER_CONFIG = {
-    'power_good_in' : 'PGOOD',
-    'power_up_outs' : [
+    'power_good_in': 'PGOOD',
+    'power_up_outs': [
         ('POWER_PIN', False),
     ],
-    'reset_outs' : [
-        ('PCIE_RESET', False),
+    'reset_outs': [
         ('USB_RESET', False),
     ],
+    'pci_reset_outs': [
+        # net name, polarity, reset hold
+        ('PCIE_RESET', False, False),
+    ],
 }
 
 # Miscellaneous non-poll sensor with system specific properties.
diff --git a/configs/Witherspoon.py b/configs/Witherspoon.py
index 27fc88f..3250fb2 100644
--- a/configs/Witherspoon.py
+++ b/configs/Witherspoon.py
@@ -479,11 +479,11 @@
 
 
 POWER_CONFIG = {
-    'power_good_in' : 'SYS_PWROK_BUFF',
-    'power_up_outs' : [
+    'power_good_in': 'SYS_PWROK_BUFF',
+    'power_up_outs': [
         ('BMC_POWER_UP', True),
     ],
-    'reset_outs' : [
+    'reset_outs': [
     ],
 }
 
diff --git a/configs/Zaius.py b/configs/Zaius.py
index bb70846..2160e69 100644
--- a/configs/Zaius.py
+++ b/configs/Zaius.py
@@ -393,13 +393,13 @@
 }
 
 POWER_CONFIG = {
-    'latch_out' : 'BMC_UCD_LATCH_LE',
-    'power_good_in' : 'SYS_PWROK_BUFF',
-    'power_up_outs' : [
+    'latch_out': 'BMC_UCD_LATCH_LE',
+    'power_good_in': 'SYS_PWROK_BUFF',
+    'power_up_outs': [
         ('SOFTWARE_PGOOD', True),
         ('BMC_POWER_UP', True),
     ],
-    'reset_outs' : [
+    'reset_outs': [
     ],
 }
 
diff --git a/libopenbmc_intf/power_gpio.c b/libopenbmc_intf/power_gpio.c
index 543281f..3c44c81 100644
--- a/libopenbmc_intf/power_gpio.c
+++ b/libopenbmc_intf/power_gpio.c
@@ -31,10 +31,14 @@
 	gchar *latch_out_name;
 	GVariantIter *power_up_outs_iter;
 	GVariantIter *reset_outs_iter;
+	GVariantIter *pci_reset_outs_iter;
 	gchar *power_up_out_name;
 	gchar *reset_out_name;
+	gchar *pci_reset_out_name;
 	gboolean power_up_polarity;
 	gboolean reset_out_polarity;
+	gboolean pci_reset_out_polarity;
+	gboolean pci_reset_out_hold;
 	int i;
 
 	proxy = g_dbus_proxy_new_sync(connection,
@@ -67,8 +71,8 @@
 
 	g_assert(value != NULL);
 	memset(power_gpio, 0, sizeof(*power_gpio));
-	g_variant_get(value, "(&s&sa(sb)a(sb))", &power_good_in_name, &latch_out_name,
-			&power_up_outs_iter, &reset_outs_iter);
+	g_variant_get(value, "(&s&sa(sb)a(sb)a(sbb))", &power_good_in_name, &latch_out_name,
+			&power_up_outs_iter, &reset_outs_iter, &pci_reset_outs_iter);
 
 	g_print("Power GPIO latch output %s\n", latch_out_name);
 	if(*latch_out_name != '\0') {  /* latch is optional */
@@ -105,8 +109,28 @@
 		power_gpio->reset_pols[i] = reset_out_polarity;
 	}
 
+	power_gpio->num_pci_reset_outs = g_variant_iter_n_children(pci_reset_outs_iter);
+	g_print("Power GPIO %d pci reset outputs\n", power_gpio->num_pci_reset_outs);
+	power_gpio->pci_reset_outs = g_malloc0_n(power_gpio->num_pci_reset_outs,
+			sizeof(GPIO));
+	power_gpio->pci_reset_pols = g_malloc0_n(power_gpio->num_pci_reset_outs,
+			sizeof(gboolean));
+	power_gpio->pci_reset_holds = g_malloc0_n(power_gpio->num_pci_reset_outs,
+			sizeof(gboolean));
+	for(i = 0; g_variant_iter_next(pci_reset_outs_iter, "(&sbb)", &pci_reset_out_name,
+				&pci_reset_out_polarity, &pci_reset_out_hold); i++) {
+		g_print("Power GPIO pci reset[%d] = %s active %s, hold - %s\n", i,
+				pci_reset_out_name,
+				pci_reset_out_polarity ? "HIGH" : "LOW",
+				pci_reset_out_hold ? "Yes" : "No");
+		power_gpio->pci_reset_outs[i].name = g_strdup(pci_reset_out_name);
+		power_gpio->pci_reset_pols[i] = pci_reset_out_polarity;
+		power_gpio->pci_reset_holds[i] = pci_reset_out_hold;
+	}
+
 	g_variant_iter_free(power_up_outs_iter);
 	g_variant_iter_free(reset_outs_iter);
+	g_variant_iter_free(pci_reset_outs_iter);
 	g_variant_unref(value);
 
 	return TRUE;
@@ -126,4 +150,10 @@
 	}
 	g_free(power_gpio->reset_outs);
 	g_free(power_gpio->reset_pols);
+	for(i = 0; i < power_gpio->num_pci_reset_outs; i++) {
+		g_free(power_gpio->pci_reset_outs[i].name);
+	}
+	g_free(power_gpio->pci_reset_outs);
+	g_free(power_gpio->pci_reset_pols);
+	g_free(power_gpio->pci_reset_holds);
 }
diff --git a/libopenbmc_intf/power_gpio.h b/libopenbmc_intf/power_gpio.h
index 77e1c9c..e3714d0 100644
--- a/libopenbmc_intf/power_gpio.h
+++ b/libopenbmc_intf/power_gpio.h
@@ -37,6 +37,11 @@
 	GPIO *reset_outs;
 	/* TRUE for active high */
 	gboolean *reset_pols;
+	size_t num_pci_reset_outs;
+	GPIO *pci_reset_outs;
+	/* TRUE for active high */
+	gboolean *pci_reset_pols;
+	gboolean *pci_reset_holds;
 } PowerGpio;
 
 /* Read system configuration for power GPIOs. */
diff --git a/op-pwrctl/power_control_obj.c b/op-pwrctl/power_control_obj.c
index eb6be21..f6cfdbc 100644
--- a/op-pwrctl/power_control_obj.c
+++ b/op-pwrctl/power_control_obj.c
@@ -17,6 +17,8 @@
 static const gchar* instance_name = "power0";
 static const gchar* dbus_name = "org.openbmc.control.Power";
 
+static int g_pci_reset_held = 1;
+
 static PowerGpio g_power_gpio;
 
 static GDBusObjectManagerServer *manager = NULL;
@@ -70,6 +72,7 @@
 			{
 				control_power_emit_power_lost(control_power);
 				control_emit_goto_system_state(control,"HOST_POWERED_OFF");
+				g_pci_reset_held = 1;
 			}
 			else
 			{
@@ -94,6 +97,36 @@
 				gpio_write(reset_out, reset_state);
 				gpio_close(reset_out);
 			}
+
+			for(i = 0; i < g_power_gpio.num_pci_reset_outs; i++)
+			{
+				GPIO *pci_reset_out = &g_power_gpio.pci_reset_outs[i];
+				if(pgood_state == 1)
+				{
+					/*
+					 * When powering on, hold PCI reset until
+					 * the processor can forward clocks and control reset.
+					 */
+					if(g_power_gpio.pci_reset_holds[i])
+					{
+						g_print("Holding pci reset: %s\n", pci_reset_out->name);
+						continue;
+					}
+				}
+				rc = gpio_open(pci_reset_out);
+				if(rc != GPIO_OK)
+				{
+					g_print("ERROR PowerControl: GPIO open error (gpio=%s,rc=%d)\n",
+							pci_reset_out->name, rc);
+					continue;
+				}
+
+				reset_state = pgood_state ^ !g_power_gpio.pci_reset_pols[i];
+				g_print("PowerControl: setting pci reset %s to %d\n", pci_reset_out->name,
+						(int)reset_state);
+				gpio_write(pci_reset_out, reset_state);
+				gpio_close(pci_reset_out);
+			}
 		}
 	} else {
 		g_print("ERROR PowerControl: GPIO read error (gpio=%s,rc=%d)\n",
@@ -116,6 +149,70 @@
 	return TRUE;
 }
 
+/* Handler for BootProgress signal from BootProgress sensor */
+static void
+on_boot_progress(GDBusConnection *connection,
+		const gchar *sender_name,
+		const gchar *object_path,
+		const gchar *interface_name,
+		const gchar *signal_name,
+		GVariant *parameters,
+		gpointer user_data)
+{
+	gchar *boot_progress;
+	uint8_t pgood_state;
+	uint8_t reset_state;
+	int rc;
+	int i;
+
+	if(!parameters)
+		return;
+
+	/* prevent release again */
+	if(!g_pci_reset_held)
+		return;
+
+	g_variant_get(parameters, "(s)", &boot_progress);
+	/* Release PCI reset when FW boot progress goes beyond 'Baseboard Init' */
+	if(strcmp(boot_progress, "FW Progress, Baseboard Init") == 0)
+		return;
+
+	rc = gpio_open(&g_power_gpio.power_good_in);
+	if(rc != GPIO_OK)
+	{
+		g_print("ERROR PowerControl: on_boot_progress(): GPIO open error (gpio=%s,rc=%d)\n",
+			g_power_gpio.power_good_in.name, rc);
+		return;
+	}
+	rc = gpio_read(&g_power_gpio.power_good_in, &pgood_state);
+	gpio_close(&g_power_gpio.power_good_in);
+	if(rc != GPIO_OK || pgood_state != 1)
+		return;
+
+	for(i = 0; i < g_power_gpio.num_pci_reset_outs; i++)
+	{
+		GPIO *pci_reset_out = &g_power_gpio.pci_reset_outs[i];
+
+		if(!g_power_gpio.pci_reset_holds[i])
+			continue;
+		rc = gpio_open(pci_reset_out);
+		if(rc != GPIO_OK)
+		{
+			g_print("ERROR PowerControl: GPIO open error (gpio=%s,rc=%d)\n",
+					pci_reset_out->name, rc);
+			continue;
+		}
+
+		reset_state = pgood_state ^ !g_power_gpio.pci_reset_pols[i];
+		g_print("PowerControl: setting pci reset %s to %d\n", pci_reset_out->name,
+				(int)reset_state);
+		gpio_write(pci_reset_out, reset_state);
+		gpio_close(pci_reset_out);
+		g_print("Released pci reset: %s - %s\n", pci_reset_out->name, boot_progress);
+	}
+	g_pci_reset_held = 0;
+}
+
 static gboolean
 on_set_power_state(ControlPower *pwr,
 		GDBusMethodInvocation *invocation,
@@ -230,6 +327,12 @@
 			error = rc;
 		}
 	}
+	for(int i = 0; i < power_gpio->num_pci_reset_outs; i++) {
+		rc = gpio_init(connection, &power_gpio->pci_reset_outs[i]);
+		if(rc != GPIO_OK) {
+			error = rc;
+		}
+	}
 
 	/* If there's a latch, it only needs to be set once. */
 	if(power_gpio->latch_out.name != NULL) {
@@ -309,6 +412,17 @@
 			G_CALLBACK(on_init),
 			object); /* user_data */
 
+	/* Listen for BootProgress signal from BootProgress sensor */
+	g_dbus_connection_signal_subscribe(connection,
+			NULL, /* service */
+			NULL, /* interface_name */
+			"BootProgress", /* member: name of the signal */
+			"/org/openbmc/sensors/host/BootProgress", /* obj path */
+			NULL, /* arg0 */
+			G_DBUS_SIGNAL_FLAGS_NONE,
+			(GDBusSignalCallback) on_boot_progress,
+			object, /* user data */
+			NULL );
 
 	/* Export the object (@manager takes its own reference to @object) */
 	g_dbus_object_manager_server_set_connection(manager, connection);
diff --git a/pysystemmgr/system_manager.py b/pysystemmgr/system_manager.py
index e6bdd29..5152ff2 100644
--- a/pysystemmgr/system_manager.py
+++ b/pysystemmgr/system_manager.py
@@ -184,13 +184,14 @@
         return r
 
     @dbus.service.method(DBUS_NAME, in_signature='',
-            out_signature='ssa(sb)a(sb)')
+            out_signature='ssa(sb)a(sb)a(sbb)')
     def getPowerConfiguration(self):
         power_good_in = System.POWER_CONFIG.get('power_good_in', '')
         latch_out = System.POWER_CONFIG.get('latch_out', '')
         power_up_outs = System.POWER_CONFIG.get('power_up_outs', [])
         reset_outs = System.POWER_CONFIG.get('reset_outs', [])
-        r = [power_good_in, latch_out, power_up_outs, reset_outs]
+        pci_reset_outs = System.POWER_CONFIG.get('pci_reset_outs', [])
+        r = [power_good_in, latch_out, power_up_outs, reset_outs, pci_reset_outs]
         print "Power GPIO config: " + str(r)
         return r