dm: Add a system reset uclass

It is common for system reset to be available at multiple levels in modern
hardware. For example, an SoC may provide a reset option, and a board may
provide its own reset for reasons of security or thoroughness. It is useful
to be able to model this hardware without hard-coding the behaviour in the
SoC or board. Also there is a distinction sometimes between resetting just
the CPU (leaving GPIO state alone) and resetting all the PMICs, just cutting
power.

To achieve this, add a simple system reset uclass. It allows multiple devices
to provide reset functionality and provides a way to walk through them,
requesting a particular reset type until is it provided.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass
2015-06-23 15:39:13 -06:00
parent 92a655c326
commit f9917454d5
5 changed files with 135 additions and 0 deletions

View File

@@ -73,3 +73,12 @@ config PCA9551_I2C_ADDR
default 0x60
help
The I2C address of the PCA9551 LED controller.
config RESET
bool "Enable support for reset drivers"
depends on DM
help
Enable reset drivers which can be used to reset the CPU or board.
Each driver can provide a reset method which will be called to
effect a reset. The uclass will try all available drivers when
reset_walk() is called.

View File

@@ -32,3 +32,4 @@ obj-$(CONFIG_TWL4030_LED) += twl4030_led.o
obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o
obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
obj-$(CONFIG_RESET) += reset-uclass.o

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <reset.h>
#include <dm.h>
#include <errno.h>
#include <regmap.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <linux/err.h>
int reset_request(struct udevice *dev, enum reset_t type)
{
struct reset_ops *ops = reset_get_ops(dev);
if (!ops->request)
return -ENOSYS;
return ops->request(dev, type);
}
void reset_walk(enum reset_t type)
{
struct udevice *dev;
int ret = 0;
while (ret != -EINPROGRESS && type < RESET_COUNT) {
for (uclass_first_device(UCLASS_RESET, &dev);
dev;
uclass_next_device(&dev)) {
ret = reset_request(dev, type);
if (ret == -EINPROGRESS)
break;
}
}
/* Wait for the reset to take effect */
mdelay(100);
/* Still no reset? Give up */
printf("Reset not supported on this platform\n");
hang();
}
/**
* reset_cpu() - calls reset_walk(RESET_WARM)
*/
void reset_cpu(ulong addr)
{
reset_walk(RESET_WARM);
}
UCLASS_DRIVER(reset) = {
.id = UCLASS_RESET,
.name = "reset",
};