2009-01-09 00:57:01

by dann frazier

[permalink] [raw]
Subject: [PATCH] add rtc platform driver for EFI

Munge Stephane Eranian's efirtc.c code into an rtc platform driver

Signed-off-by: dann frazier <[email protected]>
---
arch/ia64/kernel/time.c | 18 +++
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-efi.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 323 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-efi.c

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb34..9ed5ba0 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
#include <linux/efi.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/platform_device.h>

#include <asm/machvec.h>
#include <asm/delay.h>
@@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
.name = "timer"
};

+struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ int ret;
+ ret = platform_device_register(&rtc_efi_dev);
+ if (ret < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
+
void __init
time_init (void)
{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 165a818..37ce842 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -433,6 +433,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.

+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e79c91..29db401 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 0000000..2386da0
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,294 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <[email protected]>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.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
+
+#define is_leap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+struct efi_rtc {
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct efi_rtc *p = dev_get_drvdata(dev);
+ efi_time_t eft;
+ efi_status_t status;
+ unsigned long flags;
+
+ lock_kernel();
+
+ spin_lock_irqsave(&p->lock, flags);
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ unlock_kernel();
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return 0;
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct efi_rtc *p = dev_get_drvdata(dev);
+ efi_time_t eft;
+ efi_status_t status;
+ unsigned long flags;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ lock_kernel();
+
+ spin_lock_irqsave(&p->lock, flags);
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ unlock_kernel();
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct efi_rtc *p = dev_get_drvdata(dev);
+ unsigned long flags;
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ lock_kernel();
+
+ spin_lock_irqsave(&p->lock, flags);
+ status = efi.get_time(&eft, &cap);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ unlock_kernel();
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return 0;
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct efi_rtc *p = dev_get_drvdata(dev);
+ unsigned long flags;
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ lock_kernel();
+
+ spin_lock_irqsave(&p->lock, flags);
+ status = efi.set_time(&eft);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ unlock_kernel();
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .read_time = efi_read_time,
+ .set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
+};
+
+static int __devinit efi_rtc_probe(struct platform_device *dev)
+{
+ struct efi_rtc *p;
+
+ p = kzalloc(sizeof (*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ spin_lock_init(&p->lock);
+
+ p->rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(p->rtc)) {
+ int err = PTR_ERR(p->rtc);
+ kfree(p);
+ return err;
+ }
+
+ platform_set_drvdata(dev, p);
+
+ return 0;
+}
+
+static int __devexit efi_rtc_remove(struct platform_device *dev)
+{
+ struct efi_rtc *p = platform_get_drvdata(dev);
+
+ rtc_device_unregister(p->rtc);
+ kfree(p);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __devexit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_register(&efi_rtc_driver);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
--
1.5.6.5


2009-01-09 02:27:43

by Alessandro Zummo

[permalink] [raw]
Subject: Re: [rtc-linux] [PATCH] add rtc platform driver for EFI

On Thu, 8 Jan 2009 17:56:45 -0700
dann frazier <[email protected]> wrote:

a few comments below. please
also check http://groups.google.com/group/rtc-linux/web/checklist

> Munge Stephane Eranian's efirtc.c code into an rtc platform driver
>
> Signed-off-by: dann frazier <[email protected]>
> ---
> arch/ia64/kernel/time.c | 18 +++
> drivers/rtc/Kconfig | 10 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-efi.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 323 insertions(+), 0 deletions(-)
> create mode 100644 drivers/rtc/rtc-efi.c
>
> diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
> index f0ebb34..9ed5ba0 100644
> --- a/arch/ia64/kernel/time.c
> +++ b/arch/ia64/kernel/time.c
> @@ -20,6 +20,7 @@
> #include <linux/efi.h>
> #include <linux/timex.h>
> #include <linux/clocksource.h>
> +#include <linux/platform_device.h>
>
> #include <asm/machvec.h>
> #include <asm/delay.h>
> @@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
> .name = "timer"
> };
>
> +struct platform_device rtc_efi_dev = {
> + .name = "rtc-efi",
> + .id = -1,
> +};
> +
> +static int __init rtc_init(void)
> +{
> + int ret;
> + ret = platform_device_register(&rtc_efi_dev);
> + if (ret < 0)
> + printk(KERN_ERR "unable to register rtc device...\n");
> +
> + /* not necessarily an error */
> + return 0;
> +}
> +module_init(rtc_init);
> +
> void __init
> time_init (void)
> {
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 165a818..37ce842 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -433,6 +433,16 @@ config RTC_DRV_DS1742
> This driver can also be built as a module. If so, the module
> will be called rtc-ds1742.
>
> +config RTC_DRV_EFI
> + tristate "EFI RTC"
> + depends on IA64
> + help
> + If you say yes here you will get support for the EFI
> + Real Time Clock.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-efi.
> +
> config RTC_DRV_STK17TA8
> tristate "Simtek STK17TA8"
> depends on RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 6e79c91..29db401 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> new file mode 100644
> index 0000000..2386da0
> --- /dev/null
> +++ b/drivers/rtc/rtc-efi.c
> @@ -0,0 +1,294 @@
> +/*
> + * rtc-efi: RTC Class Driver for EFI-based systems
> + *
> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> + *
> + * Author: dann frazier <[email protected]>
> + * Based on efirtc.c by Stephane Eranian
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/platform_device.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
> +
> +#define is_leap(year) \
> + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
> +
> +struct efi_rtc {
> + struct rtc_device *rtc;
> + spinlock_t lock;
> +};
> +
> +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 }
> +};

please check drivers/rtc/rtc-lib.c

> +/*
> + * 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + struct efi_rtc *p = dev_get_drvdata(dev);
> + efi_time_t eft;
> + efi_status_t status;
> + unsigned long flags;
> +
> + lock_kernel();
> +
> + spin_lock_irqsave(&p->lock, flags);

you don't need your own lock, the rtc subsystem provides
ops locking.

> + /*
> + * As of EFI v1.10, this call always returns an unsupported status
> + */
> + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> + (efi_bool_t *)&wkalrm->pending, &eft);
> + spin_unlock_irqrestore(&p->lock, flags);
> +
> + unlock_kernel();
> +
> + if (status != EFI_SUCCESS)
> + return -EINVAL;
> +
> + convert_from_efi_time(&eft, &wkalrm->time);
> +
> + return 0;

use return rtc_valid_tm

> +}
> +
> +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + struct efi_rtc *p = dev_get_drvdata(dev);
> + efi_time_t eft;
> + efi_status_t status;
> + unsigned long flags;
> +
> + convert_to_efi_time(&wkalrm->time, &eft);
> +
> + lock_kernel();
> +
> + spin_lock_irqsave(&p->lock, flags);
> + /*
> + * XXX Fixme:
> + * As of EFI 0.92 with the firmware I have on my
> + * machine this call does not seem to work quite
> + * right
> + *
> + * As of v1.10, this call always returns an unsupported status
> + */
> + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
> + spin_unlock_irqrestore(&p->lock, flags);
> +
> + unlock_kernel();
> +
> + printk(KERN_WARNING "write status is %d\n", (int)status);
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static int efi_read_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct efi_rtc *p = dev_get_drvdata(dev);
> + unsigned long flags;
> + efi_status_t status;
> + efi_time_t eft;
> + efi_time_cap_t cap;
> +
> + lock_kernel();
> +
> + spin_lock_irqsave(&p->lock, flags);
> + status = efi.get_time(&eft, &cap);
> + spin_unlock_irqrestore(&p->lock, flags);
> +
> + unlock_kernel();
> +
> + if (status != EFI_SUCCESS) {
> + /* should never happen */
> + printk(KERN_ERR "efitime: can't read time\n");
> + return -EINVAL;
> + }
> +
> + convert_from_efi_time(&eft, tm);
> +
> + return 0;

use rtc_valid_tm

> +}
> +
> +static int efi_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + struct efi_rtc *p = dev_get_drvdata(dev);
> + unsigned long flags;
> + efi_status_t status;
> + efi_time_t eft;
> +
> + convert_to_efi_time(tm, &eft);
> +
> + lock_kernel();
> +
> + spin_lock_irqsave(&p->lock, flags);
> + status = efi.set_time(&eft);
> + spin_unlock_irqrestore(&p->lock, flags);
> +
> + unlock_kernel();
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static const struct rtc_class_ops efi_rtc_ops = {
> + .read_time = efi_read_time,
> + .set_time = efi_set_time,
> + .read_alarm = efi_read_alarm,
> + .set_alarm = efi_set_alarm,
> +};
> +
> +static int __devinit efi_rtc_probe(struct platform_device *dev)
> +{
> + struct efi_rtc *p;
> +
> + p = kzalloc(sizeof (*p), GFP_KERNEL);
> + if (!p)
> + return -ENOMEM;
> +
> + spin_lock_init(&p->lock);
> +
> + p->rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
> + THIS_MODULE);
> + if (IS_ERR(p->rtc)) {
> + int err = PTR_ERR(p->rtc);
> + kfree(p);
> + return err;
> + }
> +
> + platform_set_drvdata(dev, p);
> +
> + return 0;
> +}
> +
> +static int __devexit efi_rtc_remove(struct platform_device *dev)
> +{
> + struct efi_rtc *p = platform_get_drvdata(dev);
> +
> + rtc_device_unregister(p->rtc);
> + kfree(p);
> +
> + return 0;
> +}
> +
> +static struct platform_driver efi_rtc_driver = {
> + .driver = {
> + .name = "rtc-efi",
> + .owner = THIS_MODULE,
> + },
> + .probe = efi_rtc_probe,
> + .remove = __devexit_p(efi_rtc_remove),
> +};
> +
> +static int __init efi_rtc_init(void)
> +{
> + return platform_driver_register(&efi_rtc_driver);

you can probably use platform_driver_probe

> +}
> +
> +static void __exit efi_rtc_exit(void)
> +{
> + platform_driver_unregister(&efi_rtc_driver);
> +}
> +
> +module_init(efi_rtc_init);
> +module_exit(efi_rtc_exit);
> +
> +MODULE_AUTHOR("dann frazier <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("EFI RTC driver");
> --
> 1.5.6.5
>
>
> --~--~---------~--~----~------------~-------~--~----~
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
> -~----------~----~----~----~------~----~------~--~---
>


--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

2009-01-09 21:34:48

by dann frazier

[permalink] [raw]
Subject: Re: [rtc-linux] [PATCH] add rtc platform driver for EFI

On Fri, Jan 09, 2009 at 03:27:29AM +0100, Alessandro Zummo wrote:
> On Thu, 8 Jan 2009 17:56:45 -0700
> dann frazier <[email protected]> wrote:
>
> a few comments below. please
> also check http://groups.google.com/group/rtc-linux/web/checklist

Thanks for the pointer. I _think_ I comply, after fixing the
individual issues you brought up below.

One question is with:
"do not kfree() what's returned by rtc_device_register()"

I do kfree the return if it comes back as an error - is that still
correct?

> > Munge Stephane Eranian's efirtc.c code into an rtc platform driver
> >
> > Signed-off-by: dann frazier <[email protected]>
> > ---
> > arch/ia64/kernel/time.c | 18 +++
> > drivers/rtc/Kconfig | 10 ++
> > drivers/rtc/Makefile | 1 +
> > drivers/rtc/rtc-efi.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 323 insertions(+), 0 deletions(-)
> > create mode 100644 drivers/rtc/rtc-efi.c
> >
> > diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
> > index f0ebb34..9ed5ba0 100644
> > --- a/arch/ia64/kernel/time.c
> > +++ b/arch/ia64/kernel/time.c
> > @@ -20,6 +20,7 @@
> > #include <linux/efi.h>
> > #include <linux/timex.h>
> > #include <linux/clocksource.h>
> > +#include <linux/platform_device.h>
> >
> > #include <asm/machvec.h>
> > #include <asm/delay.h>
> > @@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
> > .name = "timer"
> > };
> >
> > +struct platform_device rtc_efi_dev = {
> > + .name = "rtc-efi",
> > + .id = -1,
> > +};
> > +
> > +static int __init rtc_init(void)
> > +{
> > + int ret;
> > + ret = platform_device_register(&rtc_efi_dev);
> > + if (ret < 0)
> > + printk(KERN_ERR "unable to register rtc device...\n");
> > +
> > + /* not necessarily an error */
> > + return 0;
> > +}
> > +module_init(rtc_init);
> > +
> > void __init
> > time_init (void)
> > {
> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index 165a818..37ce842 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -433,6 +433,16 @@ config RTC_DRV_DS1742
> > This driver can also be built as a module. If so, the module
> > will be called rtc-ds1742.
> >
> > +config RTC_DRV_EFI
> > + tristate "EFI RTC"
> > + depends on IA64
> > + help
> > + If you say yes here you will get support for the EFI
> > + Real Time Clock.
> > +
> > + This driver can also be built as a module. If so, the module
> > + will be called rtc-efi.
> > +
> > config RTC_DRV_STK17TA8
> > tristate "Simtek STK17TA8"
> > depends on RTC_CLASS
> > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> > index 6e79c91..29db401 100644
> > --- a/drivers/rtc/Makefile
> > +++ b/drivers/rtc/Makefile
> > @@ -34,6 +34,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> > obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> > obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> > obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> > +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> > obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> > obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> > obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> > diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> > new file mode 100644
> > index 0000000..2386da0
> > --- /dev/null
> > +++ b/drivers/rtc/rtc-efi.c
> > @@ -0,0 +1,294 @@
> > +/*
> > + * rtc-efi: RTC Class Driver for EFI-based systems
> > + *
> > + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> > + *
> > + * Author: dann frazier <[email protected]>
> > + * Based on efirtc.c by Stephane Eranian
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or (at your
> > + * option) any later version.
> > + *
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/time.h>
> > +#include <linux/platform_device.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
> > +
> > +#define is_leap(year) \
> > + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
> > +
> > +struct efi_rtc {
> > + struct rtc_device *rtc;
> > + spinlock_t lock;
> > +};
> > +
> > +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 }
> > +};
>
> please check drivers/rtc/rtc-lib.c

ah nice, fixed.

> > +/*
> > + * 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> > +{
> > + struct efi_rtc *p = dev_get_drvdata(dev);
> > + efi_time_t eft;
> > + efi_status_t status;
> > + unsigned long flags;
> > +
> > + lock_kernel();
> > +
> > + spin_lock_irqsave(&p->lock, flags);
>
> you don't need your own lock, the rtc subsystem provides
> ops locking.

cool, removed.

> > + /*
> > + * As of EFI v1.10, this call always returns an unsupported status
> > + */
> > + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> > + (efi_bool_t *)&wkalrm->pending, &eft);
> > + spin_unlock_irqrestore(&p->lock, flags);
> > +
> > + unlock_kernel();
> > +
> > + if (status != EFI_SUCCESS)
> > + return -EINVAL;
> > +
> > + convert_from_efi_time(&eft, &wkalrm->time);
> > +
> > + return 0;
>
> use return rtc_valid_tm

