linux-efi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Lee, Chun-Yi" <joeyli.kernel@gmail.com>
To: "Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Alessandro Zummo <a.zummo@towertech.it>,
	"H. Peter Anvin" <hpa@zytor.com>,
	Matt Fleming <matt@console-pimps.org>,
	Matthew Garrett <matthew.garrett@nebula.com>
Cc: Elliott@hp.com, samer.el-haj-mahmoud@hp.com,
	Oliver Neukum <oneukum@suse.de>,
	werner@suse.com, trenn@suse.de, JBeulich@suse.com,
	linux-kernel@vger.kernel.org, rtc-linux@googlegroups.com,
	x86@kernel.org,
	"linux-efi@vger.kernel.org" <linux-efi@vger.kernel.org>,
	linux-acpi@vger.kernel.org, "Lee, Chun-Yi" <jlee@suse.com>
Subject: [RFC PATCH 05/14] rtc: Add RTC driver of ACPI Time and Alarm Device
Date: Thu, 19 Dec 2013 15:51:46 +0800	[thread overview]
Message-ID: <1387439515-8926-6-git-send-email-jlee@suse.com> (raw)
In-Reply-To: <1387439515-8926-1-git-send-email-jlee@suse.com>

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

  parent reply	other threads:[~2013-12-19  7:51 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` [PATCH 03/14] rtc: block registration of rtc-cmos when CMOS RTC Not Present Lee, Chun-Yi
2013-12-19  7:51 ` [RFC PATCH 04/14] ACPI: Add ACPI 5.0 Time and Alarm Device driver Lee, Chun-Yi
     [not found]   ` <1387439515-8926-5-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
2013-12-19 15:22     ` H. Peter Anvin
     [not found]       ` <52B30F43.1060306-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
2013-12-20  5:41         ` joeyli
     [not found]           ` <1387518099.3539.4453.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
2014-01-01  0:42             ` H. Peter Anvin
     [not found]               ` <52C3647B.7000708-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
2014-01-06  8:58                 ` joeyli
2014-01-07  5:37                   ` H. Peter Anvin
2014-01-07 10:40                     ` joeyli
2014-01-07 16:35                       ` H. Peter Anvin
2014-01-08 14:59                         ` joeyli
     [not found]                           ` <1389193142.3539.6123.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
2014-01-08 17:56                             ` H. Peter Anvin
     [not found]                               ` <52CD9139.2070302-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
2014-01-09  3:47                                 ` joeyli
     [not found]                                   ` <1389239259.24105.2.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
2014-01-09  4:00                                     ` H. Peter Anvin
2014-01-09 12:16                                   ` Rafael J. Wysocki
2014-01-02  8:09     ` Lan Tianyu
2014-01-06  9:20       ` joeyli
2013-12-19  7:51 ` Lee, Chun-Yi [this message]
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
     [not found]       ` <1387513491.3539.4345.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
2013-12-20  4:30         ` H. Peter Anvin
2013-12-20 10:37           ` Borislav Petkov
     [not found]             ` <20131220103755.GA14784-fF5Pk5pvG8Y@public.gmane.org>
2013-12-20 15:14               ` Matthew Garrett
2013-12-20 21:04                 ` H. Peter Anvin
     [not found]                   ` <52B4B0ED.7030600-YMNOUZJC4hwAvxtiuMwx3w@public.gmane.org>
2013-12-21  1:24                     ` joeyli
2013-12-21  1:51                       ` H. Peter Anvin
2013-12-21 15:34                         ` joeyli
2013-12-20 16:48               ` H. Peter Anvin
2013-12-19  7:51 ` [RFC PATCH 07/14] rtc-efi: add GMTOFF support to rtc_efi Lee, Chun-Yi
     [not found]   ` <1387439515-8926-8-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
2013-12-20 15:11     ` Matthew Garrett
2013-12-21  3:56       ` joeyli
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 ` [RFC PATCH 09/14] efi: move functions of access efi time to header file for sharing 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
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 ` [RFC PATCH 12/14] efi: adjust system time base on timezone from EFI time services 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
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>
     [not found]   ` <1387448416-11672-1-git-send-email-jlee-IBi9RG/b67k@public.gmane.org>
2013-12-19 10:49     ` [PATCH 02/14] x86-64/efi: Use EFI to deal with platform wall clock (again) Matt Fleming
     [not found]       ` <20131219104908.GE3145-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-12-19 13:32         ` joeyli
     [not found]           ` <1387459939.3539.4092.camel-ONCj+Eqt86TasUa73XJKwA@public.gmane.org>
2013-12-20 11:29             ` One Thousand Gnomes
2013-12-23 23:25               ` H. Peter Anvin

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=1387439515-8926-6-git-send-email-jlee@suse.com \
    --to=joeyli.kernel@gmail.com \
    --cc=Elliott@hp.com \
    --cc=JBeulich@suse.com \
    --cc=a.zummo@towertech.it \
    --cc=hpa@zytor.com \
    --cc=jlee@suse.com \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-efi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matt@console-pimps.org \
    --cc=matthew.garrett@nebula.com \
    --cc=oneukum@suse.de \
    --cc=rjw@rjwysocki.net \
    --cc=rtc-linux@googlegroups.com \
    --cc=samer.el-haj-mahmoud@hp.com \
    --cc=trenn@suse.de \
    --cc=werner@suse.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).