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/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);