fixed

> > +}
> > +
> > +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> > +{
> > + struct efi_rtc *p = dev_get_drvdata(dev);
> > + efi_time_t eft;
> > + efi_status_t status;
> > + unsigned long flags;
> > +
> > + convert_to_efi_time(&wkalrm->time, &eft);
> > +
> > + lock_kernel();
> > +
> > + spin_lock_irqsave(&p->lock, flags);
> > + /*
> > + * XXX Fixme:
> > + * As of EFI 0.92 with the firmware I have on my
> > + * machine this call does not seem to work quite
> > + * right
> > + *
> > + * As of v1.10, this call always returns an unsupported status
> > + */
> > + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
> > + spin_unlock_irqrestore(&p->lock, flags);
> > +
> > + unlock_kernel();
> > +
> > + printk(KERN_WARNING "write status is %d\n", (int)status);
> > +
> > + return status == EFI_SUCCESS ? 0 : -EINVAL;
> > +}
> > +
> > +static int efi_read_time(struct device *dev, struct rtc_time *tm)
> > +{
> > + struct efi_rtc *p = dev_get_drvdata(dev);
> > + unsigned long flags;
> > + efi_status_t status;
> > + efi_time_t eft;
> > + efi_time_cap_t cap;
> > +
> > + lock_kernel();
> > +
> > + spin_lock_irqsave(&p->lock, flags);
> > + status = efi.get_time(&eft, &cap);
> > + spin_unlock_irqrestore(&p->lock, flags);
> > +
> > + unlock_kernel();
> > +
> > + if (status != EFI_SUCCESS) {
> > + /* should never happen */
> > + printk(KERN_ERR "efitime: can't read time\n");
> > + return -EINVAL;
> > + }
> > +
> > + convert_from_efi_time(&eft, tm);
> > +
> > + return 0;
>
> use rtc_valid_tm

fixed

> > +}
> > +
> > +static int efi_set_time(struct device *dev, struct rtc_time *tm)
> > +{
> > + struct efi_rtc *p = dev_get_drvdata(dev);
> > + unsigned long flags;
> > + efi_status_t status;
> > + efi_time_t eft;
> > +
> > + convert_to_efi_time(tm, &eft);
> > +
> > + lock_kernel();
> > +
> > + spin_lock_irqsave(&p->lock, flags);
> > + status = efi.set_time(&eft);
> > + spin_unlock_irqrestore(&p->lock, flags);
> > +
> > + unlock_kernel();
> > +
> > + return status == EFI_SUCCESS ? 0 : -EINVAL;
> > +}
> > +
> > +static const struct rtc_class_ops efi_rtc_ops = {
> > + .read_time = efi_read_time,
> > + .set_time = efi_set_time,
> > + .read_alarm = efi_read_alarm,
> > + .set_alarm = efi_set_alarm,
> > +};
> > +
> > +static int __devinit efi_rtc_probe(struct platform_device *dev)
> > +{
> > + struct efi_rtc *p;
> > +
> > + p = kzalloc(sizeof (*p), GFP_KERNEL);
> > + if (!p)
> > + return -ENOMEM;
> > +
> > + spin_lock_init(&p->lock);
> > +
> > + p->rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
> > + THIS_MODULE);
> > + if (IS_ERR(p->rtc)) {
> > + int err = PTR_ERR(p->rtc);
> > + kfree(p);
> > + return err;
> > + }
> > +
> > + platform_set_drvdata(dev, p);
> > +
> > + return 0;
> > +}
> > +
> > +static int __devexit efi_rtc_remove(struct platform_device *dev)
> > +{
> > + struct efi_rtc *p = platform_get_drvdata(dev);
> > +
> > + rtc_device_unregister(p->rtc);
> > + kfree(p);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver efi_rtc_driver = {
> > + .driver = {
> > + .name = "rtc-efi",
> > + .owner = THIS_MODULE,
> > + },
> > + .probe = efi_rtc_probe,
> > + .remove = __devexit_p(efi_rtc_remove),
> > +};
> > +
> > +static int __init efi_rtc_init(void)
> > +{
> > + return platform_driver_register(&efi_rtc_driver);
>
> you can probably use platform_driver_probe

done

> > +}
> > +
> > +static void __exit efi_rtc_exit(void)
> > +{
> > + platform_driver_unregister(&efi_rtc_driver);
> > +}
> > +
> > +module_init(efi_rtc_init);
> > +module_exit(efi_rtc_exit);
> > +
> > +MODULE_AUTHOR("dann frazier <[email protected]>");
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("EFI RTC driver");

Will follow up w/ new patch.

--
dann frazier

2009-01-09 21:37:00

by dann frazier

[permalink] [raw]
Subject: [PATCH] add rtc platform driver for EFI

Munge Stephane Eranian's efirtc.c code into an rtc platform driver

Signed-off-by: dann frazier <[email protected]>
---
arch/ia64/kernel/time.c | 18 +++
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-efi.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 304 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-efi.c

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb34..9ed5ba0 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
#include <linux/efi.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/platform_device.h>

#include <asm/machvec.h>
#include <asm/delay.h>
@@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
.name = "timer"
};

+struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ int ret;
+ ret = platform_device_register(&rtc_efi_dev);
+ if (ret < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
+
void __init
time_init (void)
{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4ad831d..2cc8a9d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -434,6 +434,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.

+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 9a4340d..09ae207 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 0000000..1e9607b
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,275 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <[email protected]>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.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
+
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+struct efi_rtc {
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+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 rtc_year_days(eft->day - 1, 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) {
+ printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (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;
+ 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ lock_kernel();
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ unlock_kernel();
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ lock_kernel();
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ unlock_kernel();
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_read_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ lock_kernel();
+
+ status = efi.get_time(&eft, &cap);
+
+ unlock_kernel();
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ lock_kernel();
+
+ status = efi.set_time(&eft);
+
+ unlock_kernel();
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .read_time = efi_read_time,
+ .set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
+};
+
+static int __devinit efi_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ int err = PTR_ERR(rtc);
+ kfree(rtc);
+ return err;
+ }
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __devexit efi_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __devexit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
--
1.5.3.4

2009-01-13 08:40:38

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Fri, 9 Jan 2009 14:36:16 -0700 dann frazier <[email protected]> wrote:

> Munge Stephane Eranian's efirtc.c code into an rtc platform driver
>
>
> ...
>
> index f0ebb34..9ed5ba0 100644
> --- a/arch/ia64/kernel/time.c
> +++ b/arch/ia64/kernel/time.c
> @@ -20,6 +20,7 @@
> #include <linux/efi.h>
> #include <linux/timex.h>
> #include <linux/clocksource.h>
> +#include <linux/platform_device.h>
>
> #include <asm/machvec.h>
> #include <asm/delay.h>
> @@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
> .name = "timer"
> };
>
> +struct platform_device rtc_efi_dev = {

static, please.

> + .name = "rtc-efi",
> + .id = -1,
> +};
> +
> +static int __init rtc_init(void)
> +{
> + int ret;
> + ret = platform_device_register(&rtc_efi_dev);
> + if (ret < 0)
> + printk(KERN_ERR "unable to register rtc device...\n");

we don't really need `ret'...

> + /* not necessarily an error */
> + return 0;
> +}
> +module_init(rtc_init);
> +
> void __init
> time_init (void)
> {
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 4ad831d..2cc8a9d 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -434,6 +434,16 @@ config RTC_DRV_DS1742
> This driver can also be built as a module. If so, the module
> will be called rtc-ds1742.
>
> +config RTC_DRV_EFI
> + tristate "EFI RTC"
> + depends on IA64
> + help
> + If you say yes here you will get support for the EFI
> + Real Time Clock.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-efi.
> +
> config RTC_DRV_STK17TA8
> tristate "Simtek STK17TA8"
> depends on RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 9a4340d..09ae207 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> new file mode 100644
> index 0000000..1e9607b
> --- /dev/null
> +++ b/drivers/rtc/rtc-efi.c
> @@ -0,0 +1,275 @@
> +/*
> + * rtc-efi: RTC Class Driver for EFI-based systems
> + *
> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> + *
> + * Author: dann frazier <[email protected]>
> + * Based on efirtc.c by Stephane Eranian
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/platform_device.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
> +
> +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
> +
> +struct efi_rtc {
> + struct rtc_device *rtc;
> + spinlock_t lock;
> +};

This never gets used?

> +static const unsigned short int __mon_yday[2][13] =
> +{

static const unsigned short __mon_yday[2][13] = {

please.

> + /* 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 }
> +};

hm, I bet there are other places in the kernel where we use (or need)
that table.

> +
> +/*
> + * 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 - 1, 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) {
> + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
> + return -1;
> + }
> +
> + for (y = EFI_RTC_EPOCH; y < eft->year; y++)
> + ndays += 365 + (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)
> +{
> +

dd

> + 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + efi_time_t eft;
> + efi_status_t status;
> +
> + lock_kernel();
> +
> + /*
> + * As of EFI v1.10, this call always returns an unsupported status
> + */
> + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> + (efi_bool_t *)&wkalrm->pending, &eft);
> +
> + unlock_kernel();

eek. The patch adds a great pile of lock_kernel() calls. Everyone
else is busily deleting them.

Can we not do this?

> + if (status != EFI_SUCCESS)
> + return -EINVAL;
> +
> + convert_from_efi_time(&eft, &wkalrm->time);
> +
> + return rtc_valid_tm(&wkalrm->time);
> +}
> +
>
> ...
>

2009-01-14 00:01:15

by dann frazier

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Tue, Jan 13, 2009 at 12:39:34AM -0800, Andrew Morton wrote:
> On Fri, 9 Jan 2009 14:36:16 -0700 dann frazier <[email protected]> wrote:
>
> > Munge Stephane Eranian's efirtc.c code into an rtc platform driver
> >
> >
> > ...
> >
> > index f0ebb34..9ed5ba0 100644
> > --- a/arch/ia64/kernel/time.c
> > +++ b/arch/ia64/kernel/time.c
> > @@ -20,6 +20,7 @@
> > #include <linux/efi.h>
> > #include <linux/timex.h>
> > #include <linux/clocksource.h>
> > +#include <linux/platform_device.h>
> >
> > #include <asm/machvec.h>
> > #include <asm/delay.h>
> > @@ -405,6 +406,23 @@ static struct irqaction timer_irqaction = {
> > .name = "timer"
> > };
> >
> > +struct platform_device rtc_efi_dev = {
>
> static, please.

fixed

> > + .name = "rtc-efi",
> > + .id = -1,
> > +};
> > +
> > +static int __init rtc_init(void)
> > +{
> > + int ret;
> > + ret = platform_device_register(&rtc_efi_dev);
> > + if (ret < 0)
> > + printk(KERN_ERR "unable to register rtc device...\n");
>
> we don't really need `ret'...

removed

> > + /* not necessarily an error */
> > + return 0;
> > +}
> > +module_init(rtc_init);
> > +
> > void __init
> > time_init (void)
> > {
> > diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> > index 4ad831d..2cc8a9d 100644
> > --- a/drivers/rtc/Kconfig
> > +++ b/drivers/rtc/Kconfig
> > @@ -434,6 +434,16 @@ config RTC_DRV_DS1742
> > This driver can also be built as a module. If so, the module
> > will be called rtc-ds1742.
> >
> > +config RTC_DRV_EFI
> > + tristate "EFI RTC"
> > + depends on IA64
> > + help
> > + If you say yes here you will get support for the EFI
> > + Real Time Clock.
> > +
> > + This driver can also be built as a module. If so, the module
> > + will be called rtc-efi.
> > +
> > config RTC_DRV_STK17TA8
> > tristate "Simtek STK17TA8"
> > depends on RTC_CLASS
> > diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> > index 9a4340d..09ae207 100644
> > --- a/drivers/rtc/Makefile
> > +++ b/drivers/rtc/Makefile
> > @@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> > obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> > obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> > obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> > +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> > obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> > obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> > obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> > diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> > new file mode 100644
> > index 0000000..1e9607b
> > --- /dev/null
> > +++ b/drivers/rtc/rtc-efi.c
> > @@ -0,0 +1,275 @@
> > +/*
> > + * rtc-efi: RTC Class Driver for EFI-based systems
> > + *
> > + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> > + *
> > + * Author: dann frazier <[email protected]>
> > + * Based on efirtc.c by Stephane Eranian
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms of the GNU General Public License as published by the
> > + * Free Software Foundation; either version 2 of the License, or (at your
> > + * option) any later version.
> > + *
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/time.h>
> > +#include <linux/platform_device.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
> > +
> > +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
> > +
> > +struct efi_rtc {
> > + struct rtc_device *rtc;
> > + spinlock_t lock;
> > +};
>
> This never gets used?

oops - yeah, removed its usage based on Alessandro's feedback but
forgot to remove the def. removed now.

> > +static const unsigned short int __mon_yday[2][13] =
> > +{
>
> static const unsigned short __mon_yday[2][13] = {
>
> please.
>
> > + /* 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 }
> > +};
>
> hm, I bet there are other places in the kernel where we use (or need)
> that table.

yes, i'm using the one in rtclib now, but forgot to remove the local
def

> > +
> > +/*
> > + * 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 - 1, 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) {
> > + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
> > + return -1;
> > + }
> > +
> > + for (y = EFI_RTC_EPOCH; y < eft->year; y++)
> > + ndays += 365 + (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)
> > +{
> > +
>
> dd

just had a vi person explain to me what that means :) done.

> > + 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> > +{
> > + efi_time_t eft;
> > + efi_status_t status;
> > +
> > + lock_kernel();
> > +
> > + /*
> > + * As of EFI v1.10, this call always returns an unsupported status
> > + */
> > + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> > + (efi_bool_t *)&wkalrm->pending, &eft);
> > +
> > + unlock_kernel();
>
> eek. The patch adds a great pile of lock_kernel() calls. Everyone
> else is busily deleting them.
>
> Can we not do this?

I copied their usage from the efirtc driver, into which it was
recently pushed down with a request for an EFI expert to clear them
out as necessary. I'm not an EFI expert, but I took a look at the
spec, and section 7.1 of the UEFI spec does say that certain EFI
runtime call sets are not reentrant. I would expect to see that
locking handled internally in the efi layer, but I'm not seeing it.

Since the RTC layer is serializing these calls for us, and we appear
to be the only callers of these functions[1], it looks like it would
be safe to drop the BKL and not introduce other locking. Though, it
may make sense to add locking internally to each of the efi runtime
calls to comform to the spec, just in case this changes.

> > + if (status != EFI_SUCCESS)
> > + return -EINVAL;
> > +
> > + convert_from_efi_time(&eft, &wkalrm->time);
> > +
> > + return rtc_valid_tm(&wkalrm->time);
> > +}
> > +
> >
> > ...
> >
>

