
On 26.02.21 10:30, Marek Behún wrote:
Add RTC driver for Armada 38x, based on Linux' driver. For now implement only `marvell,armada-380-rtc` compatible.
Signed-off-by: Marek Behún marek.behun@nic.cz Reviewed-by: Stefan Roese sr@denx.de Cc: Pali Rohár pali@kernel.org Cc: Baruch Siach baruch@tkos.co.il Cc: Chris Packham judge.packham@gmail.com Cc: Simon Glass sjg@chromium.org
Applied to u-boot-marvell/master (whole series)
Thanks, Stefan
drivers/rtc/Kconfig | 7 ++ drivers/rtc/Makefile | 1 + drivers/rtc/armada38x.c | 184 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/rtc/armada38x.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index aa6d90158c..dafba35279 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -38,6 +38,13 @@ config RTC_ENABLE_32KHZ_OUTPUT Some real-time clocks support the output of 32kHz square waves (such as ds3231), the config symbol choose Real Time Clock device 32Khz output feature.
+config RTC_ARMADA38X
- bool "Enable Armada 38x Marvell SoC RTC"
- depends on DM_RTC && ARCH_MVEBU
- help
This adds support for the in-chip RTC that can be found in the
Armada 38x Marvell's SoC devices.
- config RTC_PCF2127 bool "Enable PCF2127 driver" depends on DM_RTC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6a45a9c874..15609e7b18 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_RTC) += rtc-uclass.o
obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o obj-y += rtc-lib.o +obj-$(CONFIG_RTC_ARMADA38X) += armada38x.o obj-$(CONFIG_RTC_DAVINCI) += davinci.o obj-$(CONFIG_RTC_DS1302) += ds1302.o obj-$(CONFIG_RTC_DS1306) += ds1306.o diff --git a/drivers/rtc/armada38x.c b/drivers/rtc/armada38x.c new file mode 100644 index 0000000000..2d264acf77 --- /dev/null +++ b/drivers/rtc/armada38x.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- RTC driver for the Armada 38x Marvell SoCs
- Copyright (C) 2021 Marek Behun marek.behun@nic.cz
- Based on Linux' driver by Gregory Clement and Marvell
- */
+#include <asm/io.h> +#include <dm.h> +#include <linux/delay.h> +#include <rtc.h>
+#define RTC_STATUS 0x0 +#define RTC_TIME 0xC +#define RTC_CONF_TEST 0x1C
+/* Armada38x SoC registers */ +#define RTC_38X_BRIDGE_TIMING_CTL 0x0 +#define RTC_38X_PERIOD_OFFS 0 +#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) +#define RTC_38X_READ_DELAY_OFFS 26 +#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS)
+#define SAMPLE_NR 100
+struct armada38x_rtc {
- void __iomem *regs;
- void __iomem *regs_soc;
+};
+/*
- According to Erratum RES-3124064 we have to do some configuration in MBUS.
- To read an RTC register we need to read it 100 times and return the most
- frequent value.
- To write an RTC register we need to write 2x zero into STATUS register,
- followed by the proper write. Linux adds an 5 us delay after this, so we do
- it here as well.
- */
+static void update_38x_mbus_timing_params(struct armada38x_rtc *rtc) +{
- u32 reg;
- reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
- reg &= ~RTC_38X_PERIOD_MASK;
- reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
- reg &= ~RTC_38X_READ_DELAY_MASK;
- reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
- writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
+}
+static void armada38x_rtc_write(u32 val, struct armada38x_rtc *rtc, u8 reg) +{
- writel(0, rtc->regs + RTC_STATUS);
- writel(0, rtc->regs + RTC_STATUS);
- writel(val, rtc->regs + reg);
- udelay(5);
+}
+static u32 armada38x_rtc_read(struct armada38x_rtc *rtc, u8 reg) +{
- u8 counts[SAMPLE_NR], max_idx;
- u32 samples[SAMPLE_NR], max;
- int i, j, last;
- for (i = 0, last = 0; i < SAMPLE_NR; ++i) {
u32 sample = readl(rtc->regs + reg);
/* find if this value was already read */
for (j = 0; j < last; ++j) {
if (samples[j] == sample)
break;
}
if (j < last) {
/* if yes, increment count */
++counts[j];
} else {
/* if not, add */
samples[last] = sample;
counts[last] = 1;
++last;
}
- }
- /* finally find the sample that was read the most */
- max = 0;
- max_idx = 0;
- for (i = 0; i < last; ++i) {
if (counts[i] > max) {
max = counts[i];
max_idx = i;
}
- }
- return samples[max_idx];
+}
+static int armada38x_rtc_get(struct udevice *dev, struct rtc_time *tm) +{
- struct armada38x_rtc *rtc = dev_get_priv(dev);
- u32 time;
- time = armada38x_rtc_read(rtc, RTC_TIME);
- rtc_to_tm(time, tm);
- return 0;
+}
+static int armada38x_rtc_reset(struct udevice *dev) +{
- struct armada38x_rtc *rtc = dev_get_priv(dev);
- u32 reg;
- reg = armada38x_rtc_read(rtc, RTC_CONF_TEST);
- if (reg & 0xff) {
armada38x_rtc_write(0, rtc, RTC_CONF_TEST);
mdelay(500);
armada38x_rtc_write(0, rtc, RTC_TIME);
armada38x_rtc_write(BIT(0) | BIT(1), 0, RTC_STATUS);
- }
- return 0;
+}
+static int armada38x_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{
- struct armada38x_rtc *rtc = dev_get_priv(dev);
- unsigned long time;
- time = rtc_mktime(tm);
- if (time > U32_MAX)
printf("%s: requested time to set will overflow\n", dev->name);
- armada38x_rtc_reset(dev);
- armada38x_rtc_write(time, rtc, RTC_TIME);
- return 0;
+}
+static int armada38x_probe(struct udevice *dev) +{
- struct armada38x_rtc *rtc = dev_get_priv(dev);
- rtc->regs = dev_remap_addr_name(dev, "rtc");
- if (!rtc->regs)
goto err;
- rtc->regs_soc = dev_remap_addr_name(dev, "rtc-soc");
- if (!rtc->regs_soc)
goto err;
- update_38x_mbus_timing_params(rtc);
- return 0;
+err:
- printf("%s: io address missing\n", dev->name);
- return -ENODEV;
+}
+static const struct rtc_ops armada38x_rtc_ops = {
- .get = armada38x_rtc_get,
- .set = armada38x_rtc_set,
- .reset = armada38x_rtc_reset,
+};
+static const struct udevice_id armada38x_rtc_ids[] = {
- { .compatible = "marvell,armada-380-rtc", .data = 0 },
- { }
+};
+U_BOOT_DRIVER(rtc_armada38x) = {
- .name = "rtc-armada38x",
- .id = UCLASS_RTC,
- .of_match = armada38x_rtc_ids,
- .probe = armada38x_probe,
- .priv_auto = sizeof(struct armada38x_rtc),
- .ops = &armada38x_rtc_ops,
+};
Viele Grüße, Stefan