All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Marek Behún" <marek.behun@nic.cz>
To: u-boot@lists.denx.de
Subject: [PATCH u-boot-marvell 1/4] rtc: add armada38x driver
Date: Tue,  9 Feb 2021 23:55:11 +0100	[thread overview]
Message-ID: <20210209225514.30003-1-marek.behun@nic.cz> (raw)

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>
Cc: Pali Roh?r <pali@kernel.org>
Cc: Stefan Roese <sr@denx.de>
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 | 185 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 193 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..88dc9977c6
--- /dev/null
+++ b/drivers/rtc/armada38x.c
@@ -0,0 +1,185 @@
+// 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 <common.h>
+#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

             reply	other threads:[~2021-02-09 22:55 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-09 22:55 Marek Behún [this message]
2021-02-09 22:55 ` [PATCH u-boot-marvell 2/4] arm: mvebu: turris_omnia: Enable DM_RTC and RTC_ARMADA38X Marek Behún
2021-02-09 22:55 ` [PATCH u-boot-marvell 3/4] arm: mvebu: turris_omnia: Enable CMD_WDT Marek Behún
2021-02-09 22:55 ` [PATCH u-boot-marvell 4/4] arm: mvebu: turris_omnia: Enable CMD_FS_UUID Marek Behún
2021-02-19 17:15 ` [PATCH u-boot-marvell 1/4] rtc: add armada38x driver Marek Behún
2021-02-26  9:19 ` Stefan Roese

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210209225514.30003-1-marek.behun@nic.cz \
    --to=marek.behun@nic.cz \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.