[1] efirtc calls them too, of course, but it is Kconfig'd off when
RTC_CLASS is on

--
dann frazier

2009-01-14 01:17:46

by dann frazier

[permalink] [raw]
Subject: [PATCH] add rtc platform driver for EFI

Munge Stephane Eranian's efirtc.c code into an rtc platform driver

Signed-off-by: dann frazier <[email protected]>
---
arch/ia64/kernel/time.c | 16 +++
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-efi.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 272 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-efi.c

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb34..558051c 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
#include <linux/efi.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/platform_device.h>

#include <asm/machvec.h>
#include <asm/delay.h>
@@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
.name = "timer"
};

+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
+
void __init
time_init (void)
{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index cced4d1..56cc98f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -434,6 +434,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.

+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e28021..7256ce8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 0000000..ddf0459
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,245 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <[email protected]>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.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
+
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+/*
+ * 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 - 1, 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) {
+ printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (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;
+ 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_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 */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .read_time = efi_read_time,
+ .set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
+};
+
+static int __devinit efi_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ int err = PTR_ERR(rtc);
+ kfree(rtc);
+ return err;
+ }
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __devexit efi_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __devexit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
--
1.5.6.5

2009-01-14 01:31:19

by Alessandro Zummo

[permalink] [raw]
Subject: Re: [rtc-linux] [PATCH] add rtc platform driver for EFI

