[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [PATCH] arm/rtc: enable pl031 for arm64.



Hello Jianyong Wu,

Please find the comments inline:

Thanks & Regards
Sharan

On 3/31/20 11:57 AM, Jianyong Wu wrote:
Currently, rtc is not enabled in arm, so wall time can't
be provided currectly.
pl031 is chosen as the rtc device for arm in this patch, but
we have interface extension of capable of plugging other rtc device.

This patch use the new fdt API of "fdt_node_offset_idx_by_compatible_list"
in Justin's patch in review.

Signed-off-by: Wei Chen <wei.chen@xxxxxxx>
Signed-off-by: Jianyong Wu <jianyong.wu@xxxxxxx>
---
  plat/common/arm/time.c         |   2 +
  plat/drivers/include/rtc/rtc.h |  77 ++++++++
  plat/drivers/rtc/pl031.c       | 315 +++++++++++++++++++++++++++++++++
  plat/kvm/Config.uk             |   5 +
  plat/kvm/Makefile.uk           |  11 +-
  5 files changed, 409 insertions(+), 1 deletion(-)
  create mode 100644 plat/drivers/include/rtc/rtc.h
  create mode 100644 plat/drivers/rtc/pl031.c

diff --git a/plat/common/arm/time.c b/plat/common/arm/time.c
index bbb3c72..7560fff 100644
--- a/plat/common/arm/time.c
+++ b/plat/common/arm/time.c
@@ -150,4 +150,6 @@ void ukplat_time_init(void)
/* Enable timer */
        generic_timer_enable();
+       /* Initialize rtc */
+       _dtb_init_rtc(_libkvmplat_cfg.dtb);
  }
diff --git a/plat/drivers/include/rtc/rtc.h b/plat/drivers/include/rtc/rtc.h
new file mode 100644
index 0000000..4dafb87
--- /dev/null
+++ b/plat/drivers/include/rtc/rtc.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Wei Chen <Wei.Chen@xxxxxxx>
+ *          Jianyong Wu <Jianyong.Wu@xxxxxxx>
+ *
+ * Copyright (c) 2019, Arm Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.

Remove the above line since it is incompatible with the BSD license.


+ */
+
+#ifndef __PLAT_KVM_ARM_RTC_H__
+#define __PLAT_KVM_ARM_RTC_H__
+
+struct rtc_time {
+       int year;
+       int mon;
+       int day;
+       int hour;
+       int min;
+       int sec;
+};
+
+struct rtc_ops {
+       void (*enable)(int);
+       int (*status)(void);
+       void (*read)(struct rtc_time *);
+       uint32_t (*read_raw)(void);
+       void (*write)(struct rtc_time *);
+       void (*write_raw)(uint32_t);

We should rename it as read_time and the write_time. I am not sure why we need to expose the *_raw as a part of the rtc_ops operation because this is used only for a specific operation.

Instead we could read the boot tick with an API function like:

uint32_t rtc_boot_tick_get(struct rtc_dev *dev);


+       void (*read_alarm)(struct rtc_time *);
+       uint32_t (*read_alarm_raw)(void);
+       void (*write_alarm)(struct rtc_time *);
+       void (*write_alarm_raw)(uint32_t);
+       void (*alarm_irq_enable)(int);
+};

Why don't these rtc_ops take the rtc_dev as its parameter. It would be difficult to support multiple device.


+
+struct rtc_dev {
+       char *name;
+       int id;
+       const struct rtc_ops *ops;
+};
+
+#ifdef CONFIG_RTC_PL031
+
+#include <stdint.h>
+
+extern uint32_t rtc_boot_seconds;
+
+int _dtb_init_rtc(void *dtb);
+
+#endif
+#endif //__PLAT_KVM_ARM_GICV2_H__
s/__PLAT_KVM_ARM_GICV2_H__/__PLAT_KVM_ARM_RTC_H__
diff --git a/plat/drivers/rtc/pl031.c b/plat/drivers/rtc/pl031.c
new file mode 100644
index 0000000..ff24411
--- /dev/null
+++ b/plat/drivers/rtc/pl031.c
@@ -0,0 +1,315 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Wei Chen <Wei.Chen@xxxxxxx>
+ *          Jianyong Wu <Jianyong.Wu@xxxxxxx>
+ *
+ * Copyright (c) 2018, Arm Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.

