From 187573405d9fd1d070f035806769cfee52224ac9 Mon Sep 17 00:00:00 2001
Message-Id: <187573405d9fd1d070f035806769cfee52224ac9.1540752056.git.aditya@kobol.io>
In-Reply-To: <3eb15c0c6a0f26e418074cf3be9490a36f9161fd.1540752056.git.aditya@kobol.io>
References: <3eb15c0c6a0f26e418074cf3be9490a36f9161fd.1540752056.git.aditya@kobol.io>
From: Jon Nettleton <jon@solid-run.com>
Date: Thu, 24 Aug 2017 22:28:06 +0200
Subject: [PATCH 03/11] mvebu: rtc: Add DM driver for mvebu rtc

This is heavily based on the linux kernel driver.  Please note the
long timeout I have added.  I have found that adding this additional
time fixes a lot of the other timing problems that were worked around
with various delays and repeated commands.

Signed-off-by: Jon Nettleton <jon@solid-run.com>
---
 drivers/rtc/Kconfig     |   7 +++
 drivers/rtc/Makefile    |   1 +
 drivers/rtc/mvebu_rtc.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 159 insertions(+)

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index bcc01b1..59b3d2c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -31,6 +31,13 @@ config TPL_DM_RTC
 	  drivers to perform the actual functions. See rtc.h for a
 	  description of the API.
 
+config RTC_MVEBU
+        bool "Armada 38x Marvell SoC RTC"
+        depends on DM_RTC
+        help
+          If you say yes here you will get support for the in-chip RTC
+          that can be found in the Armada 38x Marvell's SoC device
+
 config RTC_PCF2127
 	bool "Enable PCF2127 driver"
 	depends on DM_RTC
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 1724602..0e23190 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_RTC_MCP79411) += ds1307.o
 obj-$(CONFIG_MCFRTC) += mcfrtc.o
 obj-$(CONFIG_RTC_MK48T59) += mk48t59.o
 obj-$(CONFIG_RTC_MV) += mvrtc.o
+obj-$(CONFIG_RTC_MVEBU) += mvebu_rtc.o
 obj-$(CONFIG_RTC_MX27) += mx27rtc.o
 obj-$(CONFIG_RTC_MXS) += mxsrtc.o
 obj-$(CONFIG_RTC_PCF8563) += pcf8563.o
