diff mbox series

[u-boot-marvell,v2,1/4] rtc: add armada38x driver

Message ID 20210226093022.9208-1-marek.behun@nic.cz
State Accepted
Commit aefbc2c2a2bb2c3b0d31a97397dba255c46d20f9
Delegated to: Stefan Roese
Headers show
Series [u-boot-marvell,v2,1/4] rtc: add armada38x driver | expand

Commit Message

Marek Behun Feb. 26, 2021, 9:30 a.m. UTC
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>
---
 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

Comments

Pali Rohár March 28, 2021, 7:24 p.m. UTC | #1
On Friday 26 February 2021 10:30:19 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>

Acked-by: 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>
> ---
>  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,
> +};
> -- 
> 2.26.2
>
Stefan Roese April 8, 2021, 8:51 a.m. UTC | #2
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
diff mbox series

Patch

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,
+};