Remove the above line since it is incompatible with the BSD license.


+ */
+#include <string.h>
+#include <libfdt.h>
+#include <stdio.h>
+#include <uk/assert.h>
+#include <uk/essentials.h>
+#include <uk/print.h>
+#include <uk/list.h>
+#include <arm/cpu.h>
+#include <ofw/fdt.h>
+#include <rtc/rtc.h>
+#include <gic/gic-v2.h>
+#include <ofw/gic_fdt.h>
+#include <uk/plat/common/irq.h>
+
+static uint64_t rtc_base_addr;
+uint32_t rtc_boot_seconds;
+
+/* Define offset of RTC registers */
+#define RTC_DR 0
+#define RTC_MR 0x4
+#define RTC_LR 0x8
+#define RTC_CR 0xc
+#define RTC_IMSC       0x10
+#define RTC_RIS        0x14
+#define RTC_MIS        0x18
+#define RTC_ICR        0x1c
+
+#define RTC_REG(r)     (void *)(rtc_base_addr + (r))
+
+#define RTC_DEV_NUM 1
+#define RTC_ENABLE 1
+#define RTC_DISABLE 0
+
+static int day_per_mon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static const char * const rtc_device_list[] = {
+       "arm,pl031",
+};
+
+static void pl031_raw_to_tm(uint32_t raw, struct rtc_time *rt)

 Move these function into a header file `rtc.h` as a static inline function as these are generic conversion functions.