On Tue, 13 Jan 2009 18:17:19 -0700
dann frazier <[email protected]> wrote:

Hi Dann,

thanks for doing the changes that Andrew and I requested. There are
still a few errors:

> Munge Stephane Eranian's efirtc.c code into an rtc platform driver
>
> Signed-off-by: dann frazier <[email protected]>
> ---
> arch/ia64/kernel/time.c | 16 +++
> drivers/rtc/Kconfig | 10 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-efi.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 272 insertions(+), 0 deletions(-)
> create mode 100644 drivers/rtc/rtc-efi.c
>
> diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
> index f0ebb34..558051c 100644
> --- a/arch/ia64/kernel/time.c
> +++ b/arch/ia64/kernel/time.c
> @@ -20,6 +20,7 @@
> #include <linux/efi.h>
> #include <linux/timex.h>
> #include <linux/clocksource.h>
> +#include <linux/platform_device.h>
>
> #include <asm/machvec.h>
> #include <asm/delay.h>
> @@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
> .name = "timer"
> };
>
> +static struct platform_device rtc_efi_dev = {
> + .name = "rtc-efi",
> + .id = -1,
> +};
> +
> +static int __init rtc_init(void)
> +{
> + if (platform_device_register(&rtc_efi_dev) < 0)
> + printk(KERN_ERR "unable to register rtc device...\n");
> +
> + /* not necessarily an error */
> + return 0;
> +}
> +module_init(rtc_init);
> +
> void __init
> time_init (void)
> {
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index cced4d1..56cc98f 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -434,6 +434,16 @@ config RTC_DRV_DS1742
> This driver can also be built as a module. If so, the module
> will be called rtc-ds1742.
>
> +config RTC_DRV_EFI
> + tristate "EFI RTC"
> + depends on IA64
> + help
> + If you say yes here you will get support for the EFI
> + Real Time Clock.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-efi.
> +
> config RTC_DRV_STK17TA8
> tristate "Simtek STK17TA8"
> depends on RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 6e28021..7256ce8 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> new file mode 100644
> index 0000000..ddf0459
> --- /dev/null
> +++ b/drivers/rtc/rtc-efi.c
> @@ -0,0 +1,245 @@
> +/*
> + * rtc-efi: RTC Class Driver for EFI-based systems
> + *
> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> + *
> + * Author: dann frazier <[email protected]>
> + * Based on efirtc.c by Stephane Eranian
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/platform_device.h>

where's 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
> +
> +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
> +
> +/*
> + * 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 - 1, 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) {
> + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
> + return -1;
> + }
> +
> + for (y = EFI_RTC_EPOCH; y < eft->year; y++)
> + ndays += 365 + (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;
> + 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + efi_time_t eft;
> + efi_status_t status;
> +
> + /*
> + * As of EFI v1.10, this call always returns an unsupported status
> + */
> + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> + (efi_bool_t *)&wkalrm->pending, &eft);
> +
> + if (status != EFI_SUCCESS)
> + return -EINVAL;
> +
> + convert_from_efi_time(&eft, &wkalrm->time);
> +
> + return rtc_valid_tm(&wkalrm->time);
> +}
> +
> +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + efi_time_t eft;
> + efi_status_t status;
> +
> + convert_to_efi_time(&wkalrm->time, &eft);
> +
> + /*
> + * XXX Fixme:
> + * As of EFI 0.92 with the firmware I have on my
> + * machine this call does not seem to work quite
> + * right
> + *
> + * As of v1.10, this call always returns an unsupported status
> + */
> + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
> +
> + printk(KERN_WARNING "write status is %d\n", (int)status);
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static int efi_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 */
> + printk(KERN_ERR "efitime: can't read time\n");
> + return -EINVAL;
> + }
> +
> + convert_from_efi_time(&eft, tm);
> +
> + return rtc_valid_tm(tm);
> +}
> +
> +static int efi_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + efi_status_t status;
> + efi_time_t eft;
> +
> + convert_to_efi_time(tm, &eft);
> +
> + status = efi.set_time(&eft);
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static const struct rtc_class_ops efi_rtc_ops = {
> + .read_time = efi_read_time,
> + .set_time = efi_set_time,
> + .read_alarm = efi_read_alarm,
> + .set_alarm = efi_set_alarm,
> +};
> +
> +static int __devinit efi_rtc_probe(struct platform_device *dev)

