vboot: ec: Check for EC limit power request in VbExEcVbootDone()

Add VbExEcVbootDone(), which checks the limit power flag that gets set by
the EC to request that the AP limit its power use. If this flag remains
set for 3 seconds, request a system shutdown since we're likely to
brownout if we continue to boot.

BUG=chromium:537269
TEST=Compile on daisy
BRANCH=None

Change-Id: Ie03496dfe94410a0026105c076dfa76d1b11aa65
Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/309523
Commit-Ready: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/cros/vboot/ec.c b/cros/vboot/ec.c
index 0b70f34..2c4305e 100644
--- a/cros/vboot/ec.c
+++ b/cros/vboot/ec.c
@@ -337,3 +337,47 @@
 
 	return VBERROR_SUCCESS;
 }
+
+/* Wait 3 seconds after software sync for EC to clear the limit power flag. */
+#define LIMIT_POWER_WAIT_TIMEOUT 3000
+/* Check the limit power flag every 50 ms while waiting. */
+#define LIMIT_POWER_POLL_SLEEP 50
+
+VbError_t VbExEcVbootDone(int in_recovery)
+{
+	struct cros_ec_dev *mdev = board_get_cros_ec_dev();
+	int limit_power;
+	int limit_power_wait_time = 0;
+	int message_printed = 0;
+
+	/* Ensure we have enough power to continue booting */
+	while (1) {
+		if (cros_ec_read_limit_power_request(mdev, &limit_power)) {
+			VBDEBUG("Failed to check EC limit power flag.\n");
+			return VBERROR_UNKNOWN;
+		}
+
+		/*
+		 * Do not wait for the limit power flag to be cleared in
+		 * recovery mode since we didn't just sysjump.
+		 */
+		if (!limit_power || in_recovery ||
+		    limit_power_wait_time > LIMIT_POWER_WAIT_TIMEOUT)
+			break;
+
+		if (!message_printed) {
+			VBDEBUG("Waiting for EC to clear limit power flag.\n");
+			message_printed = 1;
+		}
+
+		mdelay(LIMIT_POWER_POLL_SLEEP);
+		limit_power_wait_time += LIMIT_POWER_POLL_SLEEP;
+	}
+
+	if (limit_power) {
+		VBDEBUG("EC requests limited power usage. Request shutdown.\n");
+		return VBERROR_SHUTDOWN_REQUESTED;
+	}
+
+	return VBERROR_SUCCESS;
+}
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c
index 6c7faef..a8a02c7 100644
--- a/drivers/misc/cros_ec.c
+++ b/drivers/misc/cros_ec.c
@@ -1147,6 +1147,34 @@
 	return 0;
 }
 
+int cros_ec_read_limit_power_request(struct cros_ec_dev *dev, int *limit_power)
+{
+	struct ec_params_charge_state p;
+	struct ec_response_charge_state r;
+	int res;
+
+	p.cmd = CHARGE_STATE_CMD_GET_PARAM;
+	p.get_param.param = CS_PARAM_LIMIT_POWER;
+	res = ec_command(dev, EC_CMD_CHARGE_STATE, 0,
+			 &p, sizeof(p), &r, sizeof(r));
+
+	/*
+	 * If our EC doesn't support the LIMIT_POWER parameter, assume that
+	 * LIMIT_POWER is not requested.
+	 */
+	if (res == -EC_RES_INVALID_PARAM || res == -EC_RES_INVALID_COMMAND) {
+		debug("PARAM_LIMIT_POWER not supported by EC.\n");
+		*limit_power = 0;
+		return 0;
+	}
+
+	if (res != sizeof(r.get_param))
+		return -1;
+
+	*limit_power = r.get_param.value;
+	return 0;
+}
+
 /**
  * Decode EC interface details from the device tree and allocate a suitable
  * device.
diff --git a/include/cros_ec.h b/include/cros_ec.h
index 79a0dc1..2835a6d 100644
--- a/include/cros_ec.h
+++ b/include/cros_ec.h
@@ -500,4 +500,13 @@
  */
 void cros_ec_check_keyboard(struct cros_ec_dev *dev);
 
+/**
+ * Read limit power request from the EC
+ *
+ * @param dev		CROS-EC device
+ * @param limit_power	Pointer to result destination
+ * @return 0 if ok, -1 on error
+ */
+int cros_ec_read_limit_power_request(struct cros_ec_dev *dev, int *limit_power);
+
 #endif