Browse Source

sysreset: Add a way to find the last reset

We have a method to return the last reset as a string for humans, but not
a method that allows it to be used programmatically. Add a new method that
returns the last reset as an enum.

Signed-off-by: Simon Glass <sjg@chromium.org>
Simon Glass 5 years ago
parent
commit
751fed426f
4 changed files with 95 additions and 0 deletions
  1. 30 0
      drivers/sysreset/sysreset-uclass.c
  2. 15 0
      drivers/sysreset/sysreset_sandbox.c
  3. 30 0
      include/sysreset.h
  4. 20 0
      test/dm/sysreset.c

+ 30 - 0
drivers/sysreset/sysreset-uclass.c

@@ -36,6 +36,16 @@ int sysreset_get_status(struct udevice *dev, char *buf, int size)
 	return ops->get_status(dev, buf, size);
 }
 
+int sysreset_get_last(struct udevice *dev)
+{
+	struct sysreset_ops *ops = sysreset_get_ops(dev);
+
+	if (!ops->get_last)
+		return -ENOSYS;
+
+	return ops->get_last(dev);
+}
+
 int sysreset_walk(enum sysreset_t type)
 {
 	struct udevice *dev;
@@ -55,6 +65,26 @@ int sysreset_walk(enum sysreset_t type)
 	return ret;
 }
 
+int sysreset_get_last_walk(void)
+{
+	struct udevice *dev;
+	int value = -ENOENT;
+
+	for (uclass_first_device(UCLASS_SYSRESET, &dev);
+	     dev;
+	     uclass_next_device(&dev)) {
+		int ret;
+
+		ret = sysreset_get_last(dev);
+		if (ret >= 0) {
+			value = ret;
+			break;
+		}
+	}
+
+	return value;
+}
+
 void sysreset_walk_halt(enum sysreset_t type)
 {
 	int ret;

+ 15 - 0
drivers/sysreset/sysreset_sandbox.c

@@ -36,6 +36,11 @@ int sandbox_warm_sysreset_get_status(struct udevice *dev, char *buf, int size)
 	return 0;
 }
 
+int sandbox_warm_sysreset_get_last(struct udevice *dev)
+{
+	return SYSRESET_WARM;
+}
+
 static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
 {
 	struct sandbox_state *state = state_get_current();
@@ -58,6 +63,9 @@ static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
 			return -EACCES;
 		sandbox_exit();
 		break;
+	case SYSRESET_POWER_OFF:
+		if (!state->sysreset_allowed[type])
+			return -EACCES;
 	default:
 		return -ENOSYS;
 	}
@@ -74,9 +82,15 @@ int sandbox_sysreset_get_status(struct udevice *dev, char *buf, int size)
 	return 0;
 }
 
+int sandbox_sysreset_get_last(struct udevice *dev)
+{
+	return SYSRESET_COLD;
+}
+
 static struct sysreset_ops sandbox_sysreset_ops = {
 	.request	= sandbox_sysreset_request,
 	.get_status	= sandbox_sysreset_get_status,
+	.get_last	= sandbox_sysreset_get_last,
 };
 
 static const struct udevice_id sandbox_sysreset_ids[] = {
@@ -94,6 +108,7 @@ U_BOOT_DRIVER(sysreset_sandbox) = {
 static struct sysreset_ops sandbox_warm_sysreset_ops = {
 	.request	= sandbox_warm_sysreset_request,
 	.get_status	= sandbox_warm_sysreset_get_status,
+	.get_last	= sandbox_warm_sysreset_get_last,
 };
 
 static const struct udevice_id sandbox_warm_sysreset_ids[] = {

+ 30 - 0
include/sysreset.h

@@ -11,6 +11,7 @@ enum sysreset_t {
 	SYSRESET_WARM,	/* Reset CPU, keep GPIOs active */
 	SYSRESET_COLD,	/* Reset CPU and GPIOs */
 	SYSRESET_POWER,	/* Reset PMIC (remove and restore power) */
+	SYSRESET_POWER_OFF,	/* Turn off power */
 
 	SYSRESET_COUNT,
 };
@@ -37,6 +38,14 @@ struct sysreset_ops {
 	 * @return 0 if OK, -ve on error
 	 */
 	int (*get_status)(struct udevice *dev, char *buf, int size);
+
+	/**
+	 * get_last() - get information on the last reset
+	 *
+	 * @dev:	Device to check
+	 * @return last reset state (enum sysreset_t) or -ve error
+	 */
+	int (*get_last)(struct udevice *dev);
 };
 
 #define sysreset_get_ops(dev)        ((struct sysreset_ops *)(dev)->driver->ops)
@@ -59,6 +68,14 @@ int sysreset_request(struct udevice *dev, enum sysreset_t type);
  */
 int sysreset_get_status(struct udevice *dev, char *buf, int size);
 
+/**
+ * sysreset_get_last() - get information on the last reset
+ *
+ * @dev:	Device to check
+ * @return last reset state (enum sysreset_t) or -ve error
+ */
+int sysreset_get_last(struct udevice *dev);
+
 /**
  * sysreset_walk() - cause a system reset
  *
@@ -73,6 +90,19 @@ int sysreset_get_status(struct udevice *dev, char *buf, int size);
  */
 int sysreset_walk(enum sysreset_t type);
 
+/**
+ * sysreset_get_last_walk() - get information on the last reset
+ *
+ * This works through the available sysreset devices until it finds one that can
+ * perform a reset. If the provided sysreset type is not available, the next one
+ * will be tried.
+ *
+ * If no device prives the information, this function returns -ENOENT
+ *
+ * @return last reset state (enum sysreset_t) or -ve error
+ */
+int sysreset_get_last_walk(void);
+
 /**
  * sysreset_walk_halt() - try to reset, otherwise halt
  *

+ 20 - 0
test/dm/sysreset.c

@@ -71,6 +71,7 @@ static int dm_test_sysreset_walk(struct unit_test_state *uts)
 
 	/* If we generate a power sysreset, we will exit sandbox! */
 	state->sysreset_allowed[SYSRESET_POWER] = false;
+	state->sysreset_allowed[SYSRESET_POWER_OFF] = false;
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_WARM));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD));
 	ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER));
@@ -90,3 +91,22 @@ static int dm_test_sysreset_walk(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_sysreset_walk, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+static int dm_test_sysreset_get_last(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+
+	/* Device 1 is the warm sysreset device */
+	ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev));
+	ut_asserteq(SYSRESET_WARM, sysreset_get_last(dev));
+
+	/* Device 2 is the cold sysreset device */
+	ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev));
+	ut_asserteq(SYSRESET_COLD, sysreset_get_last(dev));
+
+	/* This is device 0, the non-DT one */
+	ut_asserteq(SYSRESET_COLD, sysreset_get_last_walk());
+
+	return 0;
+}
+DM_TEST(dm_test_sysreset_get_last, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);