__init

> +{
> + struct rtc_device *rtc;
> +
> + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
> + if (!rtc)
> + return -ENOMEM;

you are allocating memory

> + rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
> + THIS_MODULE);

and then overwriting it. you don't need kzalloc/kree. the rtc subsystem
will give you back a valid pointer which will be freed
when unregistering.

> + if (IS_ERR(rtc)) {
> + int err = PTR_ERR(rtc);
> + kfree(rtc);
> + return err;
> + }
> +
> + platform_set_drvdata(dev, rtc);
> +
> + return 0;
> +}
> +
> +static int __devexit efi_rtc_remove(struct platform_device *dev)

__exit , you are using platform_driver_probe

> +{
> + struct rtc_device *rtc = platform_get_drvdata(dev);
> +
> + rtc_device_unregister(rtc);
> + kfree(rtc);

ditto.

> +
> + return 0;
> +}
> +
> +static struct platform_driver efi_rtc_driver = {
> + .driver = {
> + .name = "rtc-efi",
> + .owner = THIS_MODULE,
> + },
> + .probe = efi_rtc_probe,
> + .remove = __devexit_p(efi_rtc_remove),

__exit_p

> +};
> +
> +static int __init efi_rtc_init(void)
> +{
> + return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
> +}
> +
> +static void __exit efi_rtc_exit(void)
> +{
> + platform_driver_unregister(&efi_rtc_driver);
> +}
> +
> +module_init(efi_rtc_init);
> +module_exit(efi_rtc_exit);
> +
> +MODULE_AUTHOR("dann frazier <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("EFI RTC driver");
> --
> 1.5.6.5
>
>
> --~--~---------~--~----~------------~-------~--~----~
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
> -~----------~----~----~----~------~----~------~--~---
>


--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

2009-01-14 04:14:19

by dann frazier

[permalink] [raw]
Subject: Re: [rtc-linux] [PATCH] add rtc platform driver for EFI

On Wed, Jan 14, 2009 at 02:30:55AM +0100, Alessandro Zummo wrote:
> On Tue, 13 Jan 2009 18:17:19 -0700
> dann frazier <[email protected]> wrote:
>
> Hi Dann,
>
> thanks for doing the changes that Andrew and I requested. There are
> still a few errors:

Great, thanks again! Updated patch will follow.

--
dann frazier

2009-01-14 04:33:53

by dann frazier

[permalink] [raw]
Subject: [PATCH] add rtc platform driver for EFI

Munge Stephane Eranian's efirtc.c code into an rtc platform driver

Signed-off-by: dann frazier <[email protected]>
---
arch/ia64/kernel/time.c | 16 +++
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-efi.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 267 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-efi.c

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb34..558051c 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
#include <linux/efi.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/platform_device.h>

#include <asm/machvec.h>
#include <asm/delay.h>
@@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
.name = "timer"
};

+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
+
void __init
time_init (void)
{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index cced4d1..56cc98f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -434,6 +434,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.

+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e28021..7256ce8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 0000000..2c98c35
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,240 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <[email protected]>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#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
+
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+/*
+ * 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 - 1, 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) {
+ printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (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;
+ 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_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 */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .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)
+{
+ struct rtc_device *rtc;
+
+ rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ int err = PTR_ERR(rtc);
+ return err;
+ }
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __exit efi_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+ kfree(rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __exit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
--
1.5.6.5

2009-01-14 10:57:46

by Alessandro Zummo

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Tue, 13 Jan 2009 21:33:38 -0700
dann frazier <[email protected]> wrote:

> Munge Stephane Eranian's efirtc.c code into an rtc platform driver

almost there :) see below

> Signed-off-by: dann frazier <[email protected]>
> ---
> arch/ia64/kernel/time.c | 16 +++
> drivers/rtc/Kconfig | 10 ++
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-efi.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 267 insertions(+), 0 deletions(-)
> create mode 100644 drivers/rtc/rtc-efi.c
>
> diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
> index f0ebb34..558051c 100644
> --- a/arch/ia64/kernel/time.c
> +++ b/arch/ia64/kernel/time.c
> @@ -20,6 +20,7 @@
> #include <linux/efi.h>
> #include <linux/timex.h>
> #include <linux/clocksource.h>
> +#include <linux/platform_device.h>
>
> #include <asm/machvec.h>
> #include <asm/delay.h>
> @@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
> .name = "timer"
> };
>
> +static struct platform_device rtc_efi_dev = {
> + .name = "rtc-efi",
> + .id = -1,
> +};
> +
> +static int __init rtc_init(void)
> +{
> + if (platform_device_register(&rtc_efi_dev) < 0)
> + printk(KERN_ERR "unable to register rtc device...\n");
> +
> + /* not necessarily an error */
> + return 0;
> +}
> +module_init(rtc_init);
> +
> void __init
> time_init (void)
> {
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index cced4d1..56cc98f 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -434,6 +434,16 @@ config RTC_DRV_DS1742
> This driver can also be built as a module. If so, the module
> will be called rtc-ds1742.
>
> +config RTC_DRV_EFI
> + tristate "EFI RTC"
> + depends on IA64
> + help
> + If you say yes here you will get support for the EFI
> + Real Time Clock.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-efi.
> +
> config RTC_DRV_STK17TA8
> tristate "Simtek STK17TA8"
> depends on RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 6e28021..7256ce8 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
> obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
> obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
> obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
> +obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
> obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
> obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
> obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
> diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
> new file mode 100644
> index 0000000..2c98c35
> --- /dev/null
> +++ b/drivers/rtc/rtc-efi.c
> @@ -0,0 +1,240 @@
> +/*
> + * rtc-efi: RTC Class Driver for EFI-based systems
> + *
> + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
> + *
> + * Author: dann frazier <[email protected]>
> + * Based on efirtc.c by Stephane Eranian
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/time.h>
> +#include <linux/platform_device.h>
> +#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
> +
> +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
> +
> +/*
> + * 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 - 1, 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) {
> + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
> + return -1;
> + }
> +
> + for (y = EFI_RTC_EPOCH; y < eft->year; y++)
> + ndays += 365 + (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;
> + 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + efi_time_t eft;
> + efi_status_t status;
> +
> + /*
> + * As of EFI v1.10, this call always returns an unsupported status
> + */
> + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
> + (efi_bool_t *)&wkalrm->pending, &eft);
> +
> + if (status != EFI_SUCCESS)
> + return -EINVAL;
> +
> + convert_from_efi_time(&eft, &wkalrm->time);
> +
> + return rtc_valid_tm(&wkalrm->time);
> +}
> +
> +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> + efi_time_t eft;
> + efi_status_t status;
> +
> + convert_to_efi_time(&wkalrm->time, &eft);
> +
> + /*
> + * XXX Fixme:
> + * As of EFI 0.92 with the firmware I have on my
> + * machine this call does not seem to work quite
> + * right
> + *
> + * As of v1.10, this call always returns an unsupported status
> + */
> + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
> +
> + printk(KERN_WARNING "write status is %d\n", (int)status);
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static int efi_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 */
> + printk(KERN_ERR "efitime: can't read time\n");
> + return -EINVAL;
> + }
> +
> + convert_from_efi_time(&eft, tm);
> +
> + return rtc_valid_tm(tm);
> +}
> +
> +static int efi_set_time(struct device *dev, struct rtc_time *tm)
> +{
> + efi_status_t status;
> + efi_time_t eft;
> +
> + convert_to_efi_time(tm, &eft);
> +
> + status = efi.set_time(&eft);
> +
> + return status == EFI_SUCCESS ? 0 : -EINVAL;
> +}
> +
> +static const struct rtc_class_ops efi_rtc_ops = {
> + .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)
> +{
> + struct rtc_device *rtc;
> +
> + rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
> + THIS_MODULE);
> + if (IS_ERR(rtc)) {
> + int err = PTR_ERR(rtc);
> + return err;
> + }