diff --git a/drivers/rtc/mvebu_rtc.c b/drivers/rtc/mvebu_rtc.c
new file mode 100644
index 0000000..b04d8e6
--- /dev/null
+++ b/drivers/rtc/mvebu_rtc.c
@@ -0,0 +1,151 @@
+/*
+ * (C) Copyright 2015 Solid Run Ltd.
+ * Author: Jon Nettleton <jon@solid-run.com>
+ *
+ * Based on Linux Kernel driver rtc-armada38x.c
+ *   Copyright (C) 2015 Marvell
+ *   Gregory Clement <gregory.clement@free-electrons.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <rtc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define RTC_NOMINAL_TIMING              0x2000
+
+#define RTC_STATUS          0x0
+#define RTC_STATUS_ALARM1   BIT(0)
+#define RTC_STATUS_ALARM2   BIT(1)
+#define RTC_TIME            0xC
+#define RTC_ALARM1          0x10
+#define RTC_CLOCK_CORR      0x18
+#define RTC_TEST_CONF       0x1C
+
+/* armada38x SoC registers  */
+#define RTC_38X_BRIDGE_TIMING_CTRL_REG_OFFS             0x0
+#define RTC_38X_WRCLK_PERIOD_OFFS                       0
+#define RTC_38X_WRCLK_PERIOD_MASK                       (0x3FF << RTC_38X_WRCLK_PERIOD_OFFS)
+#define RTC_38X_READ_OUTPUT_DELAY_OFFS                  26
+#define RTC_38X_READ_OUTPUT_DELAY_MASK                  (0x1F << RTC_38X_READ_OUTPUT_DELAY_OFFS)
+
+struct mvebu_rtc_platdata {
+	fdt_addr_t base;
+	fdt_addr_t soc_base;
+};
+
+/*
+ * According to the datasheet, the OS should wait 5us after every
+ * register write to the RTC hard macro so that the required update
+ * can occur without holding off the system bus
+ * According to errata FE-3124064, Write to any RTC register
+ * may fail. As a workaround, before writing to RTC
+ * register, issue a dummy write of 0x0 twice to RTC Status
+ * register.
+ */
+
+static void rtc_delayed_write(u32 val, struct mvebu_rtc_platdata *rtc, int offset)
+{
+        writel(0, rtc->base + RTC_STATUS);
+        writel(0, rtc->base + RTC_STATUS);
+        writel(val, rtc->base + offset);
+        mdelay(10);
+}
+
+static unsigned long read_rtc_reg(struct mvebu_rtc_platdata *rtc, uint8_t rtc_reg)
+{
+        unsigned long value = readl(rtc->base + rtc_reg);
+
+        return value;
+}
+
+static void rtc_update_38x_mbus_timing_params(struct mvebu_rtc_platdata *rtc)
+{
+        uint32_t reg;
+
+        reg = readl(rtc->soc_base + RTC_38X_BRIDGE_TIMING_CTRL_REG_OFFS);
+        reg &= ~RTC_38X_WRCLK_PERIOD_MASK;
+        reg |= 0x3FF << RTC_38X_WRCLK_PERIOD_OFFS; /*Maximum value*/
+        reg &= ~RTC_38X_READ_OUTPUT_DELAY_MASK;
+        reg |= 0x1F << RTC_38X_READ_OUTPUT_DELAY_OFFS; /*Maximum value*/
+        writel(reg, rtc->soc_base + RTC_38X_BRIDGE_TIMING_CTRL_REG_OFFS);
+}
+
+static int mvebu_rtc_get(struct udevice *dev, struct rtc_time *tm)
+{
+	struct mvebu_rtc_platdata *rtc = dev_get_platdata(dev);
+
+	rtc_to_tm(read_rtc_reg(rtc, RTC_TIME), tm);
+
+	return 0;
+}
+
+static int mvebu_rtc_set(struct udevice *dev, const struct rtc_time *tm)
+{
+	struct mvebu_rtc_platdata *rtc = dev_get_platdata(dev);
+	unsigned long time;
+
+	time = rtc_mktime(tm);
+	rtc_delayed_write(time, rtc, RTC_TIME);
+
+	return 0;
+}
+
+static int mvebu_rtc_reset(struct udevice *dev)
+{
+	struct mvebu_rtc_platdata *rtc = dev_get_platdata(dev);
+
+	rtc_delayed_write(0, rtc, RTC_TEST_CONF);
+	rtc_delayed_write(0, rtc, RTC_TIME);
+	rtc_delayed_write((RTC_STATUS_ALARM1 | RTC_STATUS_ALARM2), rtc, RTC_STATUS);
+	rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CLOCK_CORR);
+
+	return 0;
+}
+
+static int mvebu_rtc_read8(struct udevice *dev, unsigned int reg)
+{
+	return -ENOSYS;
+}
+
+static int mvebu_rtc_write8(struct udevice *dev, unsigned int reg, int val)
+{
+	return -ENOSYS;
+}
+
+static int mvebu_rtc_probe(struct udevice *dev)
+{
+	struct mvebu_rtc_platdata *rtc = dev_get_platdata(dev);
+
+	rtc->base = devfdt_get_addr(dev);
+	rtc->soc_base = devfdt_get_addr_name(dev, "rtc-soc");
+
+	rtc_update_38x_mbus_timing_params(rtc);
+
+	return 0;
+}
+
+static const struct rtc_ops mvebu_rtc_ops = {
+	.get = mvebu_rtc_get,
+	.set = mvebu_rtc_set,
+	.reset = mvebu_rtc_reset,
+	.read8 = mvebu_rtc_read8,
+	.write8 = mvebu_rtc_write8,
+};
+
+static const struct udevice_id mvebu_rtc_ids[] = {
+	{ .compatible = "marvell,armada-380-rtc" },
+	{ }
+};
+
+U_BOOT_DRIVER(rtc_mvebu) = {
+	.name	= "rtc-mvebu",
+	.id	= UCLASS_RTC,
+	.of_match = mvebu_rtc_ids,
+	.probe = mvebu_rtc_probe,
+	.ops	= &mvebu_rtc_ops,
+};
-- 
2.7.4