* [PATCH 01/14] rtc-efi: fix decrease day twice when computing year days
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
[not found] ` <1387439515-8926-1-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
` (12 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
Compared with the logic in rtc_year_days of efirtc.c, the code in
rtc-efi decreases value of day twice when it computing year days.
That's becase rtc_year_days in rtc-lib.c already decrease day for
return the year days from 0 to 365.
---
drivers/rtc/rtc-efi.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 797aa02..c4c3843 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -35,7 +35,7 @@ static inline int
compute_yday(efi_time_t *eft)
{
/* efi_time_t.month is in the [1-12] so, we need -1 */
- return rtc_year_days(eft->day - 1, eft->month - 1, eft->year);
+ return rtc_year_days(eft->day, eft->month - 1, eft->year);
}
/*
* returns day of the week [0-6] 0=Sunday
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread[parent not found: <1387439515-8926-1-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>]
* [PATCH 03/14] rtc: block registration of rtc-cmos when CMOS RTC Not Present
[not found] ` <1387439515-8926-1-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
@ 2013-12-19 7:51 ` Lee, Chun-Yi
0 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott-VXdhtT5mjnY, samer.el-haj-mahmoud-VXdhtT5mjnY,
Oliver Neukum, werner-IBi9RG/b67k, trenn-l3A5Bk7waGM,
JBeulich-IBi9RG/b67k, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw, x86-DgEjT+Ai2ygdnm+yROfE0A,
linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-acpi-u79uwXL29TY76Z2rM5mHXA, Lee, Chun-Yi
We should not acess CMOS address when CMOS RTC Not Present bit set in
FADT. The ee5872be patch didn't avoid rtc-cmos driver loaded when system support
ACPI PNP PNP0B0* devices.
So this patch block the registion of rtc-cmos driver to avoid
user space access RTC through CMOS interface.
Signed-off-by: Lee, Chun-Yi <jlee-IBi9RG/b67k@public.gmane.org>
---
arch/x86/kernel/rtc.c | 20 ++++++++++++++++----
drivers/rtc/rtc-cmos.c | 9 +++++++++
2 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index ca9622a..9b6c568 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -174,16 +174,27 @@ static __init int add_rtc_cmos(void)
{ "PNP0b00", "PNP0b01", "PNP0b02", };
struct pnp_dev *dev;
struct pnp_id *id;
- int i;
+ int i = 0;
+ bool found_pnp;
pnp_for_each_dev(dev) {
for (id = dev->id; id; id = id->next) {
for (i = 0; i < ARRAY_SIZE(ids); i++) {
- if (compare_pnp_id(id, ids[i]) != 0)
- return 0;
+ if (compare_pnp_id(id, ids[i]) != 0) {
+ found_pnp = true;
+ goto found_pnp;
+ }
}
}
}
+
+found_pnp:
+ if (found_pnp) {
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC)
+ pr_err(FW_BUG "Found %s device but CMOS RTC Not Present flag set\n", ids[i]);
+ return 0;
+ }
#endif
if (of_have_populated_dt())
return 0;
@@ -193,7 +204,8 @@ static __init int add_rtc_cmos(void)
return -ENODEV;
#ifdef CONFIG_ACPI
- if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
/* This warning can likely go away again in a year or two. */
pr_info("ACPI: not registering RTC platform device\n");
return -ENODEV;
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index f148762..3a84ca9 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -28,6 +28,9 @@
* interrupts disabled, holding the global rtc_lock, to exclude those
* other drivers and utilities on correctly configured systems.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -1144,6 +1147,12 @@ static int __init cmos_init(void)
{
int retval = 0;
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+ pr_info("ACPI CMOS RTC Not Present detected - not loading\n");
+ return 0;
+ }
+
#ifdef CONFIG_PNP
retval = pnp_register_driver(&cmos_pnp_driver);
if (retval == 0)
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [RFC PATCH 04/14] ACPI: Add ACPI 5.0 Time and Alarm Device driver
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
2013-12-19 7:51 ` [PATCH 01/14] rtc-efi: fix decrease day twice when computing year days Lee, Chun-Yi
[not found] ` <1387439515-8926-1-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
@ 2013-12-19 7:51 ` Lee, Chun-Yi
[not found] ` <1387439515-8926-5-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
2013-12-19 7:51 ` [RFC PATCH 05/14] rtc: Add RTC driver of ACPI Time and Alarm Device Lee, Chun-Yi
` (10 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
This patch add the driver of Time and Alarm Device in ACPI 5.0.
Currently it only implemented get/set time functions and grab
the capabilities of device when driver initial.
This driver also register rtc-acpitad platform device for RTC ACPITAD
stub driver using.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
drivers/acpi/Makefile | 3 +
drivers/acpi/acpi_tad.c | 176 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/bus.c | 3 +
drivers/acpi/internal.h | 5 ++
include/linux/acpi.h | 31 ++++++++
5 files changed, 218 insertions(+), 0 deletions(-)
create mode 100644 drivers/acpi/acpi_tad.c
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 0331f91..d250b15 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -50,6 +50,9 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
ifdef CONFIG_ACPI_VIDEO
acpi-y += video_detect.o
endif
+ifdef CONFIG_X86
+acpi-y += acpi_tad.o
+endif
# These are (potentially) separate modules
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
new file mode 100644
index 0000000..c2200f3
--- /dev/null
+++ b/drivers/acpi/acpi_tad.c
@@ -0,0 +1,176 @@
+/* rtc.c - ACPI 5.0 Time and Alarm Driver
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jlee@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <acpi/acpi_drivers.h>
+
+#include <asm/time.h>
+
+#define ACPI_TIME_ALARM_NAME "Time and Alarm"
+ACPI_MODULE_NAME(ACPI_TIME_ALARM_NAME);
+#define ACPI_TIME_ALARM_CLASS "time_alarm"
+
+static const struct acpi_device_id time_alarm_ids[] = {
+ {"ACPI000E", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, time_alarm_ids);
+
+static struct platform_device rtc_acpitad_dev = {
+ .name = "rtc-acpitad",
+ .id = -1,
+};
+
+static struct acpi_device *acpi_tad_dev;
+static unsigned long long cap;
+
+int acpi_read_time(struct acpi_time *output)
+{
+ unsigned long flags;
+ struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ struct acpi_time *acpit;
+ acpi_status status;
+
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ return -EINVAL;
+
+ if (!output)
+ return -EINVAL;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ status = acpi_evaluate_object(acpi_tad_dev->handle, "_GRT", NULL, &result);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GRT"));
+ return -ENODEV;
+ }
+
+ obj = result.pointer;
+ if (!obj ||
+ obj->type != ACPI_TYPE_BUFFER ||
+ obj->buffer.length > sizeof(struct acpi_time) ||
+ obj->buffer.length < offsetof(struct acpi_time, pad2)) {
+ dev_err(&acpi_tad_dev->dev, ACPI_TIME_ALARM_NAME
+ " Invalid _GRT data\n");
+ return -EINVAL;
+ }
+
+ acpit = (struct acpi_time *) obj->buffer.pointer;
+ if (acpit) {
+ output->year = acpit->year;
+ output->month = acpit->month;
+ output->day = acpit->day;
+ output->hour = acpit->hour;
+ output->minute = acpit->minute;
+ output->second = acpit->second;
+ output->milliseconds = acpit->milliseconds;
+ output->timezone = acpit->timezone;
+ output->daylight = acpit->daylight;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_read_time);
+
+int acpi_set_time(struct acpi_time *acpit)
+{
+ unsigned long flags;
+ struct acpi_object_list input;
+ union acpi_object params[1];
+ unsigned long long output;
+ acpi_status status;
+
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ return -EINVAL;
+
+ if (!acpit)
+ return -EINVAL;
+
+ input.count = 1;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(struct acpi_time);
+ params[0].buffer.pointer = (void *) acpit;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ status = acpi_evaluate_integer(acpi_tad_dev->handle, "_SRT", &input, &output);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRT"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_set_time);
+
+int acpi_tad_get_capability(unsigned long *output)
+{
+ if (!acpi_tad_dev)
+ return -ENODEV;
+
+ *output = cap;
+
+ return 0;
+}
+EXPORT_SYMBOL(acpi_tad_get_capability);
+
+static int acpi_time_alarm_add(struct acpi_device *device)
+{
+ acpi_status status;
+
+ if (!device)
+ return -EINVAL;
+
+ acpi_tad_dev = device;
+
+ /* evaluate _GCP */
+ status = acpi_evaluate_integer(device->handle, "_GCP", NULL, &cap);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GCP"));
+ return -ENODEV;
+ }
+
+ if (!(cap & TAD_CAP_GETSETTIME))
+ pr_warn(FW_INFO "Get/Set real time features not available.\n");
+
+ if (platform_device_register(&rtc_acpitad_dev) < 0)
+ pr_err("Unable to register rtc-acpitad device\n");
+
+ return 0;
+}
+
+static struct acpi_driver acpi_time_alarm_driver = {
+ .name = "time_and_alarm",
+ .class = ACPI_TIME_ALARM_CLASS,
+ .ids = time_alarm_ids,
+ .ops = {
+ .add = acpi_time_alarm_add,
+ },
+};
+
+int __init acpi_tad_init(void)
+{
+ int result = 0;
+
+ result = acpi_bus_register_driver(&acpi_time_alarm_driver);
+ if (result < 0)
+ return -ENODEV;
+
+ return result;
+}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index bba9b72..3f7a075 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -689,6 +689,9 @@ static int __init acpi_init(void)
pci_mmcfg_late_init();
acpi_scan_init();
acpi_ec_init();
+#ifdef CONFIG_X86
+ acpi_tad_init();
+#endif
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index a29739c..9cfe589 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -134,6 +134,11 @@ void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void);
void acpi_ec_unblock_transactions_early(void);
+/* --------------------------------------------------------------------------
+ Time and Alarm Device
+ -------------------------------------------------------------------------- */
+int acpi_tad_init(void);
+
/*--------------------------------------------------------------------------
Suspend/Resume
-------------------------------------------------------------------------- */
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index d9099b1..c8dc104 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -173,6 +173,37 @@ extern int ec_transaction(u8 command,
u8 *rdata, unsigned rdata_len);
extern acpi_handle ec_get_handle(void);
+/*
+ * Time and Alarm device capability flags
+ */
+#define TAD_CAP_ACWAKE (1<<0)
+#define TAD_CAP_DCWAKE (1<<1)
+#define TAD_CAP_GETSETTIME (1<<2)
+#define TAD_CAP_ACCURACY (1<<3)
+
+#define ACPI_TIME_AFFECTED_BY_DAYLIGHT (1<<0)
+#define ACPI_TIME_ADJUSTED_FOR_DAYLIGHT (1<<1)
+#define ACPI_ISDST (ACPI_TIME_AFFECTED_BY_DAYLIGHT|ACPI_TIME_ADJUSTED_FOR_DAYLIGHT)
+#define ACPI_UNSPECIFIED_TIMEZONE 2047
+
+struct acpi_time {
+ u16 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ u8 pad1;
+ u16 milliseconds;
+ s16 timezone;
+ u8 daylight;
+ u8 pad2[3];
+};
+
+extern int acpi_read_time(struct acpi_time *acpit);
+extern int acpi_set_time(struct acpi_time *acpit);
+extern int acpi_tad_get_capability(unsigned long *output);
+
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
typedef void (*wmi_notify_handler) (u32 value, void *context);
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 05/14] rtc: Add RTC driver of ACPI Time and Alarm Device
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (2 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 04/14] ACPI: Add ACPI 5.0 Time and Alarm Device driver Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled Lee, Chun-Yi
` (9 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
This patch add the RTC driver of ACPI TAD to provide userspace access
ACPI time through RTC interface.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-acpitad.c | 294 +++++++++++++++++++++++++++++++++++++++++++++
drivers/rtc/rtc-dev.c | 4 +
drivers/rtc/rtc-sysfs.c | 8 ++
include/linux/rtc.h | 5 +
include/uapi/linux/rtc.h | 5 +
7 files changed, 327 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-acpitad.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0077302..349dbc4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -878,6 +878,16 @@ config RTC_DRV_NUC900
If you say yes here you get support for the RTC subsystem of the
NUC910/NUC920 used in embedded systems.
+config RTC_ACPI_TAD
+ tristate "RTC ACPI Time and Alarm Device driver"
+ help
+ This driver exposes ACPI 5.0 Time and Alarm Device as RTC device.
+ Say Y (or M) if you have a computer with ACPI 5.0 firmware that
+ implemented Time and Alarm Device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called rtc_acpitad.
+
comment "on-CPU RTC drivers"
config RTC_DRV_DAVINCI
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 27b4bd8..bca5ab3 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
+obj-$(CONFIG_RTC_ACPI_TAD) += rtc-acpitad.o
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
diff --git a/drivers/rtc/rtc-acpitad.c b/drivers/rtc/rtc-acpitad.c
new file mode 100644
index 0000000..065a033
--- /dev/null
+++ b/drivers/rtc/rtc-acpitad.c
@@ -0,0 +1,294 @@
+/* A RTC driver for ACPI 5.0 Time and Alarm Device
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jlee@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(struct acpi_time *acpit)
+{
+ /* acpi_time.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(acpit->day, acpit->month - 1, acpit->year);
+}
+
+/*
+ * returns day of the week [0-6] 0=Sunday
+ */
+static int
+compute_wday(struct acpi_time *acpit)
+{
+ int y;
+ int ndays = 0;
+
+ if (acpit->year < 1900) {
+ pr_err("ACPI year < 1900, invalid date\n");
+ return -1;
+ }
+
+ for (y = 1900; y < acpit->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(acpit);
+
+ /*
+ * 1=1/1/1900 was a Monday
+ */
+ return (ndays + 1) % 7;
+}
+
+static void
+convert_to_acpi_time(struct rtc_time *tm, struct acpi_time *acpit)
+{
+ acpit->year = tm->tm_year + 1900;
+ acpit->month = tm->tm_mon + 1;
+ acpit->day = tm->tm_mday;
+ acpit->hour = tm->tm_hour;
+ acpit->minute = tm->tm_min;
+ acpit->second = tm->tm_sec;
+ acpit->milliseconds = 0;
+ acpit->daylight = tm->tm_isdst ? ACPI_ISDST : 0;
+}
+
+static void
+convert_from_acpi_time(struct acpi_time *acpit, struct rtc_time *tm)
+{
+ memset(tm, 0, sizeof(*tm));
+ tm->tm_sec = acpit->second;
+ tm->tm_min = acpit->minute;
+ tm->tm_hour = acpit->hour;
+ tm->tm_mday = acpit->day;
+ tm->tm_mon = acpit->month - 1;
+ tm->tm_year = acpit->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ tm->tm_wday = compute_wday(acpit);
+
+ /* day in the year [1-365]*/
+ tm->tm_yday = compute_yday(acpit);
+
+ switch (acpit->daylight & ACPI_ISDST) {
+ case ACPI_ISDST:
+ tm->tm_isdst = 1;
+ break;
+ case ACPI_TIME_AFFECTED_BY_DAYLIGHT:
+ tm->tm_isdst = 0;
+ break;
+ default:
+ tm->tm_isdst = -1;
+ }
+}
+
+static int acpitad_read_gmtoff(struct device *dev, long int *arg)
+{
+ struct acpi_time *acpit;
+ s16 timezone;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ /* transfer minutes to seconds east of UTC for userspace */
+ timezone = (s16)le16_to_cpu(acpit->timezone);
+ *arg = ACPI_UNSPECIFIED_TIMEZONE * 60;
+ if (abs(timezone) != ACPI_UNSPECIFIED_TIMEZONE &&
+ abs(timezone) <= 1440)
+ *arg = timezone * 60 * -1;
+
+error_read:
+ kfree(acpit);
+
+ return ret;
+}
+
+
+static int acpitad_set_gmtoff(struct device *dev, long int arg)
+{
+ struct acpi_time *acpit;
+ s16 timezone;
+ int ret;
+
+ /* transfer seconds east of UTC to minutes for ACPI */
+ timezone = arg / 60 * -1;
+ if (abs(timezone) > 1440 &&
+ abs(timezone) != ACPI_UNSPECIFIED_TIMEZONE)
+ return -EINVAL;
+
+ /* can not use -2047 */
+ if (timezone == ACPI_UNSPECIFIED_TIMEZONE * -1)
+ timezone = ACPI_UNSPECIFIED_TIMEZONE;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ acpit->timezone = (s16)cpu_to_le16(timezone);
+ ret = acpi_set_time(acpit);
+
+error_read:
+ kfree(acpit);
+
+ return ret;
+}
+
+static int acpitad_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ long int gmtoff;
+ int err;
+
+ switch (cmd) {
+ case RTC_RD_GMTOFF:
+ err = acpitad_read_gmtoff(dev, &gmtoff);
+ if (err)
+ return err;
+ return put_user(gmtoff, (unsigned long __user *)arg);
+ case RTC_SET_GMTOFF:
+ return acpitad_set_gmtoff(dev, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int acpitad_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct acpi_time *acpit;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ ret = acpi_read_time(acpit);
+ if (ret)
+ return ret;
+
+ convert_from_acpi_time(acpit, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int acpitad_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct acpi_time *acpit;
+ int ret;
+
+ acpit = kzalloc(sizeof(struct acpi_time), GFP_KERNEL);
+ if (!acpit)
+ return -ENOMEM;
+
+ /* read current timzone to avoid overwrite it by set time */
+ ret = acpi_read_time(acpit);
+ if (ret)
+ goto error_read;
+
+ convert_to_acpi_time(tm, acpit);
+
+ ret = acpi_set_time(acpit);
+
+error_read:
+ kfree(acpit);
+ return ret;
+}
+
+static struct rtc_class_ops acpi_rtc_ops = {
+ .ioctl = acpitad_rtc_ioctl,
+ .read_time = acpitad_read_time,
+ .set_time = acpitad_set_time,
+};
+
+static int acpitad_rtc_probe(struct platform_device *dev)
+{
+ unsigned long cap;
+ struct rtc_device *rtc;
+ int ret;
+
+ ret = acpi_tad_get_capability(&cap);
+ if (ret)
+ return ret;
+
+ if (!(cap & TAD_CAP_GETSETTIME)) {
+ acpi_rtc_ops.read_time = NULL;
+ acpi_rtc_ops.set_time = NULL;
+ pr_warn("No get/set time support\n");
+ }
+
+ /* ACPI Alarm at least need AC wake capability */
+ if (!(cap & TAD_CAP_ACWAKE)) {
+ acpi_rtc_ops.read_alarm = NULL;
+ acpi_rtc_ops.set_alarm = NULL;
+ pr_warn("No AC wake support\n");
+ }
+
+ /* register rtc device */
+ rtc = rtc_device_register("rtc-acpitad", &dev->dev, &acpi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->uie_unsupported = 1;
+ rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int acpitad_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver acpitad_rtc_driver = {
+ .driver = {
+ .name = "rtc-acpitad",
+ .owner = THIS_MODULE,
+ },
+ .probe = acpitad_rtc_probe,
+ .remove = acpitad_rtc_remove,
+};
+
+static int __init acpitad_rtc_init(void)
+{
+ return platform_driver_register(&acpitad_rtc_driver);
+}
+
+static void __exit acpitad_rtc_exit(void)
+{
+ platform_driver_unregister(&acpitad_rtc_driver);
+}
+
+module_init(acpitad_rtc_init);
+module_exit(acpitad_rtc_exit);
+
+MODULE_AUTHOR("Lee, Chun-Yi <jlee@suse.com>");
+MODULE_DESCRIPTION("RTC ACPI Time and Alarm Device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-acpitad");
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index d049393..aab70e7 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -398,6 +398,10 @@ static long rtc_dev_ioctl(struct file *file,
err = -EFAULT;
return err;
+ case RTC_CAPS_READ:
+ err = put_user(rtc->caps, (unsigned int __user *)uarg);
+ break;
+
default:
/* Finally try the driver's ioctl interface */
if (ops->ioctl) {
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index babd43b..bdffb8f 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -122,6 +122,13 @@ hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(hctosys);
+static ssize_t
+caps_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", to_rtc_device(dev)->caps);
+}
+static DEVICE_ATTR_RO(caps);
+
static struct attribute *rtc_attrs[] = {
&dev_attr_name.attr,
&dev_attr_date.attr,
@@ -129,6 +136,7 @@ static struct attribute *rtc_attrs[] = {
&dev_attr_since_epoch.attr,
&dev_attr_max_user_freq.attr,
&dev_attr_hctosys.attr,
+ &dev_attr_caps.attr,
NULL,
};
ATTRIBUTE_GROUPS(rtc);
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index c2c2897..e6380ec 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -116,6 +116,11 @@ struct rtc_device
/* Some hardware can't support UIE mode */
int uie_unsupported;
+ /* Time Zone and Daylight capabilities */
+#define RTC_TZ_CAP (1 << 0)
+#define RTC_DST_CAP (1 << 1)
+ int caps;
+
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
diff --git a/include/uapi/linux/rtc.h b/include/uapi/linux/rtc.h
index f8c82e6..5533914 100644
--- a/include/uapi/linux/rtc.h
+++ b/include/uapi/linux/rtc.h
@@ -94,6 +94,11 @@ struct rtc_pll_info {
#define RTC_VL_READ _IOR('p', 0x13, int) /* Voltage low detector */
#define RTC_VL_CLR _IO('p', 0x14) /* Clear voltage low information */
+#define RTC_RD_GMTOFF _IOR('p', 0x15, long int) /* Read time zone return seconds east of UTC */
+#define RTC_SET_GMTOFF _IOW('p', 0x16, long int) /* Set time zone input seconds east of UTC */
+
+#define RTC_CAPS_READ _IOR('p', 0x17, unsigned int) /* Get capabilities, e.g. TZ, DST */
+
/* interrupt flags */
#define RTC_IRQF 0x80 /* Any of the following is active */
#define RTC_PF 0x40 /* Periodic interrupt */
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (3 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 05/14] rtc: Add RTC driver of ACPI Time and Alarm Device Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 14:09 ` Matt Fleming
2013-12-19 7:51 ` [RFC PATCH 07/14] rtc-efi: add GMTOFF support to rtc_efi Lee, Chun-Yi
` (8 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, Matt Fleming
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi, Matt Fleming, H. Peter Anvin,
Matthew Garrett, Thomas Gleixner, Ingo Molnar
UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.
This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
checks efi_enabled flag and efi-rtc driver should enabled.
Cc: Matt Fleming <matt.fleming@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Matthew Garrett <matthew.garrett@nebula.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jan Beulich <JBeulich@suse.com>
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
drivers/rtc/Kconfig | 2 +-
2 files changed, 18 insertions(+), 1 deletions(-)
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 7a7a692..42d6052 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -42,6 +42,7 @@
#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/bcd.h>
+#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/efi.h>
@@ -876,6 +877,22 @@ void __init efi_enter_virtual_mode(void)
0, NULL);
}
+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (efi_enabled(EFI_RUNTIME_SERVICES) &&
+ platform_device_register(&rtc_efi_dev) < 0)
+ pr_err("unable to register rtc-efi device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+arch_initcall(rtc_init);
+
/*
* Convenience functions to obtain memory types and attributes
*/
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 349dbc4..bee13de 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -726,7 +726,7 @@ config RTC_DRV_DA9055
config RTC_DRV_EFI
tristate "EFI RTC"
- depends on IA64
+ depends on EFI
help
If you say yes here you will get support for the EFI
Real Time Clock.
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled
2013-12-19 7:51 ` [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled Lee, Chun-Yi
@ 2013-12-19 14:09 ` Matt Fleming
2013-12-20 4:24 ` joeyli
0 siblings, 1 reply; 44+ messages in thread
From: Matt Fleming @ 2013-12-19 14:09 UTC (permalink / raw)
To: Lee, Chun-Yi
Cc: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin,
Matthew Garrett, Elliott, samer.el-haj-mahmoud, Oliver Neukum,
werner, trenn, JBeulich, linux-kernel, rtc-linux, x86,
linux-efi@vger.kernel.org, linux-acpi, Lee, Chun-Yi, Matt Fleming,
Thomas Gleixner, Ingo Molnar
On Thu, 19 Dec, at 03:51:47PM, Lee, Chun-Yi wrote:
> UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
> supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.
>
> This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
> checks efi_enabled flag and efi-rtc driver should enabled.
>
> Cc: Matt Fleming <matt.fleming@intel.com>
> Cc: H. Peter Anvin <hpa@zytor.com>
> Cc: Matthew Garrett <matthew.garrett@nebula.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Jan Beulich <JBeulich@suse.com>
> Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
> ---
> arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
> drivers/rtc/Kconfig | 2 +-
> 2 files changed, 18 insertions(+), 1 deletions(-)
This patch needs to be justified. Enabling the EFI runtime *Time
functions just because they're available isn't good enough. We need to
know why this patch improves things, what use case does it solve?
The general attitude has been that we want to invoke the runtime
services less, not more, due to the huge variety of runtime
implementation bugs.
--
Matt Fleming, Intel Open Source Technology Center
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled
2013-12-19 14:09 ` Matt Fleming
@ 2013-12-20 4:24 ` joeyli
[not found] ` <1387513491.3539.4345.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
0 siblings, 1 reply; 44+ messages in thread
From: joeyli @ 2013-12-20 4:24 UTC (permalink / raw)
To: Matt Fleming
Cc: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin,
Matthew Garrett, Elliott, samer.el-haj-mahmoud, Oliver Neukum,
werner, trenn, JBeulich, linux-kernel, rtc-linux, x86,
linux-efi@vger.kernel.org, linux-acpi, Matt Fleming,
Thomas Gleixner, Ingo Molnar
於 四,2013-12-19 於 14:09 +0000,Matt Fleming 提到:
> On Thu, 19 Dec, at 03:51:47PM, Lee, Chun-Yi wrote:
> > UEFI time services, GetTime(), SetTime(), GetWakeupTime(), SetWakeupTime() are also
> > supported by other non-IA64 architecutre with UEFI BIOS, e.g. x86.
> >
> > This patch changed RTC_DRV_EFI configuration to depend on EFI but not just IA64. It
> > checks efi_enabled flag and efi-rtc driver should enabled.
> >
> > Cc: Matt Fleming <matt.fleming@intel.com>
> > Cc: H. Peter Anvin <hpa@zytor.com>
> > Cc: Matthew Garrett <matthew.garrett@nebula.com>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Ingo Molnar <mingo@redhat.com>
> > Cc: Jan Beulich <JBeulich@suse.com>
> > Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
> > ---
> > arch/x86/platform/efi/efi.c | 17 +++++++++++++++++
> > drivers/rtc/Kconfig | 2 +-
> > 2 files changed, 18 insertions(+), 1 deletions(-)
>
> This patch needs to be justified. Enabling the EFI runtime *Time
> functions just because they're available isn't good enough. We need to
> know why this patch improves things, what use case does it solve?
>
The main purpose of enable efi-rtc driver is providing interface to
userspace access timezone field in EFI time services.
Currently have two BIOS interfaces provided the timezone field accessing
ability, ACPI TAD and EFI. I hope can enable them on x86_64 machines.
> The general attitude has been that we want to invoke the runtime
> services less, not more, due to the huge variety of runtime
> implementation bugs.
>
I agreed, but userspace application should not be too often to access
RTC. Maybe only when system boot and set timezone.
Thanks a lot!
Joey Lee
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 44+ messages in thread
* [RFC PATCH 07/14] rtc-efi: add GMTOFF support to rtc_efi
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (4 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 06/14] rtc-efi: register rtc-efi device when EFI enabled Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
[not found] ` <1387439515-8926-8-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
2013-12-19 7:51 ` [RFC PATCH 08/14] rtc-efi: set uie_unsupported for indicate rtc-efi doesn't support UIE mode Lee, Chun-Yi
` (7 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
Per UEFI 2.3.1 spec, we can use SetTime() to store the timezone value to
BIOS and get it back by GetTime(). It's good for installation system to
gain the default timezone setting from BIOS that was set by
manufacturer.
This patch adds 2 new iotrl: RTC_RD_GMTOFF and RTC_SET_GMTOFF to rtc_efi
support get/set gmt offset that mapping to the GUN's tm_gmtoff extension
(Seconds east of UTC).
Due the timezone definition of UEFI is "Localtime = UTC - TimeZone",
rtc_efi driver will transfer the format between GNU and EFI.
The logic of timezone only affect on x86 architecture and keep the
original EFI_UNSPECIFIED_TIMEZONE value on IA64.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
drivers/rtc/rtc-efi.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 99 insertions(+), 1 deletions(-)
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index c4c3843..e0e3c7e 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -75,7 +75,10 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
eft->second = wtime->tm_sec;
eft->nanosecond = 0;
eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+#ifdef CONFIG_IA64
+ /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+#endif
}
static void
@@ -108,6 +111,84 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
}
}
+static int efi_read_gmtoff(struct device *dev, long int *arg)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+ s16 timezone;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ timezone = (s16)le16_to_cpu(eft.timezone);
+ *arg = EFI_UNSPECIFIED_TIMEZONE * 60;
+ if (abs(timezone) != EFI_UNSPECIFIED_TIMEZONE &&
+ abs(timezone) <= 1440)
+ *arg = timezone * 60 * -1;
+
+ return 0;
+}
+
+static int efi_set_gmtoff(struct device *dev, long int arg)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+ s16 timezone;
+
+ /* transfer seconds east of UTC to minutes for ACPI */
+ timezone = arg / 60 * -1;
+ if (abs(timezone) > 1440 &&
+ abs(timezone) != EFI_UNSPECIFIED_TIMEZONE)
+ return -EINVAL;
+
+ /* can not use -2047 */
+ if (timezone == EFI_UNSPECIFIED_TIMEZONE * -1)
+ timezone = EFI_UNSPECIFIED_TIMEZONE;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ eft.timezone = (s16)cpu_to_le16(timezone);
+ status = efi.set_time(&eft);
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't set timezone\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int efi_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ long int gmtoff;
+ int err;
+
+ switch (cmd) {
+ case RTC_RD_GMTOFF:
+ err = efi_read_gmtoff(dev, &gmtoff);
+ if (err)
+ return err;
+ return put_user(gmtoff, (unsigned long __user *)arg);
+ case RTC_SET_GMTOFF:
+ return efi_set_gmtoff(dev, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
efi_time_t eft;
@@ -172,6 +253,17 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
{
efi_status_t status;
efi_time_t eft;
+#ifdef CONFIG_X86
+ efi_time_cap_t cap;
+
+ /* read time for grab timezone to avoid overwrite it */
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efitime: can't read time\n");
+ return -EINVAL;
+ }
+#endif
convert_to_efi_time(tm, &eft);
@@ -181,13 +273,16 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
}
static const struct rtc_class_ops efi_rtc_ops = {
+#ifdef CONFIG_X86
+ .ioctl = efi_rtc_ioctl,
+#endif
.read_time = efi_read_time,
.set_time = efi_set_time,
.read_alarm = efi_read_alarm,
.set_alarm = efi_set_alarm,
};
-static int __init efi_rtc_probe(struct platform_device *dev)
+static int efi_rtc_probe(struct platform_device *dev)
{
struct rtc_device *rtc;
@@ -196,6 +291,8 @@ static int __init efi_rtc_probe(struct platform_device *dev)
if (IS_ERR(rtc))
return PTR_ERR(rtc);
+ rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);
+
platform_set_drvdata(dev, rtc);
return 0;
@@ -213,3 +310,4 @@ module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
MODULE_AUTHOR("dann frazier <dannf@hp.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EFI RTC driver");
+MODULE_ALIAS("platform:rtc-efi");
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 08/14] rtc-efi: set uie_unsupported for indicate rtc-efi doesn't support UIE mode
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (5 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 07/14] rtc-efi: add GMTOFF support to rtc_efi Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 09/14] efi: move functions of access efi time to header file for sharing Lee, Chun-Yi
` (6 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
This patch set uie_unsupported flag when driver probed because current
rtc-efi driver doesn't support UIE mode. Otherwise RTC_UIE_ON ioctl doesn't
return EINVAL and it causes userspace think the RTC_UIE supported by rtc-efi.
Set uie_unsupported then We can enable CONFIG_RTC_INTF_DEV_UIE_EMUL to
emulate RTC_UIE on rtc-efi.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
drivers/rtc/rtc-efi.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index e0e3c7e..693ea47 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -293,6 +293,8 @@ static int efi_rtc_probe(struct platform_device *dev)
rtc->caps = (RTC_TZ_CAP | RTC_DST_CAP);
+ rtc->uie_unsupported = 1;
+
platform_set_drvdata(dev, rtc);
return 0;
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 09/14] efi: move functions of access efi time to header file for sharing
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (6 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 08/14] rtc-efi: set uie_unsupported for indicate rtc-efi doesn't support UIE mode Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 10/14] rtc: improve and move week day computing function to rtc header Lee, Chun-Yi
` (5 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
There have some functions, e.g. compute_yday, compute_wday, convert efi
time... are duplicated in efirtc, rtc-efi and will also used in rtc.c.
So this patch moved those functions of access efi time to efi.h header
file for sharing.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
drivers/char/efirtc.c | 98 ------------------------------------
drivers/rtc/rtc-efi.c | 133 ++----------------------------------------------
include/linux/efi.h | 134 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 140 insertions(+), 225 deletions(-)
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index e39e740..8a30a02 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -41,109 +41,11 @@
#define EFI_RTC_VERSION "0.4"
-#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
-/*
- * EFI Epoch is 1/1/1998
- */
-#define EFI_RTC_EPOCH 1998
-
static DEFINE_SPINLOCK(efi_rtc_lock);
static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
-#define is_leap(year) \
- ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
-static const unsigned short int __mon_yday[2][13] =
-{
- /* Normal years. */
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- /* Leap years. */
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-};
-
-/*
- * returns day of the year [0-365]
- */
-static inline int
-compute_yday(efi_time_t *eft)
-{
- /* efi_time_t.month is in the [1-12] so, we need -1 */
- return __mon_yday[is_leap(eft->year)][eft->month-1]+ eft->day -1;
-}
-/*
- * returns day of the week [0-6] 0=Sunday
- *
- * Don't try to provide a year that's before 1998, please !
- */
-static int
-compute_wday(efi_time_t *eft)
-{
- int y;
- int ndays = 0;
-
- if ( eft->year < 1998 ) {
- printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
- return -1;
- }
-
- for(y=EFI_RTC_EPOCH; y < eft->year; y++ ) {
- ndays += 365 + (is_leap(y) ? 1 : 0);
- }
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
-}
-
-static void
-convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
-{
-
- eft->year = wtime->tm_year + 1900;
- eft->month = wtime->tm_mon + 1;
- eft->day = wtime->tm_mday;
- eft->hour = wtime->tm_hour;
- eft->minute = wtime->tm_min;
- eft->second = wtime->tm_sec;
- eft->nanosecond = 0;
- eft->daylight = wtime->tm_isdst ? EFI_ISDST: 0;
- eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
-}
-
-static void
-convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
-{
- memset(wtime, 0, sizeof(*wtime));
- wtime->tm_sec = eft->second;
- wtime->tm_min = eft->minute;
- wtime->tm_hour = eft->hour;
- wtime->tm_mday = eft->day;
- wtime->tm_mon = eft->month - 1;
- wtime->tm_year = eft->year - 1900;
-
- /* day of the week [0-6], Sunday=0 */
- wtime->tm_wday = compute_wday(eft);
-
- /* day in the year [1-365]*/
- wtime->tm_yday = compute_yday(eft);
-
-
- switch (eft->daylight & EFI_ISDST) {
- case EFI_ISDST:
- wtime->tm_isdst = 1;
- break;
- case EFI_TIME_ADJUST_DAYLIGHT:
- wtime->tm_isdst = 0;
- break;
- default:
- wtime->tm_isdst = -1;
- }
-}
-
static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index 693ea47..4687022 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -22,95 +22,6 @@
#include <linux/rtc.h>
#include <linux/efi.h>
-#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
-/*
- * EFI Epoch is 1/1/1998
- */
-#define EFI_RTC_EPOCH 1998
-
-/*
- * returns day of the year [0-365]
- */
-static inline int
-compute_yday(efi_time_t *eft)
-{
- /* efi_time_t.month is in the [1-12] so, we need -1 */
- return rtc_year_days(eft->day, eft->month - 1, eft->year);
-}
-/*
- * returns day of the week [0-6] 0=Sunday
- *
- * Don't try to provide a year that's before 1998, please !
- */
-static int
-compute_wday(efi_time_t *eft)
-{
- int y;
- int ndays = 0;
-
- if (eft->year < 1998) {
- pr_err("EFI year < 1998, invalid date\n");
- return -1;
- }
-
- for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
-}
-
-static void
-convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
-{
- eft->year = wtime->tm_year + 1900;
- eft->month = wtime->tm_mon + 1;
- eft->day = wtime->tm_mday;
- eft->hour = wtime->tm_hour;
- eft->minute = wtime->tm_min;
- eft->second = wtime->tm_sec;
- eft->nanosecond = 0;
- eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
-#ifdef CONFIG_IA64
- /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
- eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
-#endif
-}
-
-static void
-convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
-{
- memset(wtime, 0, sizeof(*wtime));
- wtime->tm_sec = eft->second;
- wtime->tm_min = eft->minute;
- wtime->tm_hour = eft->hour;
- wtime->tm_mday = eft->day;
- wtime->tm_mon = eft->month - 1;
- wtime->tm_year = eft->year - 1900;
-
- /* day of the week [0-6], Sunday=0 */
- wtime->tm_wday = compute_wday(eft);
-
- /* day in the year [1-365]*/
- wtime->tm_yday = compute_yday(eft);
-
-
- switch (eft->daylight & EFI_ISDST) {
- case EFI_ISDST:
- wtime->tm_isdst = 1;
- break;
- case EFI_TIME_ADJUST_DAYLIGHT:
- wtime->tm_isdst = 0;
- break;
- default:
- wtime->tm_isdst = -1;
- }
-}
-
static int efi_read_gmtoff(struct device *dev, long int *arg)
{
efi_status_t status;
@@ -230,54 +141,22 @@ static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
return status == EFI_SUCCESS ? 0 : -EINVAL;
}
-static int efi_read_time(struct device *dev, struct rtc_time *tm)
+static int efi_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- efi_status_t status;
- efi_time_t eft;
- efi_time_cap_t cap;
-
- status = efi.get_time(&eft, &cap);
-
- if (status != EFI_SUCCESS) {
- /* should never happen */
- dev_err(dev, "can't read time\n");
- return -EINVAL;
- }
-
- convert_from_efi_time(&eft, tm);
-
- return rtc_valid_tm(tm);
+ return efi_read_time(tm);
}
-static int efi_set_time(struct device *dev, struct rtc_time *tm)
+static int efi_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- efi_status_t status;
- efi_time_t eft;
-#ifdef CONFIG_X86
- efi_time_cap_t cap;
-
- /* read time for grab timezone to avoid overwrite it */
- status = efi.get_time(&eft, &cap);
-
- if (status != EFI_SUCCESS) {
- pr_err("efitime: can't read time\n");
- return -EINVAL;
- }
-#endif
-
- convert_to_efi_time(tm, &eft);
-
- status = efi.set_time(&eft);
-
- return status == EFI_SUCCESS ? 0 : -EINVAL;
+ return efi_set_time(tm);
}
static const struct rtc_class_ops efi_rtc_ops = {
#ifdef CONFIG_X86
.ioctl = efi_rtc_ioctl,
#endif
- .read_time = efi_read_time,
- .set_time = efi_set_time,
+ .read_time = efi_rtc_read_time,
+ .set_time = efi_rtc_set_time,
.read_alarm = efi_read_alarm,
.set_alarm = efi_set_alarm,
};
diff --git a/include/linux/efi.h b/include/linux/efi.h
index fc8fa0e..3859f3e 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -132,6 +132,12 @@ typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg);
#define EFI_TIME_ADJUST_DAYLIGHT 0x1
#define EFI_TIME_IN_DAYLIGHT 0x2
#define EFI_UNSPECIFIED_TIMEZONE 0x07ff
+#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
+
+/*
+ * EFI Epoch is 1/1/1998
+ */
+#define EFI_RTC_EPOCH 1998
typedef struct {
u16 year;
@@ -154,6 +160,90 @@ typedef struct {
} efi_time_cap_t;
/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(efi_time_t *eft)
+{
+ /* efi_time_t.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(eft->day, eft->month - 1, eft->year);
+}
+
+/*
+ * returns day of the week [0-6] 0=Sunday
+ *
+ * Don't try to provide a year that's before 1998, please !
+ */
+static inline int
+compute_wday(efi_time_t *eft)
+{
+ int y;
+ int ndays = 0;
+
+ if (eft->year < 1998) {
+ pr_err("EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(eft);
+
+ /*
+ * 4=1/1/1998 was a Thursday
+ */
+ return (ndays + 4) % 7;
+}
+
+static inline void
+convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
+{
+ memset(wtime, 0, sizeof(*wtime));
+ wtime->tm_sec = eft->second;
+ wtime->tm_min = eft->minute;
+ wtime->tm_hour = eft->hour;
+ wtime->tm_mday = eft->day;
+ wtime->tm_mon = eft->month - 1;
+ wtime->tm_year = eft->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ wtime->tm_wday = compute_wday(eft);
+
+ /* day in the year [1-365]*/
+ wtime->tm_yday = compute_yday(eft);
+
+ switch (eft->daylight & EFI_ISDST) {
+ case EFI_ISDST:
+ wtime->tm_isdst = 1;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ wtime->tm_isdst = 0;
+ break;
+ default:
+ wtime->tm_isdst = -1;
+ }
+}
+
+static inline void
+convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
+{
+ eft->year = wtime->tm_year + 1900;
+ eft->month = wtime->tm_mon + 1;
+ eft->day = wtime->tm_mday;
+ eft->hour = wtime->tm_hour;
+ eft->minute = wtime->tm_min;
+ eft->second = wtime->tm_sec;
+ eft->nanosecond = 0;
+ eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+#ifdef CONFIG_IA64
+ /* avoid overwrite timezone on non-IA64 platform. e.g. x86_64 */
+ eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+#endif
+}
+
+
+/*
* EFI Boot Services table
*/
typedef struct {
@@ -585,6 +675,50 @@ efi_guid_unparse(efi_guid_t *guid, char *out)
return out;
}
+static inline int
+efi_set_time(struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+#ifdef CONFIG_X86
+ efi_time_cap_t cap;
+
+ /* read time for grab timezone to avoid overwrite it */
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ pr_err("efi: can't read time\n");
+ return -EINVAL;
+ }
+#endif
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static inline int
+efi_read_time(struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efi: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
extern void efi_init (void);
extern void *efi_get_pal_addr (void);
extern void efi_map_pal_code (void);
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 10/14] rtc: improve and move week day computing function to rtc header
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (7 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 09/14] efi: move functions of access efi time to header file for sharing Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 11/14] rtc: switch to get/set rtc time to efi functions if CMOS RTC Not Present git set Lee, Chun-Yi
` (4 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
Due to rtc-acpid and efi time used the same logic for computing
week day, so this patch moves code to rtc.h header file.
Additionally using a leap year algorithm to replace the for-loop
block in compute_wday for improve the performance. The first
version of algorithm is from Oliver Neukum.
---
drivers/rtc/rtc-acpitad.c | 13 +------------
include/linux/efi.h | 13 +------------
include/linux/rtc.h | 38 ++++++++++++++++++++++++++++++++++++++
3 files changed, 40 insertions(+), 24 deletions(-)
diff --git a/drivers/rtc/rtc-acpitad.c b/drivers/rtc/rtc-acpitad.c
index 065a033..bdf7ae1 100644
--- a/drivers/rtc/rtc-acpitad.c
+++ b/drivers/rtc/rtc-acpitad.c
@@ -32,23 +32,12 @@ compute_yday(struct acpi_time *acpit)
static int
compute_wday(struct acpi_time *acpit)
{
- int y;
- int ndays = 0;
-
if (acpit->year < 1900) {
pr_err("ACPI year < 1900, invalid date\n");
return -1;
}
- for (y = 1900; y < acpit->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(acpit);
-
- /*
- * 1=1/1/1900 was a Monday
- */
- return (ndays + 1) % 7;
+ return rtc_wday(acpit->day, acpit->month - 1, acpit->year);
}
static void
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 3859f3e..1c78ae7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -177,23 +177,12 @@ compute_yday(efi_time_t *eft)
static inline int
compute_wday(efi_time_t *eft)
{
- int y;
- int ndays = 0;
-
if (eft->year < 1998) {
pr_err("EFI year < 1998, invalid date\n");
return -1;
}
- for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (is_leap_year(y) ? 1 : 0);
-
- ndays += compute_yday(eft);
-
- /*
- * 4=1/1/1998 was a Thursday
- */
- return (ndays + 4) % 7;
+ return rtc_wday(eft->day, eft->month - 1, eft->year);
}
static inline void
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index e6380ec..511884f 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -196,6 +196,44 @@ static inline bool is_leap_year(unsigned int year)
return (!(year % 4) && (year % 100)) || !(year % 400);
}
+#define SINCE1900 25 /* valid from 2000 */
+
+static inline int rtc_wday(unsigned int day, unsigned int month, unsigned int year)
+{
+ int ndays, correction;
+ int base;
+
+ if (year < 1900) {
+ pr_err("rtc: year < 1900, invalid date\n");
+ return -1;
+ }
+
+ if (year >= 2000)
+ base = year - 2000;
+ else
+ base = year - 1900;
+
+ correction = 0;
+ if (base >= 0) {
+ correction += base / 4;
+ correction -= base / 100;
+ correction += base / 400;
+ if (year >= 2000)
+ correction += SINCE1900;
+
+ /* later rtc_year_days will add the leap day of current year */
+ correction -= ((is_leap_year(year)) ? 1 : 0);
+ }
+
+ ndays = (year - 1900) * 365 + correction;
+ ndays += rtc_year_days(day, month, year);
+
+ /*
+ * 1=1/1/1900 was a Monday
+ */
+ return (ndays + 1) % 7;
+}
+
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
extern int rtc_hctosys_ret;
#else
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 11/14] rtc: switch to get/set rtc time to efi functions if CMOS RTC Not Present git set.
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (8 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 10/14] rtc: improve and move week day computing function to rtc header Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 12/14] efi: adjust system time base on timezone from EFI time services Lee, Chun-Yi
` (3 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
When CMOS RTC Not Present git set in FADT, system should not access CMOS
interface for time. This patch move get/set rtc time function from CMOS
to EFI runtime on x86_64 machine. And, it also set the BUG_ON check in
rtc_cmos_read/write function to avoid access it.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
arch/x86/kernel/rtc.c | 8 ++++++++
include/asm-generic/rtc.h | 22 ++++++++++++++++++++++
2 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index 9b6c568..a54cd09 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -117,6 +117,10 @@ unsigned char rtc_cmos_read(unsigned char addr)
{
unsigned char val;
+ BUG_ON(acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC &&
+ addr <= RTC_YEAR);
+
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
val = inb(RTC_PORT(1));
@@ -128,6 +132,10 @@ EXPORT_SYMBOL(rtc_cmos_read);
void rtc_cmos_write(unsigned char val, unsigned char addr)
{
+ BUG_ON(acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC &&
+ addr <= RTC_YEAR);
+
lock_cmos_prefix(addr);
outb(addr, RTC_PORT(0));
outb(val, RTC_PORT(1));
diff --git a/include/asm-generic/rtc.h b/include/asm-generic/rtc.h
index fa86f24..4ba8aa6 100644
--- a/include/asm-generic/rtc.h
+++ b/include/asm-generic/rtc.h
@@ -16,6 +16,8 @@
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/delay.h>
+#include <linux/efi.h>
+#include <linux/acpi.h>
#define RTC_PIE 0x40 /* periodic interrupt enable */
#define RTC_AIE 0x20 /* alarm interrupt enable */
@@ -51,6 +53,16 @@ static inline unsigned int __get_rtc_time(struct rtc_time *time)
unsigned int real_year;
#endif
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+#if defined(CONFIG_EFI) && defined(CONFIG_X86_64)
+ printk_once(KERN_INFO "efi: get rtc time by EFI\n");
+ return efi_read_time(time);
+#else
+ BUG();
+#endif
+ }
+
/*
* read RTC once any update in progress is done. The update
* can take just over 2ms. We wait 20ms. There is no need to
@@ -123,6 +135,16 @@ static inline int __set_rtc_time(struct rtc_time *time)
unsigned int real_yrs, leap_yr;
#endif
+ if (acpi_gbl_FADT.header.revision >= 5 &&
+ acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_CMOS_RTC) {
+#if defined(CONFIG_EFI) && defined(CONFIG_X86_64)
+ printk_once(KERN_INFO "efi: set rtc time by EFI\n");
+ return efi_set_time(time);
+#else
+ BUG();
+#endif
+ }
+
yrs = time->tm_year;
mon = time->tm_mon + 1; /* tm_mon starts at zero */
day = time->tm_mday;
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 12/14] efi: adjust system time base on timezone from EFI time services
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (9 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 11/14] rtc: switch to get/set rtc time to efi functions if CMOS RTC Not Present git set Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [RFC PATCH 13/14] Documentation/RTC: add document of ACPI TAD and EFI TIME driver Lee, Chun-Yi
` (2 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
EFI time services provide the interface to store timezone to BIOS. The
timezone value from EFI indicates the offset of RTC time in minutes from
UTC.
The formula is: Localtime = UTC - TimeZone.
This patch add a efI_warp_clock() function to initial process for adjust
system time base on timezone value from EFI time services. It will also
set persistent_clock_is_local global variable to avoid user space
adjust timezone again.
This efi warp clock mechanism will triggered on x86_64 EFI machine when
timezone value is neither 0 nor 2047(UNSPECIFIED), kernel assume the
value of RTC is local time. On the other hand, system just follow
the old logic when timezone value from EFI is 0 or 2047, kernel assume
the value of RTC is UTC time.
About the 2047(EFI_UNSPECIFIED_TIMEZONE) value, it's the default value
of UEFI BIOS if there didn't have software set it through EFI interface.
We can _NOT_ follow EFI spec to interpret the RTC time as a local time
if timezone value is EFI_UNSPECIFIED_TIMEZONE, that's because Linux stored
UTC to BIOS on shipped UEFI machines.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
arch/x86/platform/efi/efi.c | 37 +++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 2 ++
init/main.c | 5 +++++
kernel/time.c | 2 +-
4 files changed, 45 insertions(+), 1 deletions(-)
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 42d6052..848160e 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -306,6 +306,43 @@ static void efi_get_time(struct timespec *now)
now->tv_nsec = 0;
}
+static int efi_read_timezone(s16 *timezone)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ pr_err("efitime: can't read timezone.\n");
+ return -EINVAL;
+ }
+
+ *timezone = (s16)le16_to_cpu(eft.timezone);
+ return 0;
+}
+
+void __init efi_warp_clock(void)
+{
+ s16 timezone;
+
+ if (!efi_read_timezone(&timezone)) {
+ /* TimeZone value, 2047 or 0 means UTC */
+ if (timezone != 0 && timezone != 2047) {
+ struct timespec adjust;
+
+ persistent_clock_is_local = 1;
+ adjust.tv_sec = timezone * 60;
+ adjust.tv_nsec = 0;
+ timekeeping_inject_offset(&adjust);
+ pr_info("RTC timezone is %d mins behind of UTC.\n", timezone);
+ pr_info("Adjusted system time to UTC.\n");
+ }
+ }
+}
+
/*
* Tell the kernel about the EFI memory map. This might include
* more than the max 128 entries that can fit in the e820 legacy
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 1c78ae7..a8d4f5c 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -715,10 +715,12 @@ extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg);
extern void efi_gettimeofday (struct timespec *ts);
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
#ifdef CONFIG_X86
+extern void efi_warp_clock(void);
extern void efi_late_init(void);
extern void efi_free_boot_services(void);
extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size);
#else
+static inline void efi_warp_clock(void) {}
static inline void efi_late_init(void) {}
static inline void efi_free_boot_services(void) {}
diff --git a/init/main.c b/init/main.c
index 61164ce..9effb1c 100644
--- a/init/main.c
+++ b/init/main.c
@@ -570,6 +570,11 @@ asmlinkage void __init start_kernel(void)
hrtimers_init();
softirq_init();
timekeeping_init();
+#ifdef CONFIG_X86_64
+ /* adjust system time by timezone */
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ efi_warp_clock();
+#endif
time_init();
sched_clock_postinit();
perf_event_init();
diff --git a/kernel/time.c b/kernel/time.c
index 7c7964c..ce18bac 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -176,7 +176,7 @@ int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz)
update_vsyscall_tz();
if (firsttime) {
firsttime = 0;
- if (!tv)
+ if (!tv && !persistent_clock_is_local)
warp_clock();
}
}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [RFC PATCH 13/14] Documentation/RTC: add document of ACPI TAD and EFI TIME driver
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (10 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 12/14] efi: adjust system time base on timezone from EFI time services Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
2013-12-19 7:51 ` [TEST PATCH 14/14] acpi: add early parameter to set CMOS RTC Not Present bit for testing Lee, Chun-Yi
[not found] ` <1387448416-11672-1-git-send-email-jlee@suse.com>
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
This patch add rtc-tz.txt document to explain the RTC driver of
ACPI TAD, EFI TIME. It focus on the timezone field and CMOS RTC Not
Present bit of ACPI 5.0.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
Documentation/rtc-tz.txt | 510 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 510 insertions(+), 0 deletions(-)
create mode 100644 Documentation/rtc-tz.txt
diff --git a/Documentation/rtc-tz.txt b/Documentation/rtc-tz.txt
new file mode 100644
index 0000000..7dfe523
--- /dev/null
+++ b/Documentation/rtc-tz.txt
@@ -0,0 +1,510 @@
+
+ ACPI TAD and EFI TIME Drivers for Timezone and Daylight
+ =======================================================
+
+In ACPI 5.0 ACPI TAD (Time and Alram Device) and EFI, they defined
+Timezone and Daylight field in time struct:
+
+ * ACPI TAD ... INT16 TimeZone
+ -1440 to 1440 or 2047 (unspecified)
+ Time zone field is the number of minutes that
+ the local time lags behind the UTC time.
+ Follow this equation: Localtime = UTC - TimeZone
+ UINT8 Daylight
+ Daylight is a bitmask containing the daylight
+ savings time information for the time:
+ Bit0: 1 = the time is affected by daylight savings time,
+ 0= time is not affected by daylight savings.
+ Bit1: 1= the time has been adjusted for daylight savings time,
+ 0= the time hasn't been adjusted for daylight savings.
+
+ * EFI TIME ... INT16 TimeZone
+ -1440 to 1440 or 2047 (unspecified)
+ Time zone field is the number of minutes that
+ the local time lags behind the UTC time.
+ Follow this equation: Localtime = UTC - TimeZone
+ UINT8 Daylight
+ EFI_TIME_ADJUST_DAYLIGHT 0x01
+ indicates if the time is affected by daylight
+ savings time or not.
+ EFI_TIME_IN_DAYLIGHT 0x02
+ is set, the time has been adjusted for daylight
+ savings time.
+
+Then, the Timzone and daylight definition in GNU struct tm:
+
+ * GNU struct tm ... long int __tm_gmtoff; /* Seconds east of UTC. */
+ int tm_isdst; /* DST. [-1/0/1]*/
+
+For Timzone, the definition match between ACPI TAD and EFI TIME. But it
+different with GNU time struct. GNU's tm_gmtoff is "Seconds east of UTC".
+Simply say, the timzone of ACPI/EFI and GNU are sign difference.
+
+Example 1:
+ 'Asia/Taipei' UTC +8
+ GNU: tm_gmtoff = 28800 seconds.
+ Taiwan is east of UTC, so it's positive number.
+ ACPI or EFI: TimeZone = -480 hours
+ Timezone = UTC - Localtime = UTC - Taipei time
+ So, it's negative number.
+Example 2:
+ 'Americ/Los_Angeles' UTC -8
+ GNU: tm_gmtoff = -28800 seconds.
+ Los Angeles is west of UTC, so it's negative number.
+ ACPI or EFI: TimeZone = 480 hours
+ Timezone = UTC - Localtime = UTC - Taipei time
+ So, it's positive number.
+
+For Daylight, due to GNU's tm_isdst is only define: '1' means current
+time is in daylight savings. '0' means not. '-1' means non-available.
+So, 1b is the only value from ACPI/EFI will transfer to '1' in GNU.
+When ACPI or EFI value is:
+
+ * 11b: Time is affected by daylight and has been adjusted for daylight
+ tm->tm_isdst = 1
+ * 01b: Time is affected by daylight but hasn't been adjusted for daylight
+ tm->tm_isdst = 0
+ * 00b(or 10b): Time is affected by daylight
+ tm->tm_isdst = -1
+
+The above data tranfer work of Timezone and Daylight will handled by RTC driver
+to ACPI TAD and EFI TIME: rtc-acpitad and rtc-efi.
+
+SYSFS INTERFACE
+---------------
+
+The sysfs interface under /sys/class/rtc/rtcN provides access to various
+rtc attributes without requiring the use of ioctls. Here only have one new
+sysfs interface for grab the capabilities:
+
+caps: This interface will return a bitmap of capabilitites to the RTC
+ interface. Currently it indicates the RTC has capability for
+ handle Timezone or Daylight:
+ Bit0: Timezone. Set this bit means this interface has
+ capability to store Timezone
+ Bit1: Daylight: Set this bit means this interface has
+ capability to store Daylight savings time.
+ There already have the following definition in linux/rtc.h:
+ #define RTC_TZ_CAP (1 << 0)
+ #define RTC_DST_CAP (1 << 1)
+
+IOCTL INTERFACE
+---------------
+
+Here create 3 new ioctl functions for read/set timezone and read the
+capabilities of RTC interface.
+
+ * RTC_RD_GMTOFF, RTC_SET_GMTOFF ... Used to read and set timezone value.
+ Due to support the GNU tm_gmtoff format, so the input/output value
+ is "Seconds east of UTC". RTC drivers, rtc-acpitad and rtc-efi, done
+ the data transfer work.
+ The rtc-acpitad and rtc-efi driver also avoid to overwrite the timezone
+ field in their time struct in RTC_SET_TIME.
+
+ * RTC_CAPS_READ ... As the caps sysfs interface, this ioctl provides
+ interface to userspace application for grab capabilities of current
+ RTC interface. Application can early check does this interface support
+ Timezone or Daylight.
+
+CMOS RTC Not Present flag
+-------------------------
+
+ACPI 5.0 spec defines a "CMOS RTC Not Present" flag in IA-PC Boot
+Architecture Flags of FADT. If this bit set, that means OSPM need uses the
+ACPI Time and Alarm device instead. Software should not access RTC through
+CMOS interface.
+
+In Linux kernel, the defaul wallclock functions deal with RTC by CMOS, it
+should move to ACPI TAD or EFI Time servcies. ACPI Time and Alarm device is
+described in DSDT that need wait until DSDT parsed by kernel in subsystem
+initial stage, so it can not used to deal with wallclock when system boot.
+
+Current solution of "CMOS RTC Not Present" is switch to EFI time services.
+On x86_64 EFI machine kernel will deal with RTC by EFI time services. In
+initial process, system time will adjusted base on timezone value from EFI
+time services, it also set persistent_clock_is_local global variable to
+avoid user space adjust system time by userland timezone again.
+
+The above efi warp clock mechanism will triggered on x86_64 EFI machine when
+timezone value is neither 0 nor 2047(UNSPECIFIED), kernel assume the value
+of RTC is local time. On the other hand, system just follow the old logic
+when timezone value from EFI is 0 or 2047, kernel assume the value of RTC is
+UTC time.
+
+About the 2047(EFI_UNSPECIFIED_TIMEZONE) value, it's the default value of
+UEFI BIOS if there didn't have any software set it through EFI interface.
+We can _NOT_ follow EFI spec to interpret the RTC time as a local time if
+timezone value is EFI_UNSPECIFIED_TIMEZONE, that's because Linux stored
+UTC to BIOS on shipped UEFI machines.
+
+
+-------------------- 8< ---------------- 8< -----------------------------
+
+/*
+ * Timezone of ACPI/EFI RTC Driver Test Program
+ *
+ * Compile with:
+ * gcc rtc-tz-test.c -o rtc-tz-test
+ *
+ * Copyright (C) 2013 SUSE Linux Products GmbH. All rights reserved.
+ * Written by Lee, Chun-Yi (jlee@suse.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/rtc.h>
+
+#ifndef RTC_RD_GMTOFF
+#define RTC_RD_GMTOFF _IOR('p', 0x13, long int) /* Read time zone return seconds east of UTC */
+#define RTC_SET_GMTOFF _IOW('p', 0x14, long int) /* Set time zone input seconds east of UTC */
+#endif
+#ifndef RTC_CAPS_READ
+#define RTC_CAPS_READ _IOR('p', 0x15, unsigned int) /* Get capabilities, e.g. TZ, DST */
+#endif
+/* Time Zone and Daylight capabilities */
+#ifndef RTC_TZ_CAP
+#define RTC_TZ_CAP (1 << 0)
+#define RTC_DST_CAP (1 << 1)
+#endif
+
+#define MAX_DEV 5
+#define ADJUST_MIN 60
+#define DEFAULT_TZ 28800 /* GMT offset of Taiwan R.O.C (Seconds east of UTC)*/
+#define ADJUST_TZ -28800 /* GMT offset of Los Angeles (Seconds east of UTC) */
+
+static const char dev_path[] = "/dev/rtc";
+static const char sys_path[] = "/sys/class/rtc/rtc";
+
+static const struct {
+ const char *driver_name;
+ const char *name;
+} names[] = {
+ {"rtc_cmos", "CMOS"},
+ {"rtc-efi", "EFI"},
+ {"rtc-acpitad", "ACPI-TAD"},
+};
+
+struct rtc_dev {
+ char dev_path[10];
+ char sys_path[21];
+ char name[15];
+ char driver_name[15];
+ unsigned int caps;
+};
+
+struct rtc_dev rtc_devs[5];
+
+void search_rtc_dev(void)
+{
+ int i, j, fd, ret;
+ FILE *fin;
+
+ for (i = 0; i <= MAX_DEV; i++)
+ {
+ char path_tmp[30];
+ sprintf(path_tmp, "%s%d", dev_path, i);
+
+ fd = open(path_tmp, O_RDONLY);
+ if (fd != -1) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ memcpy(dev->dev_path, path_tmp, 10);
+ sprintf(dev->sys_path, "%s%d/", sys_path, i);
+
+ sprintf(path_tmp, "%s%s", dev->sys_path, "name");
+ if ((fin = fopen(path_tmp, "r")) != NULL)
+ fscanf(fin, "%s", dev->driver_name);
+ fclose(fin);
+
+ for (j = 0; j < sizeof(names)/sizeof(names[0]); j++) {
+ if (!strcmp(dev->driver_name, names[j].driver_name))
+ memcpy(dev->name, names[j].name, strlen(names[j].name));
+ }
+
+ ioctl(fd, RTC_CAPS_READ, &dev->caps);
+ close(fd);
+ }
+ }
+}
+
+void print_rtc_dev(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ printf("Name: %s(%s)\n", dev->name, dev->driver_name);
+ printf(" Device Path: %s\n", dev->dev_path);
+ printf(" Sysfs Path : %s\n", dev->sys_path);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ printf(" RTC date/time: %d-%d-%d %02d:%02d:%02d\n",
+ rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
+ rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+ printf(" Is Daylight: %d (%s)\n", rtc_tm.tm_isdst,
+ (rtc_tm.tm_isdst)? (rtc_tm.tm_isdst < 0)? "NOT AVAILABLE":"INEFFECT":"NOT IN EFFECT");
+
+ printf(" Capabilities: %d (%s %s)\n", dev->caps,
+ (dev->caps & RTC_TZ_CAP)? "TZ":"",
+ (dev->caps & RTC_DST_CAP)? "DST":"");
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1)
+ printf(" GMTOFF: not support\n");
+ else
+ printf(" GMTOFF: %ld TIMEZONE: %d\n", gmtoff, gmtoff / 60 * -1);
+
+ close(fd);
+ printf("\n");
+}
+
+void print_rtc_devs(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev(&rtc_devs[i]);
+ }
+}
+
+void print_rtc_dev_time(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ printf(" %s: %d-%d-%d %02d:%02d:%02d\n", dev->name,
+ rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
+ rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+
+ close(fd);
+}
+
+void print_rtc_devs_time(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev_time(&rtc_devs[i]);
+ }
+}
+
+void time_increase(struct rtc_dev *dev, int in_min)
+{
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ /* Increase minutes */
+ rtc_tm.tm_min += in_min;
+ if (rtc_tm.tm_min >= 60) {
+ rtc_tm.tm_hour += (rtc_tm.tm_min / 60);
+ rtc_tm.tm_min %= 60;
+ if (rtc_tm.tm_hour >= 24) {
+ rtc_tm.tm_mday += (rtc_tm.tm_hour / 24);
+ rtc_tm.tm_hour %= 24;
+ }
+ /* Yes, it's not perfect, only adjust to mday level for testing */
+ }
+
+ /* Set increased time */
+ ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_SET_TIME ioctl");
+
+ close(fd);
+}
+
+void time_decrease(struct rtc_dev *dev, int in_min)
+{
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the RTC time/date */
+ ret = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_RD_TIME ioctl");
+
+ /* Increase minutes */
+ if ((rtc_tm.tm_min - in_min) < 0) {
+ rtc_tm.tm_hour -= (in_min / 60);
+ rtc_tm.tm_min -= in_min % 60;
+ if (rtc_tm.tm_hour < 0) {
+ rtc_tm.tm_mday += (rtc_tm.tm_hour / 24);
+ rtc_tm.tm_hour = (rtc_tm.tm_hour % 24) * -1;
+ }
+ /* Yes, it's not perfect, only adjust to mday level for testing */
+ }
+
+ ret = ioctl(fd, RTC_SET_TIME, &rtc_tm);
+ if (ret == -1)
+ perror("RTC_SET_TIME ioctl");
+
+ close(fd);
+}
+
+void set_rtc_time_test(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ if(strlen(dev->sys_path) &&
+ dev->caps & RTC_TZ_CAP) {
+ printf("Test Target: %s(%s)\n", dev->name, dev->driver_name);
+
+ printf(" Before Increase\n");
+ print_rtc_devs_time();
+ time_increase(dev, ADJUST_MIN);
+ printf(" After Increased %d minutes\n", ADJUST_MIN);
+ print_rtc_devs_time();
+ printf(" Before Decrease\n");
+ print_rtc_devs_time();
+ time_decrease(dev, ADJUST_MIN);
+ printf(" After Decreased %d minutes\n", ADJUST_MIN);
+ print_rtc_devs_time();
+
+ printf("\n\n");
+ }
+ }
+}
+
+void print_rtc_dev_tz(struct rtc_dev *dev)
+{
+ long int gmtoff;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1)
+ printf(" %s(%s): not support\n", dev->name, dev->driver_name);
+ else
+ printf(" %s(%s): GMTOFF: %ld TIMEZONE: %d\n", dev->name, dev->driver_name, gmtoff, gmtoff / 60 * -1);
+
+ close(fd);
+}
+
+void print_rtc_devs_tz()
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ if(strlen(rtc_devs[i].sys_path))
+ print_rtc_dev_tz(&rtc_devs[i]);
+ }
+}
+
+long int change_gmtoff(struct rtc_dev *dev, long int gmtoff_in)
+{
+ long int gmtoff = 122820;
+ struct rtc_time rtc_tm;
+ int fd, ret;
+
+ fd = open(dev->dev_path, O_RDONLY);
+
+ /* Read the GMTOFF (Seconds east of UTC) */
+ ret = ioctl(fd, RTC_RD_GMTOFF, &gmtoff);
+ if (ret == -1) {
+ printf("RTC_RD_GMTOFF fail.\n");
+ goto read_err;
+ }
+
+ ret = ioctl(fd, RTC_SET_GMTOFF, gmtoff_in);
+ if (ret == -1)
+ printf("RTC_SET_GMTOFF fail.\n");
+
+read_err:
+ close(fd);
+
+ return gmtoff;
+}
+
+void access_gmtoff_test(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_DEV; i++) {
+ struct rtc_dev *dev = &rtc_devs[i];
+
+ if(strlen(dev->sys_path) &&
+ dev->caps & RTC_TZ_CAP) {
+ long int orig_tz;
+
+ printf("Test Target: %s(%s)\n", dev->name, dev->driver_name);
+ printf("Set to Default TZ: %ld\n", DEFAULT_TZ);
+ change_gmtoff(dev, DEFAULT_TZ);
+
+ printf(" Before Adjust TZ\n");
+ print_rtc_devs_tz();
+ orig_tz = change_gmtoff(dev, ADJUST_TZ);
+ printf(" After Adjusted TZ\n");
+ print_rtc_devs_tz();
+ orig_tz = change_gmtoff(dev, orig_tz);
+ printf(" Adjusted Back\n");
+ print_rtc_devs_tz();
+
+ printf("\n\n");
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ search_rtc_dev();
+ printf("\nThis testing program will access following ioctl interface:\n");
+ printf(" RTC_RD_TIME/RTC_SET_TIME: Used to read and set RTC value.\n");
+ printf(" RTC_RD_GMTOFF/RTC_SET_GMTOFF: Used to read and set timezone, input/output is \"Seconds east of UTC\".\n");
+ printf(" RTC_CAPS_READ: Read the Timzone and Daylight capabilities of RTC interface.\n");
+
+ printf("\n======== Read Time Testing (RTC_RD_TIME/RTC_CAPS_READ) ========\n\n");
+ print_rtc_devs();
+
+ printf("\n======== Set Time Testing (RTC_SET_TIME/RTC_RD_TIME) ========\n");
+ printf("Only testing the interface supported Timezone.\n");
+ printf("This testing will increase %d minutes of RTC time then decrease it back.\n\n", ADJUST_MIN);
+ set_rtc_time_test();
+
+ printf("\n======== Access TimeZone Testing (RTC_RD_GMTOFF/RTC_SET_GMTOFF) ========\n");
+ printf("Only testing the interface supported Timezone.\n");
+ printf("Timezone of ACPI and UEFI spec: Time zone field is the number of minutes that the local time lags behind the UTC time.\n");
+ printf(" -1440 to 1440 or 2047. Localtime = UTC - TimeZone\n");
+ printf("Timezone in GNU tm struct: Seconds east of UTC.\n");
+ printf("This testing will set time zone to Los Angeles time (-28800 Seconds east of UTC) then set it back.\n\n", ADJUST_MIN);
+ access_gmtoff_test();
+
+ return 0;
+}
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread* [TEST PATCH 14/14] acpi: add early parameter to set CMOS RTC Not Present bit for testing
2013-12-19 7:51 [RFC PATCH 00/14] Support timezone of ACPI TAD and EFI TIME Lee, Chun-Yi
` (11 preceding siblings ...)
2013-12-19 7:51 ` [RFC PATCH 13/14] Documentation/RTC: add document of ACPI TAD and EFI TIME driver Lee, Chun-Yi
@ 2013-12-19 7:51 ` Lee, Chun-Yi
[not found] ` <1387448416-11672-1-git-send-email-jlee@suse.com>
13 siblings, 0 replies; 44+ messages in thread
From: Lee, Chun-Yi @ 2013-12-19 7:51 UTC (permalink / raw)
To: Rafael J. Wysocki, Alessandro Zummo, H. Peter Anvin, Matt Fleming,
Matthew Garrett
Cc: Elliott, samer.el-haj-mahmoud, Oliver Neukum, werner, trenn,
JBeulich, linux-kernel, rtc-linux, x86, linux-efi@vger.kernel.org,
linux-acpi, Lee, Chun-Yi
This is a patch for testing that will not go to any git tree.
Add a early kernel parameter to set CMOS RTC Not Present bit in
acpi_gbl_FADT variable for testing. Use acpi_no_cmos_rtc=1 to enable
this bit to block rtc cmos interface.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 6c0b43b..64925dd 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -83,6 +83,8 @@ static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE;
#warning ACPI uses CMPXCHG, i486 and later hardware
#endif
+static bool acpi_no_cmos_rtc;
+
/* --------------------------------------------------------------------------
Boot-time Configuration
-------------------------------------------------------------------------- */
@@ -1531,6 +1533,13 @@ int __init acpi_boot_init(void)
*/
acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt);
+ /* Dummy NO_CMOS_RTC enable option to fake out RTC CMOS */
+ if (acpi_no_cmos_rtc) {
+ acpi_gbl_FADT.header.revision = 5;
+ acpi_gbl_FADT.boot_flags |= ACPI_FADT_NO_CMOS_RTC;
+ pr_info("acpi: Set NO_CMOS_RTC bit in FADT for testing\n");
+ }
+
/*
* Process the Multiple APIC Description Table (MADT), if present
*/
@@ -1673,3 +1682,11 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
e820_add_region(addr, size, E820_ACPI);
update_e820();
}
+
+/* Dummy NO_CMOS_RTC enable option to fake out RTC CMOS */
+static int __init setup_acpi_no_cmos_rtc(char *arg)
+{
+ acpi_no_cmos_rtc = true;
+ return 0;
+}
+early_param("acpi_no_cmos_rtc", setup_acpi_no_cmos_rtc);
--
1.6.4.2
^ permalink raw reply related [flat|nested] 44+ messages in thread[parent not found: <1387448416-11672-1-git-send-email-jlee@suse.com>]