if (IS_ERR(rtc))
return PTR_ERR(rtc);


> +
> + platform_set_drvdata(dev, rtc);
> +
> + return 0;
> +}
> +
> +static int __exit efi_rtc_remove(struct platform_device *dev)
> +{
> + struct rtc_device *rtc = platform_get_drvdata(dev);
> +
> + rtc_device_unregister(rtc);
> + kfree(rtc);


don't free it!!


> +
> + return 0;
> +}
> +
> +static struct platform_driver efi_rtc_driver = {
> + .driver = {
> + .name = "rtc-efi",
> + .owner = THIS_MODULE,
> + },
> + .probe = efi_rtc_probe,
> + .remove = __exit_p(efi_rtc_remove),
> +};
> +
> +static int __init efi_rtc_init(void)
> +{
> + return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
> +}
> +
> +static void __exit efi_rtc_exit(void)
> +{
> + platform_driver_unregister(&efi_rtc_driver);
> +}
> +
> +module_init(efi_rtc_init);
> +module_exit(efi_rtc_exit);
> +
> +MODULE_AUTHOR("dann frazier <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("EFI RTC driver");
> --
> 1.5.6.5
>


--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

2009-01-14 17:38:01

by dann frazier

[permalink] [raw]
Subject: [PATCH] add rtc platform driver for EFI

Munge Stephane Eranian's efirtc.c code into an rtc platform driver

Signed-off-by: dann frazier <[email protected]>
---
arch/ia64/kernel/time.c | 16 +++
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-efi.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 264 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-efi.c

diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb34..558051c 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
#include <linux/efi.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/platform_device.h>

#include <asm/machvec.h>
#include <asm/delay.h>
@@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
.name = "timer"
};

+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ printk(KERN_ERR "unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
+
void __init
time_init (void)
{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index cced4d1..56cc98f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -434,6 +434,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.

+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e28021..7256ce8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 0000000..cf37810
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,237 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <[email protected]>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#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
+
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+/*
+ * 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 - 1, 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) {
+ printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (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;
+ 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 int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_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 */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .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)
+{
+ struct rtc_device *rtc;
+
+ rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __exit efi_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __exit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
--
1.5.6.5

2009-01-14 17:39:22

by dann frazier

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Wed, Jan 14, 2009 at 11:57:28AM +0100, Alessandro Zummo wrote:
> On Tue, 13 Jan 2009 21:33:38 -0700
> dann frazier <[email protected]> wrote:
>
> > Munge Stephane Eranian's efirtc.c code into an rtc platform driver
>
> almost there :) see below

Thanks - another patch sent.

By the way, what do you think about moving the LEAP_YEAR macro in
rtc-lib into rtc.h to avoid the duplicate definition?

--
dann frazier

2009-01-14 21:27:05

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Wed, 14 Jan 2009 10:37:45 -0700
dann frazier <[email protected]> wrote:

> +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))

gargh. We should put a tax on #defines to discourage their consumption.


How about we do this:

From: Andrew Morton <[email protected]>

- the LEAP_YEAR macro is buggy - it references its arg multiple times.
Fix this by turning it into a C function.

- give it a more approriate name

- Move it to rtc.h so that other .c files can use it, instead of copying it.

Cc: dann frazier <[email protected]>
Cc: Alessandro Zummo <[email protected]>
Cc: stephane eranian <[email protected]>
Cc: "Luck, Tony" <[email protected]>
Cc: David Brownell <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---

drivers/rtc/rtc-lib.c | 7 +++----
include/linux/rtc.h | 6 ++++++
2 files changed, 9 insertions(+), 4 deletions(-)

diff -puN drivers/rtc/rtc-lib.c~rtc-convert-leap_year-into-an-inline drivers/rtc/rtc-lib.c
--- a/drivers/rtc/rtc-lib.c~rtc-convert-leap_year-into-an-inline
+++ a/drivers/rtc/rtc-lib.c
@@ -26,14 +26,13 @@ static const unsigned short rtc_ydays[2]
};

#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
-#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))

/*
* The number of days in the month.
*/
int rtc_month_days(unsigned int month, unsigned int year)
{
- return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+ return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
}
EXPORT_SYMBOL(rtc_month_days);

@@ -42,7 +41,7 @@ EXPORT_SYMBOL(rtc_month_days);
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
- return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
+ return rtc_ydays[is_leap_year(year)][month] + day-1;
}
EXPORT_SYMBOL(rtc_year_days);

@@ -66,7 +65,7 @@ void rtc_time_to_tm(unsigned long time,
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
- days += 365 + LEAP_YEAR(year);
+ days += 365 + is_leap_year(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
diff -puN include/linux/rtc.h~rtc-convert-leap_year-into-an-inline include/linux/rtc.h
--- a/include/linux/rtc.h~rtc-convert-leap_year-into-an-inline
+++ a/include/linux/rtc.h
@@ -99,6 +99,7 @@ struct rtc_pll_info {

#ifdef __KERNEL__

+#include <linux/types.h>
#include <linux/interrupt.h>

extern int rtc_month_days(unsigned int month, unsigned int year);
@@ -232,6 +233,11 @@ int rtc_register(rtc_task_t *task);
int rtc_unregister(rtc_task_t *task);
int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg);

+static inline bool is_leap_year(unsigned int year)
+{
+ return (!(year % 4) && (year % 100)) || !(year % 400);
+}
+
#endif /* __KERNEL__ */

#endif /* _LINUX_RTC_H_ */
_



and then we modify your patch thusly:


From: Andrew Morton <[email protected]>

Use is_leap_year()

Cc: "Luck, Tony" <[email protected]>
Cc: Alessandro Zummo <[email protected]>
Cc: David Brownell <[email protected]>
Cc: dann frazier <[email protected]>
Cc: dann frazier <[email protected]>
Cc: stephane eranian <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
---

drivers/rtc/rtc-efi.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)

diff -puN arch/ia64/kernel/time.c~rtc-add-platform-driver-for-efi-fix arch/ia64/kernel/time.c
diff -puN drivers/rtc/Kconfig~rtc-add-platform-driver-for-efi-fix drivers/rtc/Kconfig
diff -puN drivers/rtc/Makefile~rtc-add-platform-driver-for-efi-fix drivers/rtc/Makefile
diff -puN drivers/rtc/rtc-efi.c~rtc-add-platform-driver-for-efi-fix drivers/rtc/rtc-efi.c
--- a/drivers/rtc/rtc-efi.c~rtc-add-platform-driver-for-efi-fix
+++ a/drivers/rtc/rtc-efi.c
@@ -26,8 +26,6 @@
*/
#define EFI_RTC_EPOCH 1998

-#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
-
/*
* returns day of the year [0-365]
*/
@@ -54,7 +52,7 @@ compute_wday(efi_time_t *eft)
}

