diff mbox

[RFC,07/14] rtc-efi: add GMTOFF support to rtc_efi

Message ID 1387439515-8926-8-git-send-email-jlee@suse.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Chun-Yi Lee Dec. 19, 2013, 7:51 a.m. UTC
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(-)

Comments

Matthew Garrett Dec. 20, 2013, 3:11 p.m. UTC | #1
T24gVGh1LCAyMDEzLTEyLTE5IGF0IDE1OjUxICswODAwLCBMZWUsIENodW4tWWkgd3JvdGU6DQo+
IFRoaXMgcGF0Y2ggYWRkcyAyIG5ldyBpb3RybDogUlRDX1JEX0dNVE9GRiBhbmQgUlRDX1NFVF9H
TVRPRkYgdG8NCj4gcnRjX2VmaSBzdXBwb3J0IGdldC9zZXQgZ210IG9mZnNldCB0aGF0IG1hcHBp
bmcgdG8gdGhlIEdVTidzIHRtX2dtdG9mZg0KPiBleHRlbnNpb24gKFNlY29uZHMgZWFzdCBvZiBV
VEMpLg0KDQpTaG91bGRuJ3QgdGhpcyBpbmZvcm1hdGlvbiBhbHNvIGJlIGV4cG9ydGVkIHZpYSB0
aGUgcnRjLXN5c2ZzIGludGVyZmFjZT8NCg0KLS0gDQpNYXR0aGV3IEdhcnJldHQgPG1hdHRoZXcu
Z2FycmV0dEBuZWJ1bGEuY29tPg0K
--
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
joeyli Dec. 21, 2013, 3:56 a.m. UTC | #2
? ??2013-12-20 ? 15:11 +0000?Matthew Garrett ???
> On Thu, 2013-12-19 at 15:51 +0800, Lee, Chun-Yi wrote:
> > 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).
> 
> Shouldn't this information also be exported via the rtc-sysfs interface?
> 

Thanks for your suggestion, I will also export it to rtc-sysfs.

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
diff mbox

Patch

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");