+{
+       int hour, days, years, dy4, dy100, dy400, normal_days,
+                       day_in_year, sum = 0, leap;
+
+       rt->sec = raw % 60;
+       rt->min = (raw % (60 * 60)) / 60;
+       hour = raw / 60 / 60;
+       days =  hour / 24;
+       rt->hour = hour % 24;
+       /*
+        * total days for every continue 4-years, assuming there is a leap
+        * year among every 4 years.
+        */
+       dy4 = 365 * 3 + 366;
+       // total days for every continue 100-years.
+       dy100 = 25 * dy4 - 1;
+       // total days for every continue 400-years.
+       dy400 = dy100 * 4 + 1;
+       // normalize the days by get rid of the additional day in leap year
+       normal_days = days - days / dy4 + days / dy100 + days / dy400;
+       years = normal_days / 365;
+       rt->year = 1970 + years;
+       leap = ((!(rt->year % 4) && (rt->year % 100)) || !(rt->year % 400));
+       day_in_year =  normal_days - years * 365;
+       /*
+        * if the residue days larger than the sum of the first two month
+        * we should consider Feb,29.
+        */
+       sum += leap * (day_in_year >= (day_per_mon[0] + day_per_mon[1]));
+       for (int i = 0; i < 12; i++) {
+               sum += day_per_mon[i];
+               if (day_in_year < sum) {
+                       rt->mon = i + 1;
+                       rt->day = day_in_year - (sum - day_per_mon[i]) + 1;
+                       break;
+               }
+       }
+}
+
+static uint32_t pl031_tm_to_raw(struct rtc_time *rt)
Move these function into a header file `rtc.h` as a static inline function.
+{
+       int leaps, leap, days, sec;
+
+       leaps = (rt->year - 1970) / 4 - (rt->year - 1970) / 100 +
+                       (rt->year - 1970) / 400;
+       leap = ((!(rt->year % 4) && (rt->year % 100)) || !(rt->year % 400));
+       days = (rt->year - 1970) * 365 + leaps;
+       if (rt->mon == 1) {
+               days += day_per_mon[0];
+       } else {
+               for (int i = 0; i < rt->mon - 1; i++)
+                       days += day_per_mon[i];
+       }
+       days += rt->day + (rt->mon > 2) * leap - 1;
+       sec = days * 3600 * 24 + rt->hour * 3600 + rt->min * 60 + rt->sec;
+
+       return sec;
+}
+
+static uint32_t pl031_read_raw(void)
+{
+       return ioreg_read32(RTC_REG(RTC_DR));
+}
+
+static void pl031_read_time(struct rtc_time *rt)
+{
+       uint32_t raw;
+
+       raw = pl031_read_raw();
+       pl031_raw_to_tm(raw, rt);
+}
+
+static void pl031_write_raw(uint32_t val)
+{
+       ioreg_write32(RTC_REG(RTC_LR), val);
+}
+
+static void pl031_write_time(struct rtc_time *rt)
+{
+       uint32_t raw;
+
+       raw = pl031_tm_to_raw(rt);
+       pl031_write_raw(raw);
+}
+
+/*
+ * set rtc match register comparing with counter
+ * value to generat a interrupt
+ */
+static void pl031_write_alarm_raw(uint32_t alarm)
+{
+       ioreg_write32(RTC_REG(RTC_MR), alarm);
+}
+
+static void pl031_write_alarm(struct rtc_time *rt)
+{
+       uint32_t raw;
+
+       raw = pl031_tm_to_raw(rt);
+       pl031_write_alarm_raw(raw);
+}
+
+static uint32_t pl031_read_alarm_raw(void)
+{
+       return ioreg_read32(RTC_REG(RTC_MR));
+}
+
+static void pl031_read_alarm(struct rtc_time *rt)
+{
+       pl031_raw_to_tm(pl031_read_alarm_raw(), rt);
+}
+
+/*
+ * If pl031 is not enabled, enable it by write 1 to RTC_CR, otherwise
+ * do nothing.
+ */
+static void pl031_enable(int enable)
+{
+       ioreg_write32(RTC_REG(RTC_CR), enable);
+}
+
+/* return rtc status, 1 denotes enable and 0 denotes disable */
+static int pl031_get_status(void)
+{
+       int val;
+
+       val = ioreg_read32(RTC_REG(RTC_CR));
+       val &= RTC_ENABLE;
+       return val;
+}
+
+/* enable alarm irq, 1 denotes enable, 2 denotes disable */
+static void pl031_enable_intr(int enable)
+{
+       ioreg_write32(RTC_REG(RTC_IMSC), enable);
+}
+
+static uint32_t pl031_get_raw_intr_state(void)
+{
+       return ioreg_read32(RTC_REG(RTC_RIS));
+}
+
+static void pl031_clear_intr(void)
+{
+       while (pl031_get_raw_intr_state())
+               ioreg_write32(RTC_REG(RTC_ICR), 1);
+}
+
+/* wait for platform device framework to register this handler */
+int pl031_irq_handler(void *arg __unused)
+{
+       pl031_clear_intr();
+       // TODO: do something real
+
+       return 1;
+}
+
+static const struct rtc_ops ops_pl031 = {
+       .enable         = pl031_enable,
+       .status         = pl031_get_status,
+       .read           = pl031_read_time,
+       .read_raw       = pl031_read_raw,
+       .write          = pl031_write_time,
+       .write_raw      = pl031_write_raw,
+       .read_alarm     = pl031_read_alarm,
+       .read_alarm_raw = pl031_read_alarm_raw,
+       .write_alarm    = pl031_write_alarm,
+       .write_alarm_raw        = pl031_write_alarm_raw,
+       .alarm_irq_enable       = pl031_enable_intr,
+};
+
+const struct rtc_dev rtc_pl031 = {
+       .name   = "rtc_pl031",
+       .id     = 0,
+       .ops    = &ops_pl031,
+};

Do we need to combine the rtc_pl031 driver code with a generic rtc library code. The code below this comment belongs to generic `plat/driver/rtc/rtc.c`. While we do this change we should also split it into 2 libraries namely,

librtc and the libpl031.

+
+const struct rtc_dev *rtc_list[RTC_DEV_NUM];
Why should we define this as an array. We might define it using the list implementation `include/uk/list.h`
+
+static void rtc_dev_register(void)

Register function with parameter struct rtc_dev.