for (y = EFI_RTC_EPOCH; y < eft->year; y++)
- ndays += 365 + (LEAP_YEAR(y) ? 1 : 0);
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);

ndays += compute_yday(eft);

_

2009-01-14 21:39:35

by Alessandro Zummo

[permalink] [raw]
Subject: Re: [rtc-linux] Re: [PATCH] add rtc platform driver for EFI

On Wed, 14 Jan 2009 13:25:54 -0800
Andrew Morton <[email protected]> wrote:

> From: Andrew Morton <[email protected]>
>
> - the LEAP_YEAR macro is buggy - it references its arg multiple times.
> Fix this by turning it into a C function.
>
> - give it a more approriate name
>
> - Move it to rtc.h so that other .c files can use it, instead of copying it.

Seems nice! I'll send to Linus :))))


Acked-by: Alessandro Zummo <[email protected]>

--

Best regards,

Alessandro Zummo,
Tower Technologies - Torino, Italy

http://www.towertech.it

2009-01-14 21:46:30

by dann frazier

[permalink] [raw]
Subject: Re: [PATCH] add rtc platform driver for EFI

On Wed, Jan 14, 2009 at 01:25:54PM -0800, Andrew Morton wrote:
> On Wed, 14 Jan 2009 10:37:45 -0700
> dann frazier <[email protected]> wrote:
>
> > +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
>
> gargh. We should put a tax on #defines to discourage their consumption.
>
>
> How about we do this:

Sounds good to me :)

> From: Andrew Morton <[email protected]>
>
> - the LEAP_YEAR macro is buggy - it references its arg multiple times.
> Fix this by turning it into a C function.
>
> - give it a more approriate name
>
> - Move it to rtc.h so that other .c files can use it, instead of copying it.
>
> Cc: dann frazier <[email protected]>
> Cc: Alessandro Zummo <[email protected]>
> Cc: stephane eranian <[email protected]>
> Cc: "Luck, Tony" <[email protected]>
> Cc: David Brownell <[email protected]>
> Signed-off-by: Andrew Morton <[email protected]>
> ---
>
> drivers/rtc/rtc-lib.c | 7 +++----
> include/linux/rtc.h | 6 ++++++
> 2 files changed, 9 insertions(+), 4 deletions(-)
>
> diff -puN drivers/rtc/rtc-lib.c~rtc-convert-leap_year-into-an-inline drivers/rtc/rtc-lib.c
> --- a/drivers/rtc/rtc-lib.c~rtc-convert-leap_year-into-an-inline
> +++ a/drivers/rtc/rtc-lib.c
> @@ -26,14 +26,13 @@ static const unsigned short rtc_ydays[2]
> };
>
> #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
> -#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
>
> /*
> * The number of days in the month.
> */
> int rtc_month_days(unsigned int month, unsigned int year)
> {
> - return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
> + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
> }
> EXPORT_SYMBOL(rtc_month_days);
>
> @@ -42,7 +41,7 @@ EXPORT_SYMBOL(rtc_month_days);
> */
> int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
> {
> - return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
> + return rtc_ydays[is_leap_year(year)][month] + day-1;
> }
> EXPORT_SYMBOL(rtc_year_days);
>
> @@ -66,7 +65,7 @@ void rtc_time_to_tm(unsigned long time,
> - LEAPS_THRU_END_OF(1970 - 1);
> if (days < 0) {
> year -= 1;
> - days += 365 + LEAP_YEAR(year);
> + days += 365 + is_leap_year(year);
> }
> tm->tm_year = year - 1900;
> tm->tm_yday = days + 1;
> diff -puN include/linux/rtc.h~rtc-convert-leap_year-into-an-inline include/linux/rtc.h
> --- a/include/linux/rtc.h~rtc-convert-leap_year-into-an-inline
> +++ a/include/linux/rtc.h
> @@ -99,6 +99,7 @@ struct rtc_pll_info {
>
> #ifdef __KERNEL__
>
> +#include <linux/types.h>
> #include <linux/interrupt.h>
>
> extern int rtc_month_days(unsigned int month, unsigned int year);
> @@ -232,6 +233,11 @@ int rtc_register(rtc_task_t *task);
> int rtc_unregister(rtc_task_t *task);
> int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg);
>
> +static inline bool is_leap_year(unsigned int year)
> +{
> + return (!(year % 4) && (year % 100)) || !(year % 400);
> +}
> +
> #endif /* __KERNEL__ */
>
> #endif /* _LINUX_RTC_H_ */
> _
>
>
>
> and then we modify your patch thusly:
>
>
> From: Andrew Morton <[email protected]>
>
> Use is_leap_year()
>
> Cc: "Luck, Tony" <[email protected]>
> Cc: Alessandro Zummo <[email protected]>
> Cc: David Brownell <[email protected]>
> Cc: dann frazier <[email protected]>
> Cc: dann frazier <[email protected]>
> Cc: stephane eranian <[email protected]>
> Signed-off-by: Andrew Morton <[email protected]>
> ---
>
> drivers/rtc/rtc-efi.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff -puN arch/ia64/kernel/time.c~rtc-add-platform-driver-for-efi-fix arch/ia64/kernel/time.c
> diff -puN drivers/rtc/Kconfig~rtc-add-platform-driver-for-efi-fix drivers/rtc/Kconfig
> diff -puN drivers/rtc/Makefile~rtc-add-platform-driver-for-efi-fix drivers/rtc/Makefile
> diff -puN drivers/rtc/rtc-efi.c~rtc-add-platform-driver-for-efi-fix drivers/rtc/rtc-efi.c
> --- a/drivers/rtc/rtc-efi.c~rtc-add-platform-driver-for-efi-fix
> +++ a/drivers/rtc/rtc-efi.c
> @@ -26,8 +26,6 @@
> */
> #define EFI_RTC_EPOCH 1998
>
> -#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
> -
> /*
> * returns day of the year [0-365]
> */
> @@ -54,7 +52,7 @@ compute_wday(efi_time_t *eft)
> }
>
> for (y = EFI_RTC_EPOCH; y < eft->year; y++)
> - ndays += 365 + (LEAP_YEAR(y) ? 1 : 0);
> + ndays += 365 + (is_leap_year(y) ? 1 : 0);
>
> ndays += compute_yday(eft);
>
> _
>

--
dann frazier