aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-11-05 11:57:50 -0500
committerTom Rini <trini@konsulko.com>2020-11-05 11:57:50 -0500
commit7716c328c843d4365c9fe2703e66a7aeee557dfa (patch)
tree9c5e71617f5dfa53de3bca87c660b1554047766d /drivers
parent35b7ca768f7d826b77d5d3d6ccd6b1b8ed21f186 (diff)
parentb431970e7f0ce5b83fae1502eddc3568115207ad (diff)
Merge tag 'u-boot-imx-20201105' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
u-boot-imx for 2021.1 --------------------- - new boards : GE (new B1x5v2), phytec phyCORE-i.MX8MM - converted doc to reST - fixes for verdin-imx8mm (Toradex) - fixes for i.MX thermal driver - mx7ulp: Align the PLL_USB frequency - mx53: primary/secondary bmode Travis: https://travis-ci.org/github/sbabic/u-boot-imx/builds/741465284
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bootcount/Kconfig10
-rw-r--r--drivers/bootcount/Makefile1
-rw-r--r--drivers/bootcount/spi-flash.c125
-rw-r--r--drivers/rtc/m41t62.c139
-rw-r--r--drivers/sysreset/Kconfig7
-rw-r--r--drivers/sysreset/Makefile1
-rw-r--r--drivers/sysreset/poweroff_gpio.c92
-rw-r--r--drivers/thermal/imx_tmu.c1
-rw-r--r--drivers/watchdog/Kconfig13
9 files changed, 377 insertions, 12 deletions
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
index c8e6fa7f89..b5ccea0d9c 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -108,6 +108,16 @@ config DM_BOOTCOUNT_I2C_EEPROM
pointing to the underlying i2c eeprom device) and an optional 'offset'
property are supported.
+config DM_BOOTCOUNT_SPI_FLASH
+ bool "Support SPI flash devices as a backing store for bootcount"
+ depends on DM_SPI_FLASH
+ help
+ Enabled reading/writing the bootcount in a DM SPI flash device.
+ The wrapper device is to be specified with the compatible string
+ 'u-boot,bootcount-spi-flash' and the 'spi-flash'-property (a phandle
+ pointing to the underlying SPI flash device) and an optional 'offset'
+ property are supported.
+
config BOOTCOUNT_MEM
bool "Support memory based bootcounter"
help
diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile
index 059d40d16b..51d860b00e 100644
--- a/drivers/bootcount/Makefile
+++ b/drivers/bootcount/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o
obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o
obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o
obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o
+obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o
diff --git a/drivers/bootcount/spi-flash.c b/drivers/bootcount/spi-flash.c
new file mode 100644
index 0000000000..7cd388e661
--- /dev/null
+++ b/drivers/bootcount/spi-flash.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019 Collabora
+ * (C) Copyright 2019 GE
+ */
+
+#include <common.h>
+#include <bootcount.h>
+#include <dm.h>
+#include <spi_flash.h>
+
+static const u8 bootcount_magic = 0xbc;
+
+struct bootcount_spi_flash_priv {
+ struct udevice *spi_flash;
+ u32 offset;
+};
+
+static int bootcount_spi_flash_update(struct udevice *dev, u32 offset, u32 len, const void *buf)
+{
+ struct spi_flash *flash = dev_get_uclass_priv(dev);
+ u32 sector_size = flash->sector_size;
+ u32 sector_offset = offset % sector_size;
+ u32 sector = offset - sector_offset;
+ int err = 0;
+
+ /* code only supports updating a single sector */
+ if (sector_offset + len > sector_size)
+ return -ENOSYS;
+
+ u8 *buffer = malloc(sector_size);
+ if (!buffer)
+ return -ENOMEM;
+
+ err = spi_flash_read_dm(dev, sector, sector_size, buffer);
+ if (err < 0)
+ goto out;
+
+ memcpy(buffer + sector_offset, buf, len);
+
+ err = spi_flash_erase_dm(dev, sector, sector_size);
+ if (err < 0)
+ goto out;
+
+ err = spi_flash_write_dm(dev, sector, sector_size, buffer);
+ if (err < 0)
+ goto out;
+
+out:
+ free(buffer);
+ return err;
+}
+
+static int bootcount_spi_flash_set(struct udevice *dev, const u32 a)
+{
+ struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
+ const u16 val = bootcount_magic << 8 | (a & 0xff);
+
+ if (bootcount_spi_flash_update(priv->spi_flash, priv->offset, 2, &val) < 0) {
+ debug("%s: write failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int bootcount_spi_flash_get(struct udevice *dev, u32 *a)
+{
+ struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
+ u16 val;
+
+ if (spi_flash_read_dm(priv->spi_flash, priv->offset, 2, &val) < 0) {
+ debug("%s: read failed\n", __func__);
+ return -EIO;
+ }
+
+ if (val >> 8 == bootcount_magic) {
+ *a = val & 0xff;
+ return 0;
+ }
+
+ debug("%s: bootcount magic does not match on %04x\n", __func__, val);
+ return -EIO;
+}
+
+static int bootcount_spi_flash_probe(struct udevice *dev)
+{
+ struct ofnode_phandle_args phandle_args;
+ struct bootcount_spi_flash_priv *priv = dev_get_priv(dev);
+ struct udevice *spi_flash;
+
+ if (dev_read_phandle_with_args(dev, "spi-flash", NULL, 0, 0, &phandle_args)) {
+ debug("%s: spi-flash backing device not specified\n", dev->name);
+ return -ENOENT;
+ }
+
+ if (uclass_get_device_by_ofnode(UCLASS_SPI_FLASH, phandle_args.node, &spi_flash)) {
+ debug("%s: could not get backing device\n", dev->name);
+ return -ENODEV;
+ }
+
+ priv->spi_flash = spi_flash;
+ priv->offset = dev_read_u32_default(dev, "offset", 0);
+
+ return 0;
+}
+
+static const struct bootcount_ops bootcount_spi_flash_ops = {
+ .get = bootcount_spi_flash_get,
+ .set = bootcount_spi_flash_set,
+};
+
+static const struct udevice_id bootcount_spi_flash_ids[] = {
+ { .compatible = "u-boot,bootcount-spi-flash" },
+ { }
+};
+
+U_BOOT_DRIVER(bootcount_spi_flash) = {
+ .name = "bootcount-spi-flash",
+ .id = UCLASS_BOOTCOUNT,
+ .priv_auto_alloc_size = sizeof(struct bootcount_spi_flash_priv),
+ .probe = bootcount_spi_flash_probe,
+ .of_match = bootcount_spi_flash_ids,
+ .ops = &bootcount_spi_flash_ops,
+};
diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c
index 94a6b523aa..0a4e12d698 100644
--- a/drivers/rtc/m41t62.c
+++ b/drivers/rtc/m41t62.c
@@ -22,6 +22,8 @@
#include <log.h>
#include <rtc.h>
#include <i2c.h>
+#include <linux/log2.h>
+#include <linux/delay.h>
#define M41T62_REG_SSEC 0
#define M41T62_REG_SEC 1
@@ -47,8 +49,14 @@
#define M41T62_ALMON_SQWE (1 << 6) /* SQWE: SQW Enable Bit */
#define M41T62_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */
#define M41T62_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */
+#define M41T62_FLAGS_OF (1 << 2) /* OF: Oscillator Flag Bit */
#define M41T62_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */
+#define M41T62_WDAY_SQW_FREQ_MASK 0xf0
+#define M41T62_WDAY_SQW_FREQ_SHIFT 4
+
+#define M41T62_SQW_MAX_FREQ 32768
+
#define M41T62_FEATURE_HT (1 << 0)
#define M41T62_FEATURE_BL (1 << 1)
@@ -139,21 +147,140 @@ static int m41t62_rtc_set(struct udevice *dev, const struct rtc_time *tm)
return 0;
}
-static int m41t62_rtc_reset(struct udevice *dev)
+static int m41t62_sqw_enable(struct udevice *dev, bool enable)
{
u8 val;
+ int ret;
+
+ ret = dm_i2c_read(dev, M41T62_REG_ALARM_MON, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ if (enable)
+ val |= M41T62_ALMON_SQWE;
+ else
+ val &= ~M41T62_ALMON_SQWE;
+
+ return dm_i2c_write(dev, M41T62_REG_ALARM_MON, &val, sizeof(val));
+}
+
+static int m41t62_sqw_set_rate(struct udevice *dev, unsigned int rate)
+{
+ u8 val, newval, sqwrateval;
+ int ret;
+
+ if (rate >= M41T62_SQW_MAX_FREQ)
+ sqwrateval = 1;
+ else if (rate >= M41T62_SQW_MAX_FREQ / 4)
+ sqwrateval = 2;
+ else if (rate)
+ sqwrateval = 15 - ilog2(rate);
+
+ ret = dm_i2c_read(dev, M41T62_REG_WDAY, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ newval = val;
+ newval &= ~M41T62_WDAY_SQW_FREQ_MASK;
+ newval |= (sqwrateval << M41T62_WDAY_SQW_FREQ_SHIFT);
+
+ /*
+ * Try to avoid writing unchanged values. Writing to this register
+ * will reset the internal counter pipeline and thus affect system
+ * time.
+ */
+ if (newval == val)
+ return 0;
+
+ return dm_i2c_write(dev, M41T62_REG_WDAY, &newval, sizeof(newval));
+}
+
+static int m41t62_rtc_restart_osc(struct udevice *dev)
+{
+ u8 val;
+ int ret;
+
+ /* 0. check if oscillator failure happened */
+ ret = dm_i2c_read(dev, M41T62_REG_FLAGS, &val, sizeof(val));
+ if (ret)
+ return ret;
+ if (!(val & M41T62_FLAGS_OF))
+ return 0;
+
+ ret = dm_i2c_read(dev, M41T62_REG_SEC, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ /* 1. Set stop bit */
+ val |= M41T62_SEC_ST;
+ ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ /* 2. Clear stop bit */
+ val &= ~M41T62_SEC_ST;
+ ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ /* 3. wait 4 seconds */
+ mdelay(4000);
+
+ ret = dm_i2c_read(dev, M41T62_REG_FLAGS, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ /* 4. clear M41T62_FLAGS_OF bit */
+ val &= ~M41T62_FLAGS_OF;
+ ret = dm_i2c_write(dev, M41T62_REG_FLAGS, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int m41t62_rtc_clear_ht(struct udevice *dev)
+{
+ u8 val;
+ int ret;
/*
* M41T82: Make sure HT (Halt Update) bit is cleared.
* This bit is 0 in M41T62 so its save to clear it always.
*/
- int ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
-
+ ret = dm_i2c_read(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
+ if (ret)
+ return ret;
val &= ~M41T80_ALHOUR_HT;
- ret |= dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
+ ret = dm_i2c_write(dev, M41T62_REG_ALARM_HOUR, &val, sizeof(val));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int m41t62_rtc_reset(struct udevice *dev)
+{
+ int ret;
+
+ ret = m41t62_rtc_restart_osc(dev);
+ if (ret)
+ return ret;
+
+ ret = m41t62_rtc_clear_ht(dev);
+ if (ret)
+ return ret;
- return ret;
+ /*
+ * Some boards feed the square wave as clock input into
+ * the SoC. This enables a 32.768kHz square wave, which is
+ * also the hardware default after power-loss.
+ */
+ ret = m41t62_sqw_set_rate(dev, 32768);
+ if (ret)
+ return ret;
+ return m41t62_sqw_enable(dev, true);
}
/*
@@ -162,7 +289,7 @@ static int m41t62_rtc_reset(struct udevice *dev)
*/
static int m41t62_rtc_probe(struct udevice *dev)
{
- return m41t62_rtc_reset(dev);
+ return m41t62_rtc_clear_ht(dev);
}
static const struct rtc_ops m41t62_rtc_ops = {
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index 70692f07e7..0e5c7c9971 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -43,6 +43,13 @@ config SYSRESET_CMD_POWEROFF
endif
+config POWEROFF_GPIO
+ bool "Enable support for GPIO poweroff driver"
+ select DM_GPIO
+ help
+ Support for system poweroff using a GPIO pin. This can be used
+ for systems having a single GPIO to trigger a system poweroff.
+
config SYSRESET_GPIO
bool "Enable support for GPIO reset driver"
select DM_GPIO
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index 920c69233f..de81c399d7 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_ASPEED) += sysreset_ast.o
obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o
obj-$(CONFIG_ARCH_STI) += sysreset_sti.o
obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
+obj-$(CONFIG_POWEROFF_GPIO) += poweroff_gpio.o
obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o
obj-$(CONFIG_SYSRESET_MPC83XX) += sysreset_mpc83xx.o
obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o
diff --git a/drivers/sysreset/poweroff_gpio.c b/drivers/sysreset/poweroff_gpio.c
new file mode 100644
index 0000000000..ac482c37f4
--- /dev/null
+++ b/drivers/sysreset/poweroff_gpio.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Toggles a GPIO pin to power down a device
+ *
+ * Created using the Linux driver as reference, which
+ * has been written by:
+ *
+ * Jamie Lentin <jm@lentin.co.uk>
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * Copyright (C) 2012 Jamie Lentin
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <sysreset.h>
+
+#include <asm/gpio.h>
+#include <linux/delay.h>
+
+struct poweroff_gpio_info {
+ struct gpio_desc gpio;
+ u32 active_delay_ms;
+ u32 inactive_delay_ms;
+ u32 timeout_ms;
+};
+
+static int poweroff_gpio_request(struct udevice *dev, enum sysreset_t type)
+{
+ struct poweroff_gpio_info *priv = dev_get_priv(dev);
+ int r;
+
+ if (type != SYSRESET_POWER_OFF)
+ return -ENOSYS;
+
+ debug("GPIO poweroff\n");
+
+ /* drive it active, also inactive->active edge */
+ r = dm_gpio_set_value(&priv->gpio, 1);
+ if (r < 0)
+ return r;
+ mdelay(priv->active_delay_ms);
+
+ /* drive inactive, also active->inactive edge */
+ r = dm_gpio_set_value(&priv->gpio, 0);
+ if (r < 0)
+ return r;
+ mdelay(priv->inactive_delay_ms);
+
+ /* drive it active, also inactive->active edge */
+ r = dm_gpio_set_value(&priv->gpio, 1);
+ if (r < 0)
+ return r;
+
+ /* give it some time */
+ mdelay(priv->timeout_ms);
+
+ return -EINPROGRESS;
+}
+
+static int poweroff_gpio_probe(struct udevice *dev)
+{
+ struct poweroff_gpio_info *priv = dev_get_priv(dev);
+ int flags;
+
+ flags = dev_read_bool(dev, "input") ? GPIOD_IS_IN : GPIOD_IS_OUT;
+ priv->active_delay_ms = dev_read_u32_default(dev, "active-delay-ms", 100);
+ priv->inactive_delay_ms = dev_read_u32_default(dev, "inactive-delay-ms", 100);
+ priv->timeout_ms = dev_read_u32_default(dev, "timeout-ms", 3000);
+
+ return gpio_request_by_name(dev, "gpios", 0, &priv->gpio, flags);
+}
+
+static struct sysreset_ops poweroff_gpio_ops = {
+ .request = poweroff_gpio_request,
+};
+
+static const struct udevice_id poweroff_gpio_ids[] = {
+ { .compatible = "gpio-poweroff", },
+ {},
+};
+
+U_BOOT_DRIVER(poweroff_gpio) = {
+ .name = "poweroff-gpio",
+ .id = UCLASS_SYSRESET,
+ .ops = &poweroff_gpio_ops,
+ .probe = poweroff_gpio_probe,
+ .priv_auto_alloc_size = sizeof(struct poweroff_gpio_info),
+ .of_match = poweroff_gpio_ids,
+};
diff --git a/drivers/thermal/imx_tmu.c b/drivers/thermal/imx_tmu.c
index 4ca22089b8..936068c6cb 100644
--- a/drivers/thermal/imx_tmu.c
+++ b/drivers/thermal/imx_tmu.c
@@ -14,6 +14,7 @@
#include <dm/device.h>
#include <errno.h>
#include <fuse.h>
+#include <linux/delay.h>
#include <malloc.h>
#include <thermal.h>
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 210d9f8093..4532a40e45 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -21,12 +21,6 @@ config WATCHDOG_TIMEOUT_MSECS
config HW_WATCHDOG
bool
-config WATCHDOG_RESET_DISABLE
- bool "Disable reset watchdog"
- help
- Disable reset watchdog, which can let WATCHDOG_RESET invalid, so
- that the watchdog will not be fed in u-boot.
-
config IMX_WATCHDOG
bool "Enable Watchdog Timer support for IMX and LSCH2 of NXP"
select HW_WATCHDOG if !WDT
@@ -34,6 +28,13 @@ config IMX_WATCHDOG
Select this to enable the IMX and LSCH2 of Layerscape watchdog
driver.
+config WATCHDOG_RESET_DISABLE
+ bool "Disable reset watchdog"
+ depends on IMX_WATCHDOG
+ help
+ Disable reset watchdog, which can let WATCHDOG_RESET invalid, so
+ that the watchdog will not be fed in u-boot.
+
config OMAP_WATCHDOG
bool "TI OMAP watchdog driver"
depends on ARCH_OMAP2PLUS