+{
+       rtc_list[rtc_pl031.id] = &rtc_pl031;
+}
+
+void _dtb_init_rtc(void *dtb)
+{
+       uint64_t size;
+       uint32_t irq_type, hwirq, trigger_type;
+       int fdt_rtc, ret, index, irq, rc;
+
+       uk_pr_info("Probing RTC...\n");
+       /*
+        * We choose the first available rtc device in device list as the
+        * system rtc.
+        */
+       fdt_rtc = fdt_node_offset_idx_by_compatible_list(dtb, -1,
+                       rtc_device_list, &index);

Missing this function. I guess this should be fdt_node_offset_by_compatible_list.


+       if (fdt_rtc < 0) {
+               uk_pr_warn("Could not find rtc device!, fdt_rtc is %d\n",
+                       fdt_rtc);
+               return;
+       }
+
+       ret = fdt_get_address(dtb, fdt_rtc, 0, &rtc_base_addr, &size);
+       if (ret < 0) {
+               uk_pr_warn("Could not get rtc address\n");
Print the error code.
+               return;
+       }
+
+       rc = gic_get_irq_from_dtb(dtb, fdt_rtc, 0, &irq_type, &hwirq,
+                       &trigger_type);
+       if (rc < 0) {
+               uk_pr_warn("Failed to find IRQ number from DTB\n");
Print the error code.
+               return;
+       }
+
+       irq = gic_irq_translate(irq_type, hwirq);
+       if (irq < 0 || irq >= __MAX_IRQ) {
+               uk_pr_warn("Failed to translate IRQ number, type=%u,
+                          hwirq=%u\n", irq_type, hwirq);
+               return;
+       }
+
+       rc = ukplat_irq_register(irq, pl031_irq_handler, NULL);
+       if (rc < 0) {
+               uk_pr_warn("Failed to register rtc interrupt handler\n");
Print the error code.
+               return;
+       }
+
+       rtc_dev_register();
+
+       if (!rtc_list[index]->ops->status())
+               rtc_list[index]->ops->enable(RTC_ENABLE);
+
+       /* Record the boot seconds */
+       rtc_boot_seconds = rtc_list[index]->ops->read_raw();
+       /* Disable rtc alarm irq at its reset */
+       rtc_list[index]->ops->alarm_irq_enable(RTC_DISABLE);
+
+       uk_pr_info("Found RTC on: %lu\n", rtc_base_addr);
+}
diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk
index 3372b6c..1bf72f3 100644
--- a/plat/kvm/Config.uk
+++ b/plat/kvm/Config.uk
@@ -133,6 +133,11 @@ config LIBGICV2
         select LIBOFW
         depends on ARCH_ARM_64
+config LIBPL031
+       bool "Arm platform rtc device driver"
+       default y if ARCH_ARM_64
+       depends on ARCH_ARM_64
+
  config LIBOFW
         bool "Open Firmware library support"
         default n
diff --git a/plat/kvm/Makefile.uk b/plat/kvm/Makefile.uk
index a6d6f5e..d4f4cfd 100644
--- a/plat/kvm/Makefile.uk
+++ b/plat/kvm/Makefile.uk
@@ -14,7 +14,7 @@ $(eval $(call 
addplatlib_s,kvm,libkvmvirtioblk,$(CONFIG_VIRTIO_BLK)))
  $(eval $(call addplatlib_s,kvm,libkvmvirtio9p,$(CONFIG_VIRTIO_9P)))
  $(eval $(call addplatlib_s,kvm,libkvmofw,$(CONFIG_LIBOFW)))
  $(eval $(call addplatlib_s,kvm,libkvmgicv2,$(CONFIG_LIBGICV2)))
-
+$(eval $(call addplatlib_s,kvm,libkvmpl031,$(CONFIG_LIBPL031)))
  ##
  ## Platform library definitions
  ##
@@ -181,3 +181,12 @@ LIBKVMGICV2_CINCLUDES-y         += 
-I$(UK_PLAT_COMMON_BASE)/include
  LIBKVMGICV2_CINCLUDES-y         += -I$(UK_PLAT_DRIVERS_BASE)/include
LIBKVMGICV2_SRCS-y += $(UK_PLAT_DRIVERS_BASE)/gic/gic-v2.c
+
+##
+## RTC-PL031 library definitions
+##
+LIBKVMPL031_CINCLUDES-y                += -I$(LIBKVMPLAT_BASE)/include
Does it need some header from the  KVM platform?
+LIBKVMPL031_CINCLUDES-y                += -I$(UK_PLAT_COMMON_BASE)/include
+LIBKVMPL031_CINCLUDES-y                += -I$(UK_PLAT_DRIVERS_BASE)/include
+
+LIBKVMPL031_SRCS-y += $(UK_PLAT_DRIVERS_BASE)/rtc/pl031.c



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.