summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-07-23 21:16:21 +0300
committerTom Rini <trini@konsulko.com>2019-07-23 21:16:21 +0300
commitff8c23e784f57a7098e91a200ed7f5a48612b653 (patch)
tree2d007e81ba9fc0d6334a9568d31422bd0e9dba88 /drivers
parent9565bd7c6f4241af746fd67ee5c8406a99c148b3 (diff)
parent1f99eaff08f699472860c82480344e824a737d57 (diff)
downloadu-boot-ff8c23e784f57a7098e91a200ed7f5a48612b653.tar.xz
Merge tag 'u-boot-stm32-20190723' of https://gitlab.denx.de/u-boot/custodians/u-boot-stm
- add rtc driver for stm32mp1 - add remoteproc driver for stm32mp1 - use kernel qspi compatible string for stm32
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk_stm32mp1.c9
-rw-r--r--drivers/core/of_addr.c4
-rw-r--r--drivers/core/ofnode.c8
-rw-r--r--drivers/core/read.c5
-rw-r--r--drivers/remoteproc/Kconfig10
-rw-r--r--drivers/remoteproc/Makefile3
-rw-r--r--drivers/remoteproc/rproc-elf-loader.c106
-rw-r--r--drivers/remoteproc/sandbox_testproc.c19
-rw-r--r--drivers/remoteproc/stm32_copro.c257
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/stm32_rtc.c323
-rw-r--r--drivers/spi/stm32_qspi.c1
13 files changed, 750 insertions, 2 deletions
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c
index 6ffa05b8fd..5806d48696 100644
--- a/drivers/clk/clk_stm32mp1.c
+++ b/drivers/clk/clk_stm32mp1.c
@@ -300,6 +300,7 @@ enum stm32mp1_parent_sel {
_DSI_SEL,
_ADC12_SEL,
_SPI1_SEL,
+ _RTC_SEL,
_PARENT_SEL_NB,
_UNKNOWN_SEL = 0xff,
};
@@ -534,6 +535,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR(RCC_MP_APB4ENSETR, 16, USBPHY_K, _USBPHY_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 2, I2C4_K, _I2C46_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 8, RTCAPB, _PCLK5),
STM32MP1_CLK_SET_CLR(RCC_MP_APB5ENSETR, 20, STGEN_K, _STGEN_SEL),
STM32MP1_CLK_SET_CLR_F(RCC_MP_AHB2ENSETR, 5, ADC12, _HCLK2),
@@ -569,6 +571,8 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 24, USBH, _UNKNOWN_SEL),
STM32MP1_CLK(RCC_DBGCFGR, 8, CK_DBG, _UNKNOWN_SEL),
+
+ STM32MP1_CLK(RCC_BDCR, 20, RTC, _RTC_SEL),
};
static const u8 i2c12_parents[] = {_PCLK1, _PLL4_R, _HSI_KER, _CSI_KER};
@@ -594,6 +598,7 @@ static const u8 dsi_parents[] = {_DSI_PHY, _PLL4_P};
static const u8 adc_parents[] = {_PLL4_R, _CK_PER, _PLL3_Q};
static const u8 spi_parents[] = {_PLL4_P, _PLL3_Q, _I2S_CKIN, _CK_PER,
_PLL3_R};
+static const u8 rtc_parents[] = {_UNKNOWN_ID, _LSE, _LSI, _HSE};
static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
STM32MP1_CLK_PARENT(_I2C12_SEL, RCC_I2C12CKSELR, 0, 0x7, i2c12_parents),
@@ -619,6 +624,9 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = {
STM32MP1_CLK_PARENT(_DSI_SEL, RCC_DSICKSELR, 0, 0x1, dsi_parents),
STM32MP1_CLK_PARENT(_ADC12_SEL, RCC_ADCCKSELR, 0, 0x1, adc_parents),
STM32MP1_CLK_PARENT(_SPI1_SEL, RCC_SPI2S1CKSELR, 0, 0x7, spi_parents),
+ STM32MP1_CLK_PARENT(_RTC_SEL, RCC_BDCR, RCC_BDCR_RTCSRC_SHIFT,
+ (RCC_BDCR_RTCSRC_MASK >> RCC_BDCR_RTCSRC_SHIFT),
+ rtc_parents),
};
#ifdef STM32MP1_CLOCK_TREE_INIT
@@ -734,6 +742,7 @@ char * const stm32mp1_clk_parent_sel_name[_PARENT_SEL_NB] = {
[_DSI_SEL] = "DSI",
[_ADC12_SEL] = "ADC12",
[_SPI1_SEL] = "SPI1",
+ [_RTC_SEL] = "RTC",
};
static const struct stm32mp1_clk_data stm32mp1_data = {
diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c
index 1bfaaeec00..4e256d9926 100644
--- a/drivers/core/of_addr.c
+++ b/drivers/core/of_addr.c
@@ -318,6 +318,10 @@ u64 of_translate_address(const struct device_node *dev, const __be32 *in_addr)
return __of_translate_address(dev, in_addr, "ranges");
}
+u64 of_translate_dma_address(const struct device_node *dev, const __be32 *in_addr)
+{
+ return __of_translate_address(dev, in_addr, "dma-ranges");
+}
static int __of_address_to_resource(const struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index c72c6e2673..179a6447dc 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -770,6 +770,14 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr)
return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
}
+u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
+{
+ if (ofnode_is_np(node))
+ return of_translate_dma_address(ofnode_to_np(node), in_addr);
+ else
+ return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
+}
+
int ofnode_device_is_compatible(ofnode node, const char *compat)
{
if (ofnode_is_np(node))
diff --git a/drivers/core/read.c b/drivers/core/read.c
index 6bda077a34..1a044b05e8 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -265,6 +265,11 @@ u64 dev_translate_address(struct udevice *dev, const fdt32_t *in_addr)
return ofnode_translate_address(dev_ofnode(dev), in_addr);
}
+u64 dev_translate_dma_address(struct udevice *dev, const fdt32_t *in_addr)
+{
+ return ofnode_translate_dma_address(dev_ofnode(dev), in_addr);
+}
+
int dev_read_alias_highest_id(const char *stem)
{
if (of_live_active())
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 9eb532bc7a..fa6f1113e1 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -40,6 +40,16 @@ config REMOTEPROC_SANDBOX
Say 'y' here to add support for test processor which does dummy
operations for sandbox platform.
+config REMOTEPROC_STM32_COPRO
+ bool "Support for STM32 coprocessor"
+ select REMOTEPROC
+ depends on DM
+ depends on ARCH_STM32MP
+ depends on OF_CONTROL
+ help
+ Say 'y' here to add support for STM32 Cortex-M4 coprocessors via the
+ remoteproc framework.
+
config REMOTEPROC_TI_POWER
bool "Support for TI Power processor"
select REMOTEPROC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 77eb708523..b9a06acdef 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -4,10 +4,11 @@
# Texas Instruments Incorporated - http://www.ti.com/
#
-obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o
+obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o
# Remote proc drivers - Please keep this list alphabetically sorted.
obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
obj-$(CONFIG_REMOTEPROC_K3) += k3_rproc.o
obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
+obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o
diff --git a/drivers/remoteproc/rproc-elf-loader.c b/drivers/remoteproc/rproc-elf-loader.c
new file mode 100644
index 0000000000..67937a7595
--- /dev/null
+++ b/drivers/remoteproc/rproc-elf-loader.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+#include <common.h>
+#include <dm.h>
+#include <elf.h>
+#include <remoteproc.h>
+
+/* Basic function to verify ELF32 image format */
+int rproc_elf32_sanity_check(ulong addr, ulong size)
+{
+ Elf32_Ehdr *ehdr;
+ char class;
+
+ if (!addr) {
+ pr_debug("Invalid fw address?\n");
+ return -EFAULT;
+ }
+
+ if (size < sizeof(Elf32_Ehdr)) {
+ pr_debug("Image is too small\n");
+ return -ENOSPC;
+ }
+
+ ehdr = (Elf32_Ehdr *)addr;
+ class = ehdr->e_ident[EI_CLASS];
+
+ if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS32) {
+ pr_debug("Not an executable ELF32 image\n");
+ return -EPROTONOSUPPORT;
+ }
+
+ /* We assume the firmware has the same endianness as the host */
+# ifdef __LITTLE_ENDIAN
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+# else /* BIG ENDIAN */
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+# endif
+ pr_debug("Unsupported firmware endianness\n");
+ return -EILSEQ;
+ }
+
+ if (size < ehdr->e_shoff + sizeof(Elf32_Shdr)) {
+ pr_debug("Image is too small\n");
+ return -ENOSPC;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+ pr_debug("Image is corrupted (bad magic)\n");
+ return -EBADF;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ pr_debug("No loadable segments\n");
+ return -ENOEXEC;
+ }
+
+ if (ehdr->e_phoff > size) {
+ pr_debug("Firmware size is too small\n");
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+/* A very simple elf loader, assumes the image is valid */
+int rproc_elf32_load_image(struct udevice *dev, unsigned long addr)
+{
+ Elf32_Ehdr *ehdr; /* Elf header structure pointer */
+ Elf32_Phdr *phdr; /* Program header structure pointer */
+ const struct dm_rproc_ops *ops;
+ unsigned int i;
+
+ ehdr = (Elf32_Ehdr *)addr;
+ phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
+
+ ops = rproc_get_ops(dev);
+
+ /* Load each program header */
+ for (i = 0; i < ehdr->e_phnum; ++i) {
+ void *dst = (void *)(uintptr_t)phdr->p_paddr;
+ void *src = (void *)addr + phdr->p_offset;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ if (ops->device_to_virt)
+ dst = ops->device_to_virt(dev, (ulong)dst);
+
+ dev_dbg(dev, "Loading phdr %i to 0x%p (%i bytes)\n",
+ i, dst, phdr->p_filesz);
+ if (phdr->p_filesz)
+ memcpy(dst, src, phdr->p_filesz);
+ if (phdr->p_filesz != phdr->p_memsz)
+ memset(dst + phdr->p_filesz, 0x00,
+ phdr->p_memsz - phdr->p_filesz);
+ flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
+ roundup((unsigned long)dst + phdr->p_filesz,
+ ARCH_DMA_MINALIGN) -
+ rounddown((unsigned long)dst, ARCH_DMA_MINALIGN));
+ ++phdr;
+ }
+
+ return 0;
+}
diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c
index 51a67e6bf1..5f35119ab7 100644
--- a/drivers/remoteproc/sandbox_testproc.c
+++ b/drivers/remoteproc/sandbox_testproc.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <errno.h>
#include <remoteproc.h>
+#include <asm/io.h>
/**
* enum sandbox_state - different device states
@@ -300,6 +301,23 @@ static int sandbox_testproc_ping(struct udevice *dev)
return ret;
}
+#define SANDBOX_RPROC_DEV_TO_PHY_OFFSET 0x1000
+/**
+ * sandbox_testproc_device_to_virt() - Convert device address to virtual address
+ * @dev: device to operate upon
+ * @da: device address
+ * @return converted virtual address
+ */
+static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da)
+{
+ u64 paddr;
+
+ /* Use a simple offset conversion */
+ paddr = da + SANDBOX_RPROC_DEV_TO_PHY_OFFSET;
+
+ return phys_to_virt(paddr);
+}
+
static const struct dm_rproc_ops sandbox_testproc_ops = {
.init = sandbox_testproc_init,
.reset = sandbox_testproc_reset,
@@ -308,6 +326,7 @@ static const struct dm_rproc_ops sandbox_testproc_ops = {
.stop = sandbox_testproc_stop,
.is_running = sandbox_testproc_is_running,
.ping = sandbox_testproc_ping,
+ .device_to_virt = sandbox_testproc_device_to_virt,
};
static const struct udevice_id sandbox_ids[] = {
diff --git a/drivers/remoteproc/stm32_copro.c b/drivers/remoteproc/stm32_copro.c
new file mode 100644
index 0000000000..de3b9729f3
--- /dev/null
+++ b/drivers/remoteproc/stm32_copro.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <regmap.h>
+#include <remoteproc.h>
+#include <reset.h>
+#include <syscon.h>
+#include <asm/io.h>
+
+#define RCC_GCR_HOLD_BOOT 0
+#define RCC_GCR_RELEASE_BOOT 1
+
+/**
+ * struct stm32_copro_privdata - power processor private data
+ * @reset_ctl: reset controller handle
+ * @hold_boot_regmap: regmap for remote processor reset hold boot
+ * @hold_boot_offset: offset of the register controlling the hold boot setting
+ * @hold_boot_mask: bitmask of the register for the hold boot field
+ * @is_running: is the remote processor running
+ */
+struct stm32_copro_privdata {
+ struct reset_ctl reset_ctl;
+ struct regmap *hold_boot_regmap;
+ uint hold_boot_offset;
+ uint hold_boot_mask;
+ bool is_running;
+};
+
+/**
+ * stm32_copro_probe() - Basic probe
+ * @dev: corresponding STM32 remote processor device
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_probe(struct udevice *dev)
+{
+ struct stm32_copro_privdata *priv;
+ struct regmap *regmap;
+ const fdt32_t *cell;
+ int len, ret;
+
+ priv = dev_get_priv(dev);
+
+ regmap = syscon_regmap_lookup_by_phandle(dev, "st,syscfg-holdboot");
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "unable to find holdboot regmap (%ld)\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ cell = dev_read_prop(dev, "st,syscfg-holdboot", &len);
+ if (len < 3 * sizeof(fdt32_t)) {
+ dev_err(dev, "holdboot offset and mask not available\n");
+ return -EINVAL;
+ }
+
+ priv->hold_boot_regmap = regmap;
+ priv->hold_boot_offset = fdtdec_get_number(cell + 1, 1);
+ priv->hold_boot_mask = fdtdec_get_number(cell + 2, 1);
+
+ ret = reset_get_by_index(dev, 0, &priv->reset_ctl);
+ if (ret) {
+ dev_err(dev, "failed to get reset (%d)\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "probed\n");
+
+ return 0;
+}
+
+/**
+ * stm32_copro_set_hold_boot() - Hold boot bit management
+ * @dev: corresponding STM32 remote processor device
+ * @hold: hold boot value
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_set_hold_boot(struct udevice *dev, bool hold)
+{
+ struct stm32_copro_privdata *priv;
+ uint val;
+ int ret;
+
+ priv = dev_get_priv(dev);
+
+ val = hold ? RCC_GCR_HOLD_BOOT : RCC_GCR_RELEASE_BOOT;
+
+ /*
+ * Note: shall run an SMC call (STM32_SMC_RCC) if platform is secured.
+ * To be updated when the code for this SMC service is available which
+ * is not the case for the time being.
+ */
+ ret = regmap_update_bits(priv->hold_boot_regmap, priv->hold_boot_offset,
+ priv->hold_boot_mask, val);
+ if (ret)
+ dev_err(dev, "failed to set hold boot\n");
+
+ return ret;
+}
+
+/**
+ * stm32_copro_device_to_virt() - Convert device address to virtual address
+ * @dev: corresponding STM32 remote processor device
+ * @da: device address
+ * @return converted virtual address
+ */
+static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da)
+{
+ fdt32_t in_addr = cpu_to_be32(da);
+ u64 paddr;
+
+ paddr = dev_translate_dma_address(dev, &in_addr);
+ if (paddr == OF_BAD_ADDR) {
+ dev_err(dev, "Unable to convert address %ld\n", da);
+ return NULL;
+ }
+
+ return phys_to_virt(paddr);
+}
+
+/**
+ * stm32_copro_load() - Loadup the STM32 remote processor
+ * @dev: corresponding STM32 remote processor device
+ * @addr: Address in memory where image is stored
+ * @size: Size in bytes of the image
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size)
+{
+ struct stm32_copro_privdata *priv;
+ int ret;
+
+ priv = dev_get_priv(dev);
+
+ ret = stm32_copro_set_hold_boot(dev, true);
+ if (ret)
+ return ret;
+
+ ret = reset_assert(&priv->reset_ctl);
+ if (ret) {
+ dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret);
+ return ret;
+ }
+
+ /* Support only ELF32 image */
+ ret = rproc_elf32_sanity_check(addr, size);
+ if (ret) {
+ dev_err(dev, "Invalid ELF32 image (%d)\n", ret);
+ return ret;
+ }
+
+ return rproc_elf32_load_image(dev, addr);
+}
+
+/**
+ * stm32_copro_start() - Start the STM32 remote processor
+ * @dev: corresponding STM32 remote processor device
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_start(struct udevice *dev)
+{
+ struct stm32_copro_privdata *priv;
+ int ret;
+
+ priv = dev_get_priv(dev);
+
+ /* move hold boot from true to false start the copro */
+ ret = stm32_copro_set_hold_boot(dev, false);
+ if (ret)
+ return ret;
+
+ /*
+ * Once copro running, reset hold boot flag to avoid copro
+ * rebooting autonomously
+ */
+ ret = stm32_copro_set_hold_boot(dev, true);
+ priv->is_running = !ret;
+ return ret;
+}
+
+/**
+ * stm32_copro_reset() - Reset the STM32 remote processor
+ * @dev: corresponding STM32 remote processor device
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_reset(struct udevice *dev)
+{
+ struct stm32_copro_privdata *priv;
+ int ret;
+
+ priv = dev_get_priv(dev);
+
+ ret = stm32_copro_set_hold_boot(dev, true);
+ if (ret)
+ return ret;
+
+ ret = reset_assert(&priv->reset_ctl);
+ if (ret) {
+ dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret);
+ return ret;
+ }
+
+ priv->is_running = false;
+
+ return 0;
+}
+
+/**
+ * stm32_copro_stop() - Stop the STM32 remote processor
+ * @dev: corresponding STM32 remote processor device
+ * @return 0 if all went ok, else corresponding -ve error
+ */
+static int stm32_copro_stop(struct udevice *dev)
+{
+ return stm32_copro_reset(dev);
+}
+
+/**
+ * stm32_copro_is_running() - Is the STM32 remote processor running
+ * @dev: corresponding STM32 remote processor device
+ * @return 1 if the remote processor is running, 0 otherwise
+ */
+static int stm32_copro_is_running(struct udevice *dev)
+{
+ struct stm32_copro_privdata *priv;
+
+ priv = dev_get_priv(dev);
+ return priv->is_running;
+}
+
+static const struct dm_rproc_ops stm32_copro_ops = {
+ .load = stm32_copro_load,
+ .start = stm32_copro_start,
+ .stop = stm32_copro_stop,
+ .reset = stm32_copro_reset,
+ .is_running = stm32_copro_is_running,
+ .device_to_virt = stm32_copro_device_to_virt,
+};
+
+static const struct udevice_id stm32_copro_ids[] = {
+ {.compatible = "st,stm32mp1-rproc"},
+ {}
+};
+
+U_BOOT_DRIVER(stm32_copro) = {
+ .name = "stm32_m4_proc",
+ .of_match = stm32_copro_ids,
+ .id = UCLASS_REMOTEPROC,
+ .ops = &stm32_copro_ops,
+ .probe = stm32_copro_probe,
+ .priv_auto_alloc_size = sizeof(struct stm32_copro_privdata),
+};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 532e94d337..0b58a18f5f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -120,4 +120,10 @@ config RTC_M41T62
Enable driver for ST's M41T62 compatible RTC devices (like RV-4162).
It is a serial (I2C) real-time clock (RTC) with alarm.
+config RTC_STM32
+ bool "Enable STM32 RTC driver"
+ depends on DM_RTC
+ help
+ Enable STM32 RTC driver. This driver supports the rtc that is present
+ on some STM32 SoCs.
endmenu
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 915adb87fe..f97a669982 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -51,5 +51,6 @@ obj-$(CONFIG_RTC_RX8025) += rx8025.o
obj-$(CONFIG_RTC_RX8010SJ) += rx8010sj.o
obj-$(CONFIG_RTC_S3C24X0) += s3c24x0_rtc.o
obj-$(CONFIG_RTC_S35392A) += s35392a.o
+obj-$(CONFIG_RTC_STM32) += stm32_rtc.o
obj-$(CONFIG_SANDBOX) += sandbox_rtc.o
obj-$(CONFIG_RTC_X1205) += x1205.o
diff --git a/drivers/rtc/stm32_rtc.c b/drivers/rtc/stm32_rtc.c
new file mode 100644
index 0000000000..abd339076a
--- /dev/null
+++ b/drivers/rtc/stm32_rtc.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <rtc.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+
+#define STM32_RTC_TR 0x00
+#define STM32_RTC_DR 0x04
+#define STM32_RTC_ISR 0x0C
+#define STM32_RTC_PRER 0x10
+#define STM32_RTC_CR 0x18
+#define STM32_RTC_WPR 0x24
+
+/* STM32_RTC_TR bit fields */
+#define STM32_RTC_SEC_SHIFT 0
+#define STM32_RTC_SEC GENMASK(6, 0)
+#define STM32_RTC_MIN_SHIFT 8
+#define STM32_RTC_MIN GENMASK(14, 8)
+#define STM32_RTC_HOUR_SHIFT 16
+#define STM32_RTC_HOUR GENMASK(21, 16)
+
+/* STM32_RTC_DR bit fields */
+#define STM32_RTC_DATE_SHIFT 0
+#define STM32_RTC_DATE GENMASK(5, 0)
+#define STM32_RTC_MONTH_SHIFT 8
+#define STM32_RTC_MONTH GENMASK(12, 8)
+#define STM32_RTC_WDAY_SHIFT 13
+#define STM32_RTC_WDAY GENMASK(15, 13)
+#define STM32_RTC_YEAR_SHIFT 16
+#define STM32_RTC_YEAR GENMASK(23, 16)
+
+/* STM32_RTC_CR bit fields */
+#define STM32_RTC_CR_FMT BIT(6)
+
+/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */
+#define STM32_RTC_ISR_INITS BIT(4)
+#define STM32_RTC_ISR_RSF BIT(5)
+#define STM32_RTC_ISR_INITF BIT(6)
+#define STM32_RTC_ISR_INIT BIT(7)
+
+/* STM32_RTC_PRER bit fields */
+#define STM32_RTC_PRER_PRED_S_SHIFT 0
+#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
+#define STM32_RTC_PRER_PRED_A_SHIFT 16
+#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
+
+/* STM32_RTC_WPR key constants */
+#define RTC_WPR_1ST_KEY 0xCA
+#define RTC_WPR_2ND_KEY 0x53
+#define RTC_WPR_WRONG_KEY 0xFF
+
+struct stm32_rtc_priv {
+ fdt_addr_t base;
+};
+
+static int stm32_rtc_get(struct udevice *dev, struct rtc_time *tm)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ u32 tr, dr;
+
+ tr = readl(priv->base + STM32_RTC_TR);
+ dr = readl(priv->base + STM32_RTC_DR);
+
+ tm->tm_sec = bcd2bin((tr & STM32_RTC_SEC) >> STM32_RTC_SEC_SHIFT);
+ tm->tm_min = bcd2bin((tr & STM32_RTC_MIN) >> STM32_RTC_MIN_SHIFT);
+ tm->tm_hour = bcd2bin((tr & STM32_RTC_HOUR) >> STM32_RTC_HOUR_SHIFT);
+
+ tm->tm_mday = bcd2bin((dr & STM32_RTC_DATE) >> STM32_RTC_DATE_SHIFT);
+ tm->tm_mon = bcd2bin((dr & STM32_RTC_MONTH) >> STM32_RTC_MONTH_SHIFT);
+ tm->tm_year = bcd2bin((dr & STM32_RTC_YEAR) >> STM32_RTC_YEAR_SHIFT);
+ tm->tm_wday = bcd2bin((dr & STM32_RTC_WDAY) >> STM32_RTC_WDAY_SHIFT);
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+
+ dev_dbg(dev, "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+static void stm32_rtc_unlock(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+
+ writel(RTC_WPR_1ST_KEY, priv->base + STM32_RTC_WPR);
+ writel(RTC_WPR_2ND_KEY, priv->base + STM32_RTC_WPR);
+}
+
+static void stm32_rtc_lock(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+
+ writel(RTC_WPR_WRONG_KEY, priv->base + STM32_RTC_WPR);
+}
+
+static int stm32_rtc_enter_init_mode(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ u32 isr = readl(priv->base + STM32_RTC_ISR);
+
+ if (!(isr & STM32_RTC_ISR_INITF)) {
+ isr |= STM32_RTC_ISR_INIT;
+ writel(isr, priv->base + STM32_RTC_ISR);
+
+ return readl_poll_timeout(priv->base + STM32_RTC_ISR,
+ isr,
+ (isr & STM32_RTC_ISR_INITF),
+ 100000);
+ }
+
+ return 0;
+}
+
+static int stm32_rtc_wait_sync(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ u32 isr = readl(priv->base + STM32_RTC_ISR);
+
+ isr &= ~STM32_RTC_ISR_RSF;
+ writel(isr, priv->base + STM32_RTC_ISR);
+
+ /*
+ * Wait for RSF to be set to ensure the calendar registers are
+ * synchronised, it takes around 2 rtc_ck clock cycles
+ */
+ return readl_poll_timeout(priv->base + STM32_RTC_ISR,
+ isr, (isr & STM32_RTC_ISR_RSF),
+ 100000);
+}
+
+static void stm32_rtc_exit_init_mode(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ u32 isr = readl(priv->base + STM32_RTC_ISR);
+
+ isr &= ~STM32_RTC_ISR_INIT;
+ writel(isr, priv->base + STM32_RTC_ISR);
+}
+
+static int stm32_rtc_set_time(struct udevice *dev, u32 time, u32 date)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ stm32_rtc_unlock(dev);
+
+ ret = stm32_rtc_enter_init_mode(dev);
+ if (ret)
+ goto lock;
+
+ writel(time, priv->base + STM32_RTC_TR);
+ writel(date, priv->base + STM32_RTC_DR);
+
+ stm32_rtc_exit_init_mode(dev);
+
+ ret = stm32_rtc_wait_sync(dev);
+
+lock:
+ stm32_rtc_lock(dev);
+ return ret;
+}
+
+static int stm32_rtc_set(struct udevice *dev, const struct rtc_time *tm)
+{
+ u32 t, d;
+
+ dev_dbg(dev, "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_wday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ /* Time in BCD format */
+ t = (bin2bcd(tm->tm_sec) << STM32_RTC_SEC_SHIFT) & STM32_RTC_SEC;
+ t |= (bin2bcd(tm->tm_min) << STM32_RTC_MIN_SHIFT) & STM32_RTC_MIN;
+ t |= (bin2bcd(tm->tm_hour) << STM32_RTC_HOUR_SHIFT) & STM32_RTC_HOUR;
+
+ /* Date in BCD format */
+ d = (bin2bcd(tm->tm_mday) << STM32_RTC_DATE_SHIFT) & STM32_RTC_DATE;
+ d |= (bin2bcd(tm->tm_mon) << STM32_RTC_MONTH_SHIFT) & STM32_RTC_MONTH;
+ d |= (bin2bcd(tm->tm_year) << STM32_RTC_YEAR_SHIFT) & STM32_RTC_YEAR;
+ d |= (bin2bcd(tm->tm_wday) << STM32_RTC_WDAY_SHIFT) & STM32_RTC_WDAY;
+
+ return stm32_rtc_set_time(dev, t, d);
+}
+
+static int stm32_rtc_reset(struct udevice *dev)
+{
+ dev_dbg(dev, "Reset DATE\n");
+
+ return stm32_rtc_set_time(dev, 0, 0);
+}
+
+static int stm32_rtc_init(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
+ unsigned int rate;
+ struct clk clk;
+ int ret;
+ u32 isr = readl(priv->base + STM32_RTC_ISR);
+
+ if (isr & STM32_RTC_ISR_INITS)
+ return 0;
+
+ ret = clk_get_by_index(dev, 1, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ clk_free(&clk);
+ return ret;
+ }
+
+ rate = clk_get_rate(&clk);
+
+ /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
+ pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
+ pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
+
+ for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) {
+ pred_s = (rate / (pred_a + 1)) - 1;
+
+ if (((pred_s + 1) * (pred_a + 1)) == rate)
+ break;
+ }
+
+ /*
+ * Can't find a 1Hz, so give priority to RTC power consumption
+ * by choosing the higher possible value for prediv_a
+ */
+ if (pred_s > pred_s_max || pred_a > pred_a_max) {
+ pred_a = pred_a_max;
+ pred_s = (rate / (pred_a + 1)) - 1;
+ }
+
+ stm32_rtc_unlock(dev);
+
+ ret = stm32_rtc_enter_init_mode(dev);
+ if (ret) {
+ dev_err(dev,
+ "Can't enter in init mode. Prescaler config failed.\n");
+ goto unlock;
+ }
+
+ prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
+ prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
+ writel(prer, priv->base + STM32_RTC_PRER);
+
+ /* Force 24h time format */
+ cr = readl(priv->base + STM32_RTC_CR);
+ cr &= ~STM32_RTC_CR_FMT;
+ writel(cr, priv->base + STM32_RTC_CR);
+
+ stm32_rtc_exit_init_mode(dev);
+
+ ret = stm32_rtc_wait_sync(dev);
+
+unlock:
+ stm32_rtc_lock(dev);
+
+ if (ret) {
+ clk_disable(&clk);
+ clk_free(&clk);
+ }
+
+ return ret;
+}
+
+static int stm32_rtc_probe(struct udevice *dev)
+{
+ struct stm32_rtc_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ int ret;
+
+ priv->base = dev_read_addr(dev);
+ if (priv->base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ clk_free(&clk);
+ return ret;
+ }
+
+ ret = stm32_rtc_init(dev);
+
+ if (ret) {
+ clk_disable(&clk);
+ clk_free(&clk);
+ }
+
+ return ret;
+}
+
+static const struct rtc_ops stm32_rtc_ops = {
+ .get = stm32_rtc_get,
+ .set = stm32_rtc_set,
+ .reset = stm32_rtc_reset,
+};
+
+static const struct udevice_id stm32_rtc_ids[] = {
+ { .compatible = "st,stm32mp1-rtc" },
+ { }
+};
+
+U_BOOT_DRIVER(rtc_stm32) = {
+ .name = "rtc-stm32",
+ .id = UCLASS_RTC,
+ .probe = stm32_rtc_probe,
+ .of_match = stm32_rtc_ids,
+ .ops = &stm32_rtc_ops,
+ .priv_auto_alloc_size = sizeof(struct stm32_rtc_priv),
+};
diff --git a/drivers/spi/stm32_qspi.c b/drivers/spi/stm32_qspi.c
index 8d612f22d6..958c394a1a 100644
--- a/drivers/spi/stm32_qspi.c
+++ b/drivers/spi/stm32_qspi.c
@@ -526,7 +526,6 @@ static const struct dm_spi_ops stm32_qspi_ops = {
};
static const struct udevice_id stm32_qspi_ids[] = {
- { .compatible = "st,stm32-qspi" },
{ .compatible = "st,stm32f469-qspi" },
{ }
};