aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig276
-rw-r--r--drivers/rtc/Makefile8
-rw-r--r--drivers/rtc/class.c118
-rw-r--r--drivers/rtc/hctosys.c14
-rw-r--r--drivers/rtc/interface.c86
-rw-r--r--drivers/rtc/rtc-at91rm9200.c32
-rw-r--r--drivers/rtc/rtc-cmos.c79
-rw-r--r--drivers/rtc/rtc-core.h70
-rw-r--r--drivers/rtc/rtc-dev.c184
-rw-r--r--drivers/rtc/rtc-ds1553.c2
-rw-r--r--drivers/rtc/rtc-lib.c81
-rw-r--r--drivers/rtc/rtc-max6900.c311
-rw-r--r--drivers/rtc/rtc-omap.c57
-rw-r--r--drivers/rtc/rtc-pl031.c2
-rw-r--r--drivers/rtc/rtc-proc.c68
-rw-r--r--drivers/rtc/rtc-rs5c313.c405
-rw-r--r--drivers/rtc/rtc-s3c.c26
-rw-r--r--drivers/rtc/rtc-sa1100.c4
-rw-r--r--drivers/rtc/rtc-sh.c6
-rw-r--r--drivers/rtc/rtc-sysfs.c129
-rw-r--r--drivers/rtc/rtc-test.c6
-rw-r--r--drivers/rtc/rtc-vr41xx.c32
22 files changed, 1333 insertions, 663 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ef1eae98ba4..5e439836db2 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -21,21 +21,31 @@ config RTC_CLASS
will be called rtc-class.
config RTC_HCTOSYS
- bool "Set system time from RTC on startup"
+ bool "Set system time from RTC on startup and resume"
depends on RTC_CLASS = y
default y
help
- If you say yes here, the system time will be set using
- the value read from the specified RTC device. This is useful
- in order to avoid unnecessary fsck runs.
+ If you say yes here, the system time (wall clock) will be set using
+ the value read from a specified RTC device. This is useful to avoid
+ unnecessary fsck runs at boot time, and to network better.
config RTC_HCTOSYS_DEVICE
- string "The RTC to read the time from"
+ string "RTC used to set the system time"
depends on RTC_HCTOSYS = y
default "rtc0"
help
- The RTC device that will be used as the source for
- the system time, usually rtc0.
+ The RTC device that will be used to (re)initialize the system
+ clock, usually rtc0. Initialization is done when the system
+ starts up, and when it resumes from a low power state.
+
+ This clock should be battery-backed, so that it reads the correct
+ time when the system boots from a power-off state. Otherwise, your
+ system will need an external clock source (like an NTP server).
+
+ If the clock you specify here is not battery backed, it may still
+ be useful to reinitialize system time when resuming from system
+ sleep states. Do not specify an RTC here unless it stays powered
+ during all this system's supported sleep states.
config RTC_DEBUG
bool "RTC debug support"
@@ -48,7 +58,7 @@ comment "RTC interfaces"
depends on RTC_CLASS
config RTC_INTF_SYSFS
- tristate "sysfs"
+ boolean "sysfs"
depends on RTC_CLASS && SYSFS
default RTC_CLASS
help
@@ -59,7 +69,7 @@ config RTC_INTF_SYSFS
will be called rtc-sysfs.
config RTC_INTF_PROC
- tristate "proc"
+ boolean "proc"
depends on RTC_CLASS && PROC_FS
default RTC_CLASS
help
@@ -71,7 +81,7 @@ config RTC_INTF_PROC
will be called rtc-proc.
config RTC_INTF_DEV
- tristate "dev"
+ boolean "dev"
depends on RTC_CLASS
default RTC_CLASS
help
@@ -92,44 +102,26 @@ config RTC_INTF_DEV_UIE_EMUL
driver does not expose RTC_UIE ioctls. Those requests generate
once-per-second update interrupts, used for synchronization.
-comment "RTC drivers"
+config RTC_DRV_TEST
+ tristate "Test driver/device"
depends on RTC_CLASS
-
-# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
-# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
-# global rtc_lock ... it's not yet just another platform_device.
-
-config RTC_DRV_CMOS
- tristate "PC-style 'CMOS' real time clock"
- depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \
- || M32R || ATARI || POWERPC)
- help
- Say "yes" here to get direct support for the real time clock
- found in every PC or ACPI-based system, and some other boards.
- Specifically the original MC146818, compatibles like those in
- PC south bridges, the DS12887 or M48T86, some multifunction
- or LPC bus chips, and so on.
-
- Your system will need to define the platform device used by
- this driver, otherwise it won't be accessible. This means
- you can safely enable this driver if you don't know whether
- or not your board has this kind of hardware.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-cmos.
-
-config RTC_DRV_X1205
- tristate "Xicor/Intersil X1205"
- depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
- Xicor/Intersil X1205 RTC chip.
+ RTC test driver. It's a software RTC which can be
+ used to test the RTC subsystem APIs. It gets
+ the time from the system clock.
+ You want this driver only if you are doing development
+ on the RTC subsystem. Please read the source code
+ for further details.
This driver can also be built as a module. If so, the module
- will be called rtc-x1205.
+ will be called rtc-test.
+
+comment "I2C RTC drivers"
+ depends on RTC_CLASS
config RTC_DRV_DS1307
- tristate "Dallas/Maxim DS1307 and similar I2C RTC chips"
+ tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for various compatible RTC
@@ -146,53 +138,55 @@ config RTC_DRV_DS1307
This driver can also be built as a module. If so, the module
will be called rtc-ds1307.
-config RTC_DRV_DS1553
- tristate "Dallas DS1553"
- depends on RTC_CLASS
+config RTC_DRV_DS1672
+ tristate "Dallas/Maxim DS1672"
+ depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
- Dallas DS1553 timekeeping chip.
+ Dallas/Maxim DS1672 timekeeping chip.
This driver can also be built as a module. If so, the module
- will be called rtc-ds1553.
+ will be called rtc-ds1672.
-config RTC_DRV_ISL1208
- tristate "Intersil 1208"
+config RTC_DRV_MAX6900
+ tristate "Maxim 6900"
depends on RTC_CLASS && I2C
help
- If you say yes here you get support for the
- Intersil 1208 RTC chip.
+ If you say yes here you will get support for the
+ Maxim MAX6900 I2C RTC chip.
This driver can also be built as a module. If so, the module
- will be called rtc-isl1208.
+ will be called rtc-max6900.
-config RTC_DRV_DS1672
- tristate "Dallas/Maxim DS1672"
+config RTC_DRV_RS5C372
+ tristate "Ricoh RS5C372A/B"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
- Dallas/Maxim DS1672 timekeeping chip.
+ Ricoh RS5C372A and RS5C372B RTC chips.
This driver can also be built as a module. If so, the module
- will be called rtc-ds1672.
+ will be called rtc-rs5c372.
-config RTC_DRV_DS1742
- tristate "Dallas DS1742/1743"
- depends on RTC_CLASS
+config RTC_DRV_ISL1208
+ tristate "Intersil 1208"
+ depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
- Dallas DS1742/1743 timekeeping chip.
+ Intersil 1208 RTC chip.
This driver can also be built as a module. If so, the module
- will be called rtc-ds1742.
+ will be called rtc-isl1208.
-config RTC_DRV_OMAP
- tristate "TI OMAP1"
- depends on RTC_CLASS && ( \
- ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 )
+config RTC_DRV_X1205
+ tristate "Xicor/Intersil X1205"
+ depends on RTC_CLASS && I2C
help
- Say "yes" here to support the real time clock on TI OMAP1 chips.
- This driver can also be built as a module called rtc-omap.
+ If you say yes here you get support for the
+ Xicor/Intersil X1205 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-x1205.
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
@@ -207,16 +201,20 @@ config RTC_DRV_PCF8563
config RTC_DRV_PCF8583
tristate "Philips PCF8583"
- depends on RTC_CLASS && I2C && ARCH_RPC
+ depends on RTC_CLASS && I2C
help
If you say yes here you get support for the Philips PCF8583
- RTC chip found on Acorn RiscPCs. This driver supports the
+ RTC chip found on Acorn RiscPCs. This driver supports the
platform specific method of retrieving the current year from
- the RTC's SRAM.
+ the RTC's SRAM. It will work on other platforms with the same
+ chip, but the year will probably have to be tweaked.
This driver can also be built as a module. If so, the module
will be called rtc-pcf8583.
+comment "SPI RTC drivers"
+ depends on RTC_CLASS
+
config RTC_DRV_RS5C348
tristate "Ricoh RS5C348A/B"
depends on RTC_CLASS && SPI
@@ -227,15 +225,92 @@ config RTC_DRV_RS5C348
This driver can also be built as a module. If so, the module
will be called rtc-rs5c348.
-config RTC_DRV_RS5C372
- tristate "Ricoh RS5C372A/B"
- depends on RTC_CLASS && I2C
+config RTC_DRV_MAX6902
+ tristate "Maxim 6902"
+ depends on RTC_CLASS && SPI
+ help
+ If you say yes here you will get support for the
+ Maxim MAX6902 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max6902.
+
+comment "Platform RTC drivers"
+ depends on RTC_CLASS
+
+# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
+# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
+# global rtc_lock ... it's not yet just another platform_device.
+
+config RTC_DRV_CMOS
+ tristate "PC-style 'CMOS'"
+ depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \
+ || M32R || ATARI || POWERPC)
+ help
+ Say "yes" here to get direct support for the real time clock
+ found in every PC or ACPI-based system, and some other boards.
+ Specifically the original MC146818, compatibles like those in
+ PC south bridges, the DS12887 or M48T86, some multifunction
+ or LPC bus chips, and so on.
+
+ Your system will need to define the platform device used by
+ this driver, otherwise it won't be accessible. This means
+ you can safely enable this driver if you don't know whether
+ or not your board has this kind of hardware.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-cmos.
+
+config RTC_DRV_DS1553
+ tristate "Dallas DS1553"
+ depends on RTC_CLASS
help
If you say yes here you get support for the
- Ricoh RS5C372A and RS5C372B RTC chips.
+ Dallas DS1553 timekeeping chip.
This driver can also be built as a module. If so, the module
- will be called rtc-rs5c372.
+ will be called rtc-ds1553.
+
+config RTC_DRV_DS1742
+ tristate "Dallas DS1742/1743"
+ depends on RTC_CLASS
+ help
+ If you say yes here you get support for the
+ Dallas DS1742/1743 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1742.
+
+config RTC_DRV_M48T86
+ tristate "ST M48T86/Dallas DS12887"
+ depends on RTC_CLASS
+ help
+ If you say Y here you will get support for the
+ ST M48T86 and Dallas DS12887 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m48t86.
+
+config RTC_DRV_V3020
+ tristate "EM Microelectronic V3020"
+ depends on RTC_CLASS
+ help
+ If you say yes here you will get support for the
+ EM Microelectronic v3020 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-v3020.
+
+comment "on-CPU RTC drivers"
+ depends on RTC_CLASS
+
+config RTC_DRV_OMAP
+ tristate "TI OMAP1"
+ depends on RTC_CLASS && ( \
+ ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 )
+ help
+ Say "yes" here to support the real time clock on TI OMAP1 chips.
+ This driver can also be built as a module called rtc-omap.
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
@@ -253,16 +328,6 @@ config RTC_DRV_S3C
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
-config RTC_DRV_M48T86
- tristate "ST M48T86/Dallas DS12887"
- depends on RTC_CLASS
- help
- If you say Y here you will get support for the
- ST M48T86 and Dallas DS12887 RTC chips.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-m48t86.
-
config RTC_DRV_EP93XX
tristate "Cirrus Logic EP93XX"
depends on RTC_CLASS && ARCH_EP93XX
@@ -308,7 +373,7 @@ config RTC_DRV_PL031
depends on RTC_CLASS && ARM_AMBA
help
If you say Y here you will get access to ARM AMBA
- PrimeCell PL031 UART found on certain ARM SOCs.
+ PrimeCell PL031 RTC found on certain ARM SOCs.
To compile this driver as a module, choose M here: the
module will be called rtc-pl031.
@@ -319,41 +384,6 @@ config RTC_DRV_AT91RM9200
help
Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock).
-config RTC_DRV_TEST
- tristate "Test driver/device"
- depends on RTC_CLASS
- help
- If you say yes here you get support for the
- RTC test driver. It's a software RTC which can be
- used to test the RTC subsystem APIs. It gets
- the time from the system clock.
- You want this driver only if you are doing development
- on the RTC subsystem. Please read the source code
- for further details.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-test.
-
-config RTC_DRV_MAX6902
- tristate "Maxim 6902"
- depends on RTC_CLASS && SPI
- help
- If you say yes here you will get support for the
- Maxim MAX6902 spi RTC chip.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-max6902.
-
-config RTC_DRV_V3020
- tristate "EM Microelectronic V3020"
- depends on RTC_CLASS
- help
- If you say yes here you will get support for the
- EM Microelectronic v3020 RTC chip.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-v3020.
-
config RTC_DRV_BFIN
tristate "Blackfin On-Chip RTC"
depends on RTC_CLASS && BFIN
@@ -364,4 +394,10 @@ config RTC_DRV_BFIN
This driver can also be built as a module. If so, the module
will be called rtc-bfin.
+config RTC_DRV_RS5C313
+ tristate "Ricoh RS5C313"
+ depends on RTC_CLASS && BROKEN
+ help
+ If you say yes here you get support for the Ricoh RS5C313 RTC chips.
+
endmenu
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 9218cf28d6e..a1afbc23607 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -11,9 +11,9 @@ obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o
-obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
-obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
-obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
+rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
+rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
+rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
@@ -30,10 +30,12 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
+obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
+obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 04aaa634723..8b3cd31d6a6 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -16,19 +16,94 @@
#include <linux/kdev_t.h>
#include <linux/idr.h>
+#include "rtc-core.h"
+
+
static DEFINE_IDR(rtc_idr);
static DEFINE_MUTEX(idr_lock);
struct class *rtc_class;
-static void rtc_device_release(struct class_device *class_dev)
+static void rtc_device_release(struct device *dev)
{
- struct rtc_device *rtc = to_rtc_device(class_dev);
+ struct rtc_device *rtc = to_rtc_device(dev);
mutex_lock(&idr_lock);
idr_remove(&rtc_idr, rtc->id);
mutex_unlock(&idr_lock);
kfree(rtc);
}
+#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
+
+/*
+ * On suspend(), measure the delta between one RTC and the
+ * system's wall clock; restore it on resume().
+ */
+
+static struct timespec delta;
+static time_t oldtime;
+
+static int rtc_suspend(struct device *dev, pm_message_t mesg)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ struct rtc_time tm;
+
+ if (strncmp(rtc->dev.bus_id,
+ CONFIG_RTC_HCTOSYS_DEVICE,
+ BUS_ID_SIZE) != 0)
+ return 0;
+
+ rtc_read_time(rtc, &tm);
+ rtc_tm_to_time(&tm, &oldtime);
+
+ /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
+ set_normalized_timespec(&delta,
+ xtime.tv_sec - oldtime,
+ xtime.tv_nsec - (NSEC_PER_SEC >> 1));
+
+ return 0;
+}
+
+static int rtc_resume(struct device *dev)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ struct rtc_time tm;
+ time_t newtime;
+ struct timespec time;
+
+ if (strncmp(rtc->dev.bus_id,
+ CONFIG_RTC_HCTOSYS_DEVICE,
+ BUS_ID_SIZE) != 0)
+ return 0;
+
+ rtc_read_time(rtc, &tm);
+ if (rtc_valid_tm(&tm) != 0) {
+ pr_debug("%s: bogus resume time\n", rtc->dev.bus_id);
+ return 0;
+ }
+ rtc_tm_to_time(&tm, &newtime);
+ if (newtime <= oldtime) {
+ if (newtime < oldtime)
+ pr_debug("%s: time travel!\n", rtc->dev.bus_id);
+ return 0;
+ }
+
+ /* restore wall clock using delta against this RTC;
+ * adjust again for avg 1/2 second RTC sampling error
+ */
+ set_normalized_timespec(&time,
+ newtime + delta.tv_sec,
+ (NSEC_PER_SEC >> 1) + delta.tv_nsec);
+ do_settimeofday(&time);
+
+ return 0;
+}
+
+#else
+#define rtc_suspend NULL
+#define rtc_resume NULL
+#endif
+
+
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
@@ -70,23 +145,29 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
- rtc->class_dev.dev = dev;
- rtc->class_dev.class = rtc_class;
- rtc->class_dev.release = rtc_device_release;
+ rtc->dev.parent = dev;
+ rtc->dev.class = rtc_class;
+ rtc->dev.release = rtc_device_release;
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
- snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id);
+ snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id);
- err = class_device_register(&rtc->class_dev);
+ rtc_dev_prepare(rtc);
+
+ err = device_register(&rtc->dev);
if (err)
goto exit_kfree;
+ rtc_dev_add_device(rtc);
+ rtc_sysfs_add_device(rtc);
+ rtc_proc_add_device(rtc);
+
dev_info(dev, "rtc core: registered %s as %s\n",
- rtc->name, rtc->class_dev.class_id);
+ rtc->name, rtc->dev.bus_id);
return rtc;
@@ -113,26 +194,22 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
- if (class_device_get(&rtc->class_dev) != NULL) {
+ if (get_device(&rtc->dev) != NULL) {
mutex_lock(&rtc->ops_lock);
/* remove innards of this RTC, then disable it, before
* letting any rtc_class_open() users access it again
*/
- class_device_unregister(&rtc->class_dev);
+ rtc_sysfs_del_device(rtc);
+ rtc_dev_del_device(rtc);
+ rtc_proc_del_device(rtc);
+ device_unregister(&rtc->dev);
rtc->ops = NULL;
mutex_unlock(&rtc->ops_lock);
- class_device_put(&rtc->class_dev);
+ put_device(&rtc->dev);
}
}
EXPORT_SYMBOL_GPL(rtc_device_unregister);
-int rtc_interface_register(struct class_interface *intf)
-{
- intf->class = rtc_class;
- return class_interface_register(intf);
-}
-EXPORT_SYMBOL_GPL(rtc_interface_register);
-
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
@@ -140,11 +217,16 @@ static int __init rtc_init(void)
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
+ rtc_class->suspend = rtc_suspend;
+ rtc_class->resume = rtc_resume;
+ rtc_dev_init();
+ rtc_sysfs_init(rtc_class);
return 0;
}
static void __exit rtc_exit(void)
{
+ rtc_dev_exit();
class_destroy(rtc_class);
}
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
index d02fe9a0001..178527252c6 100644
--- a/drivers/rtc/hctosys.c
+++ b/drivers/rtc/hctosys.c
@@ -26,15 +26,15 @@ static int __init rtc_hctosys(void)
{
int err;
struct rtc_time tm;
- struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (class_dev == NULL) {
+ if (rtc == NULL) {
printk("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -ENODEV;
}
- err = rtc_read_time(class_dev, &tm);
+ err = rtc_read_time(rtc, &tm);
if (err == 0) {
err = rtc_valid_tm(&tm);
if (err == 0) {
@@ -46,7 +46,7 @@ static int __init rtc_hctosys(void)
do_settimeofday(&tv);
- dev_info(class_dev->dev,
+ dev_info(rtc->dev.parent,
"setting the system clock to "
"%d-%02d-%02d %02d:%02d:%02d (%u)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
@@ -54,14 +54,14 @@ static int __init rtc_hctosys(void)
(unsigned int) tv.tv_sec);
}
else
- dev_err(class_dev->dev,
+ dev_err(rtc->dev.parent,
"hctosys: invalid date/time\n");
}
else
- dev_err(class_dev->dev,
+ dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
- rtc_class_close(class_dev);
+ rtc_class_close(rtc);
return 0;
}
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index ef40df0f169..ad66c6ecf36 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -13,10 +13,9 @@
#include <linux/rtc.h>
-int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
+int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
- struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
@@ -28,7 +27,7 @@ int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
err = -EINVAL;
else {
memset(tm, 0, sizeof(struct rtc_time));
- err = rtc->ops->read_time(class_dev->dev, tm);
+ err = rtc->ops->read_time(rtc->dev.parent, tm);
}
mutex_unlock(&rtc->ops_lock);
@@ -36,10 +35,9 @@ int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
}
EXPORT_SYMBOL_GPL(rtc_read_time);
-int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
+int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
{
int err;
- struct rtc_device *rtc = to_rtc_device(class_dev);
err = rtc_valid_tm(tm);
if (err != 0)
@@ -54,17 +52,16 @@ int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
else if (!rtc->ops->set_time)
err = -EINVAL;
else
- err = rtc->ops->set_time(class_dev->dev, tm);
+ err = rtc->ops->set_time(rtc->dev.parent, tm);
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);
-int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
+int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
{
int err;
- struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
@@ -73,11 +70,11 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_mmss)
- err = rtc->ops->set_mmss(class_dev->dev, secs);
+ err = rtc->ops->set_mmss(rtc->dev.parent, secs);
else if (rtc->ops->read_time && rtc->ops->set_time) {
struct rtc_time new, old;
- err = rtc->ops->read_time(class_dev->dev, &old);
+ err = rtc->ops->read_time(rtc->dev.parent, &old);
if (err == 0) {
rtc_time_to_tm(secs, &new);
@@ -89,7 +86,8 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
*/
if (!((old.tm_hour == 23 && old.tm_min == 59) ||
(new.tm_hour == 23 && new.tm_min == 59)))
- err = rtc->ops->set_time(class_dev->dev, &new);
+ err = rtc->ops->set_time(rtc->dev.parent,
+ &new);
}
}
else
@@ -101,10 +99,9 @@ int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);
-int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
- struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
@@ -116,7 +113,7 @@ int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
err = -EINVAL;
else {
memset(alarm, 0, sizeof(struct rtc_wkalrm));
- err = rtc->ops->read_alarm(class_dev->dev, alarm);
+ err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
}
mutex_unlock(&rtc->ops_lock);
@@ -124,10 +121,13 @@ int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
-int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
- struct rtc_device *rtc = to_rtc_device(class_dev);
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err != 0)
+ return err;
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
@@ -138,7 +138,7 @@ int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
else if (!rtc->ops->set_alarm)
err = -EINVAL;
else
- err = rtc->ops->set_alarm(class_dev->dev, alarm);
+ err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
mutex_unlock(&rtc->ops_lock);
return err;
@@ -147,16 +147,14 @@ EXPORT_SYMBOL_GPL(rtc_set_alarm);
/**
* rtc_update_irq - report RTC periodic, alarm, and/or update irqs
- * @class_dev: the rtc's class device
+ * @rtc: the rtc device
* @num: how many irqs are being reported (usually one)
* @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
* Context: in_interrupt(), irqs blocked
*/
-void rtc_update_irq(struct class_device *class_dev,
+void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
{
- struct rtc_device *rtc = to_rtc_device(class_dev);
-
spin_lock(&rtc->irq_lock);
rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
spin_unlock(&rtc->irq_lock);
@@ -171,40 +169,43 @@ void rtc_update_irq(struct class_device *class_dev,
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
-struct class_device *rtc_class_open(char *name)
+struct rtc_device *rtc_class_open(char *name)
{
- struct class_device *class_dev = NULL,
- *class_dev_tmp;
+ struct device *dev;
+ struct rtc_device *rtc = NULL;
down(&rtc_class->sem);
- list_for_each_entry(class_dev_tmp, &rtc_class->children, node) {
- if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) {
- class_dev = class_device_get(class_dev_tmp);
+ list_for_each_entry(dev, &rtc_class->devices, node) {
+ if (strncmp(dev->bus_id, name, BUS_ID_SIZE) == 0) {
+ dev = get_device(dev);
+ if (dev)
+ rtc = to_rtc_device(dev);
break;
}
}
- if (class_dev) {
- if (!try_module_get(to_rtc_device(class_dev)->owner))
- class_dev = NULL;
+ if (rtc) {
+ if (!try_module_get(rtc->owner)) {
+ put_device(dev);
+ rtc = NULL;
+ }
}
up(&rtc_class->sem);
- return class_dev;
+ return rtc;
}
EXPORT_SYMBOL_GPL(rtc_class_open);
-void rtc_class_close(struct class_device *class_dev)
+void rtc_class_close(struct rtc_device *rtc)
{
- module_put(to_rtc_device(class_dev)->owner);
- class_device_put(class_dev);
+ module_put(rtc->owner);
+ put_device(&rtc->dev);
}
EXPORT_SYMBOL_GPL(rtc_class_close);
-int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
+int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
{
int retval = -EBUSY;
- struct rtc_device *rtc = to_rtc_device(class_dev);
if (task == NULL || task->func == NULL)
return -EINVAL;
@@ -220,9 +221,8 @@ int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
}
EXPORT_SYMBOL_GPL(rtc_irq_register);
-void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
+void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
{
- struct rtc_device *rtc = to_rtc_device(class_dev);
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == task)
@@ -231,11 +231,10 @@ void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);
-int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled)
+int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)
{
int err = 0;
unsigned long flags;
- struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->ops->irq_set_state == NULL)
return -ENXIO;
@@ -246,17 +245,16 @@ int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0)
- err = rtc->ops->irq_set_state(class_dev->dev, enabled);
+ err = rtc->ops->irq_set_state(rtc->dev.parent, enabled);
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);
-int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
+int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
{
int err = 0;
unsigned long flags;
- struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->ops->irq_set_freq == NULL)
return -ENXIO;
@@ -267,7 +265,7 @@ int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0) {
- err = rtc->ops->irq_set_freq(class_dev->dev, freq);
+ err = rtc->ops->irq_set_freq(rtc->dev.parent, freq);
if (err == 0)
rtc->irq_freq = freq;
}
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index ac0e68e2f02..33795e5a559 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -263,7 +263,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
at91_sys_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
- rtc_update_irq(&rtc->class_dev, 1, events);
+ rtc_update_irq(rtc, 1, events);
pr_debug("%s(): num=%ld, events=0x%02lx\n", __FUNCTION__,
events >> 8, events & 0x000000FF);
@@ -348,21 +348,10 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)
/* AT91RM9200 RTC Power management control */
-static struct timespec at91_rtc_delta;
static u32 at91_rtc_imr;
static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- /* calculate time delta for suspend */
- at91_rtc_readtime(&pdev->dev, &tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- save_time_delta(&at91_rtc_delta, &time);
-
/* this IRQ is shared with DBGU and other hardware which isn't
* necessarily doing PM like we are...
*/
@@ -374,36 +363,17 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
else
at91_sys_write(AT91_RTC_IDR, at91_rtc_imr);
}
-
- pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
- 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-
return 0;
}
static int at91_rtc_resume(struct platform_device *pdev)
{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- at91_rtc_readtime(&pdev->dev, &tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- restore_time_delta(&at91_rtc_delta, &time);
-
if (at91_rtc_imr) {
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(AT91_ID_SYS);
else
at91_sys_write(AT91_RTC_IER, at91_rtc_imr);
}
-
- pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
- 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-
return 0;
}
#else
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 7c0d6091007..6085261aa2c 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -46,6 +46,10 @@ struct cmos_rtc {
int irq;
struct resource *iomem;
+ void (*wake_on)(struct device *);
+ void (*wake_off)(struct device *);
+
+ u8 enabled_wake;
u8 suspend_ctrl;
/* newer hardware extends the original register set */
@@ -203,7 +207,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
- rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
/* update alarm */
CMOS_WRITE(hrs, RTC_HOURS_ALARM);
@@ -223,7 +227,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
- rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
}
spin_unlock_irq(&rtc_lock);
@@ -304,7 +308,7 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
- rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
@@ -379,12 +383,12 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
return IRQ_NONE;
}
-#ifdef CONFIG_PNPACPI
-#define is_pnpacpi() 1
+#ifdef CONFIG_PNP
+#define is_pnp() 1
#define INITSECTION
#else
-#define is_pnpacpi() 0
+#define is_pnp() 0
#define INITSECTION __init
#endif
@@ -405,13 +409,20 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
cmos_rtc.irq = rtc_irq;
cmos_rtc.iomem = ports;
- /* For ACPI systems the info comes from the FADT. On others,
- * board specific setup provides it as appropriate.
+ /* For ACPI systems extension info comes from the FADT. On others,
+ * board specific setup provides it as appropriate. Systems where
+ * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
+ * some almost-clones) can provide hooks to make that behave.
*/
if (info) {
cmos_rtc.day_alrm = info->rtc_day_alarm;
cmos_rtc.mon_alrm = info->rtc_mon_alarm;
cmos_rtc.century = info->rtc_century;
+
+ if (info->wake_on && info->wake_off) {
+ cmos_rtc.wake_on = info->wake_on;
+ cmos_rtc.wake_off = info->wake_off;
+ }
}
cmos_rtc.rtc = rtc_device_register(driver_name, dev,
@@ -427,14 +438,14 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
* REVISIT for non-x86 systems we may need to handle io memory
* resources: ioremap them, and request_mem_region().
*/
- if (is_pnpacpi()) {
+ if (is_pnp()) {
retval = request_resource(&ioport_resource, ports);
if (retval < 0) {
dev_dbg(dev, "i/o registers already in use\n");
goto cleanup0;
}
}
- rename_region(ports, cmos_rtc.rtc->class_dev.class_id);
+ rename_region(ports, cmos_rtc.rtc->dev.bus_id);
spin_lock_irq(&rtc_lock);
@@ -470,8 +481,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
if (is_valid_irq(rtc_irq))
retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED,
- cmos_rtc.rtc->class_dev.class_id,
- &cmos_rtc.rtc->class_dev);
+ cmos_rtc.rtc->dev.bus_id,
+ cmos_rtc.rtc);
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
goto cleanup1;
@@ -483,7 +494,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
*/
pr_info("%s: alarms up to one %s%s\n",
- cmos_rtc.rtc->class_dev.class_id,
+ cmos_rtc.rtc->dev.bus_id,
is_valid_irq(rtc_irq)
? (cmos_rtc.mon_alrm
? "year"
@@ -520,12 +531,12 @@ static void __exit cmos_do_remove(struct device *dev)
cmos_do_shutdown();
- if (is_pnpacpi())
+ if (is_pnp())
release_resource(cmos->iomem);
rename_region(cmos->iomem, NULL);
if (is_valid_irq(cmos->irq))
- free_irq(cmos->irq, &cmos_rtc.rtc->class_dev);
+ free_irq(cmos->irq, cmos_rtc.rtc);
rtc_device_unregister(cmos_rtc.rtc);
@@ -555,16 +566,20 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
irqstat = CMOS_READ(RTC_INTR_FLAGS);
irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(irqstat))
- rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat);
+ rtc_update_irq(cmos->rtc, 1, irqstat);
}
spin_unlock_irq(&rtc_lock);
- /* ACPI HOOK: enable ACPI_EVENT_RTC when (tmp & RTC_AIE)
- * ... it'd be best if we could do that under rtc_lock.
- */
+ if (tmp & RTC_AIE) {
+ cmos->enabled_wake = 1;
+ if (cmos->wake_on)
+ cmos->wake_on(dev);
+ else
+ enable_irq_wake(cmos->irq);
+ }
pr_debug("%s: suspend%s, ctrl %02x\n",
- cmos_rtc.rtc->class_dev.class_id,
+ cmos_rtc.rtc->dev.bus_id,
(tmp & RTC_AIE) ? ", alarm may wake" : "",
tmp);
@@ -576,26 +591,28 @@ static int cmos_resume(struct device *dev)
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char tmp = cmos->suspend_ctrl;
- /* REVISIT: a mechanism to resync the system clock (jiffies)
- * on resume should be portable between platforms ...
- */
-
/* re-enable any irqs previously active */
if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
- /* ACPI HOOK: disable ACPI_EVENT_RTC when (tmp & RTC_AIE) */
+ if (cmos->enabled_wake) {
+ if (cmos->wake_off)
+ cmos->wake_off(dev);
+ else
+ disable_irq_wake(cmos->irq);
+ cmos->enabled_wake = 0;
+ }
spin_lock_irq(&rtc_lock);
CMOS_WRITE(tmp, RTC_CONTROL);
tmp = CMOS_READ(RTC_INTR_FLAGS);
tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(tmp))
- rtc_update_irq(&cmos->rtc->class_dev, 1, tmp);
+ rtc_update_irq(cmos->rtc, 1, tmp);
spin_unlock_irq(&rtc_lock);
}
pr_debug("%s: resume, ctrl %02x\n",
- cmos_rtc.rtc->class_dev.class_id,
+ cmos_rtc.rtc->dev.bus_id,
cmos->suspend_ctrl);
@@ -613,7 +630,7 @@ static int cmos_resume(struct device *dev)
* the device node will always be created as a PNPACPI device.
*/
-#ifdef CONFIG_PNPACPI
+#ifdef CONFIG_PNP
#include <linux/pnp.h>
@@ -684,11 +701,11 @@ static void __exit cmos_exit(void)
}
module_exit(cmos_exit);
-#else /* no PNPACPI */
+#else /* no PNP */
/*----------------------------------------------------------------*/
-/* Platform setup should have set up an RTC device, when PNPACPI is
+/* Platform setup should have set up an RTC device, when PNP is
* unavailable ... this could happen even on (older) PCs.
*/
@@ -734,7 +751,7 @@ static void __exit cmos_exit(void)
module_exit(cmos_exit);
-#endif /* !PNPACPI */
+#endif /* !PNP */
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
new file mode 100644
index 00000000000..5f9df7430a2
--- /dev/null
+++ b/drivers/rtc/rtc-core.h
@@ -0,0 +1,70 @@
+#ifdef CONFIG_RTC_INTF_DEV
+
+extern void __init rtc_dev_init(void);
+extern void __exit rtc_dev_exit(void);
+extern void rtc_dev_prepare(struct rtc_device *rtc);
+extern void rtc_dev_add_device(struct rtc_device *rtc);
+extern void rtc_dev_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_dev_init(void)
+{
+}
+
+static inline void rtc_dev_exit(void)
+{
+}
+
+static inline void rtc_dev_prepare(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_dev_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_dev_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_RTC_INTF_PROC
+
+extern void rtc_proc_add_device(struct rtc_device *rtc);
+extern void rtc_proc_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_proc_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_proc_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_RTC_INTF_SYSFS
+
+extern void __init rtc_sysfs_init(struct class *);
+extern void rtc_sysfs_add_device(struct rtc_device *rtc);
+extern void rtc_sysfs_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_sysfs_init(struct class *rtc)
+{
+}
+
+static inline void rtc_sysfs_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_sysfs_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 137330b8636..f4e5f0040ff 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -13,8 +13,8 @@
#include <linux/module.h>
#include <linux/rtc.h>
+#include "rtc-core.h"
-static struct class *rtc_dev_class;
static dev_t rtc_devt;
#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */
@@ -32,9 +32,9 @@ static int rtc_dev_open(struct inode *inode, struct file *file)
if (!(mutex_trylock(&rtc->char_lock)))
return -EBUSY;
- file->private_data = &rtc->class_dev;
+ file->private_data = rtc;
- err = ops->open ? ops->open(rtc->class_dev.dev) : 0;
+ err = ops->open ? ops->open(rtc->dev.parent) : 0;
if (err == 0) {
spin_lock_irq(&rtc->irq_lock);
rtc->irq_data = 0;
@@ -61,7 +61,7 @@ static void rtc_uie_task(struct work_struct *work)
int num = 0;
int err;
- err = rtc_read_time(&rtc->class_dev, &tm);
+ err = rtc_read_time(rtc, &tm);
local_irq_disable();
spin_lock(&rtc->irq_lock);
@@ -79,7 +79,7 @@ static void rtc_uie_task(struct work_struct *work)
}
spin_unlock(&rtc->irq_lock);
if (num)
- rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
+ rtc_update_irq(rtc, num, RTC_UF | RTC_IRQF);
local_irq_enable();
}
static void rtc_uie_timer(unsigned long data)
@@ -121,7 +121,7 @@ static int set_uie(struct rtc_device *rtc)
struct rtc_time tm;
int err;
- err = rtc_read_time(&rtc->class_dev, &tm);
+ err = rtc_read_time(rtc, &tm);
if (err)
return err;
spin_lock_irq(&rtc->irq_lock);
@@ -180,7 +180,7 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
if (ret == 0) {
/* Check for any data updates */
if (rtc->ops->read_callback)
- data = rtc->ops->read_callback(rtc->class_dev.dev,
+ data = rtc->ops->read_callback(rtc->dev.parent,
data);
if (sizeof(int) != sizeof(long) &&
@@ -210,8 +210,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
- struct class_device *class_dev = file->private_data;
- struct rtc_device *rtc = to_rtc_device(class_dev);
+ struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
@@ -252,7 +251,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
/* try the driver's ioctl interface */
if (ops->ioctl) {
- err = ops->ioctl(class_dev->dev, cmd, arg);
+ err = ops->ioctl(rtc->dev.parent, cmd, arg);
if (err != -ENOIOCTLCMD)
return err;
}
@@ -264,7 +263,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case RTC_ALM_READ:
- err = rtc_read_alarm(class_dev, &alarm);
+ err = rtc_read_alarm(rtc, &alarm);
if (err < 0)
return err;
@@ -278,17 +277,53 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
alarm.enabled = 0;
alarm.pending = 0;
- alarm.time.tm_mday = -1;
- alarm.time.tm_mon = -1;
- alarm.time.tm_year = -1;
alarm.time.tm_wday = -1;
alarm.time.tm_yday = -1;
alarm.time.tm_isdst = -1;
- err = rtc_set_alarm(class_dev, &alarm);
+
+ /* RTC_ALM_SET alarms may be up to 24 hours in the future.
+ * Rather than expecting every RTC to implement "don't care"
+ * for day/month/year fields, just force the alarm to have
+ * the right values for those fields.
+ *
+ * RTC_WKALM_SET should be used instead. Not only does it
+ * eliminate the need for a separate RTC_AIE_ON call, it
+ * doesn't have the "alarm 23:59:59 in the future" race.
+ *
+ * NOTE: some legacy code may have used invalid fields as
+ * wildcards, exposing hardware "periodic alarm" capabilities.
+ * Not supported here.
+ */
+ {
+ unsigned long now, then;
+
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0)
+ return err;
+ rtc_tm_to_time(&tm, &now);
+
+ alarm.time.tm_mday = tm.tm_mday;
+ alarm.time.tm_mon = tm.tm_mon;
+ alarm.time.tm_year = tm.tm_year;
+ err = rtc_valid_tm(&alarm.time);
+ if (err < 0)
+ return err;
+ rtc_tm_to_time(&alarm.time, &then);
+
+ /* alarm may need to wrap into tomorrow */
+ if (then < now) {
+ rtc_time_to_tm(now + 24 * 60 * 60, &tm);
+ alarm.time.tm_mday = tm.tm_mday;
+ alarm.time.tm_mon = tm.tm_mon;
+ alarm.time.tm_year = tm.tm_year;
+ }
+ }
+
+ err = rtc_set_alarm(rtc, &alarm);
break;
case RTC_RD_TIME:
- err = rtc_read_time(class_dev, &tm);
+ err = rtc_read_time(rtc, &tm);
if (err < 0)
return err;
@@ -300,7 +335,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
- err = rtc_set_time(class_dev, &tm);
+ err = rtc_set_time(rtc, &tm);
break;
case RTC_IRQP_READ:
@@ -310,7 +345,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
case RTC_IRQP_SET:
if (ops->irq_set_freq)
- err = rtc_irq_set_freq(class_dev, rtc->irq_task, arg);
+ err = rtc_irq_set_freq(rtc, rtc->irq_task, arg);
break;
#if 0
@@ -336,11 +371,11 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
if (copy_from_user(&alarm, uarg, sizeof(alarm)))
return -EFAULT;
- err = rtc_set_alarm(class_dev, &alarm);
+ err = rtc_set_alarm(rtc, &alarm);
break;
case RTC_WKALM_RD:
- err = rtc_read_alarm(class_dev, &alarm);
+ err = rtc_read_alarm(rtc, &alarm);
if (err < 0)
return err;
@@ -372,7 +407,7 @@ static int rtc_dev_release(struct inode *inode, struct file *file)
clear_uie(rtc);
#endif
if (rtc->ops->release)
- rtc->ops->release(rtc->class_dev.dev);
+ rtc->ops->release(rtc->dev.parent);
mutex_unlock(&rtc->char_lock);
return 0;
@@ -397,17 +432,18 @@ static const struct file_operations rtc_dev_fops = {
/* insertion/removal hooks */
-static int rtc_dev_add_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_dev_prepare(struct rtc_device *rtc)
{
- int err = 0;
- struct rtc_device *rtc = to_rtc_device(class_dev);
+ if (!rtc_devt)
+ return;
if (rtc->id >= RTC_DEV_MAX) {
- dev_err(class_dev->dev, "too many RTCs\n");
- return -EINVAL;
+ pr_debug("%s: too many RTC devices\n", rtc->name);
+ return;
}
+ rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
+
mutex_init(&rtc->char_lock);
spin_lock_init(&rtc->irq_lock);
init_waitqueue_head(&rtc->irq_queue);
@@ -418,100 +454,36 @@ static int rtc_dev_add_device(struct class_device *class_dev,
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc->char_dev.owner = rtc->owner;
+}
- if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) {
- dev_err(class_dev->dev,
- "failed to add char device %d:%d\n",
+void rtc_dev_add_device(struct rtc_device *rtc)
+{
+ if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
+ printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
+ rtc->name, MAJOR(rtc_devt), rtc->id);
+ else
+ pr_debug("%s: dev (%d:%d)\n", rtc->name,
MAJOR(rtc_devt), rtc->id);
- return -ENODEV;
- }
-
- rtc->rtc_dev = class_device_create(rtc_dev_class, NULL,
- MKDEV(MAJOR(rtc_devt), rtc->id),
- class_dev->dev, "rtc%d", rtc->id);
- if (IS_ERR(rtc->rtc_dev)) {
- dev_err(class_dev->dev, "cannot create rtc_dev device\n");
- err = PTR_ERR(rtc->rtc_dev);
- goto err_cdev_del;
- }
-
- dev_dbg(class_dev->dev, "rtc intf: dev (%d:%d)\n",
- MAJOR(rtc->rtc_dev->devt),
- MINOR(rtc->rtc_dev->devt));
-
- return 0;
-
-err_cdev_del:
-
- cdev_del(&rtc->char_dev);
- return err;
}
-static void rtc_dev_remove_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_dev_del_device(struct rtc_device *rtc)
{
- struct rtc_device *rtc = to_rtc_device(class_dev);
-
- if (rtc->rtc_dev) {
- dev_dbg(class_dev->dev, "removing char %d:%d\n",
- MAJOR(rtc->rtc_dev->devt),
- MINOR(rtc->rtc_dev->devt));
-
- class_device_unregister(rtc->rtc_dev);
+ if (rtc->dev.devt)
cdev_del(&rtc->char_dev);
- }
}
-/* interface registration */
-
-static struct class_interface rtc_dev_interface = {
- .add = &rtc_dev_add_device,
- .remove = &rtc_dev_remove_device,
-};
-
-static int __init rtc_dev_init(void)
+void __init rtc_dev_init(void)
{
int err;
- rtc_dev_class = class_create(THIS_MODULE, "rtc-dev");
- if (IS_ERR(rtc_dev_class))
- return PTR_ERR(rtc_dev_class);
-
err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
- if (err < 0) {
+ if (err < 0)
printk(KERN_ERR "%s: failed to allocate char dev region\n",
__FILE__);
- goto err_destroy_class;
- }
-
- err = rtc_interface_register(&rtc_dev_interface);
- if (err < 0) {
- printk(KERN_ERR "%s: failed to register the interface\n",
- __FILE__);
- goto err_unregister_chrdev;
- }
-
- return 0;
-
-err_unregister_chrdev:
- unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
-
-err_destroy_class:
- class_destroy(rtc_dev_class);
-
- return err;
}
-static void __exit rtc_dev_exit(void)
+void __exit rtc_dev_exit(void)
{
- class_interface_unregister(&rtc_dev_interface);
- class_destroy(rtc_dev_class);
- unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
+ if (rtc_devt)
+ unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
}
-
-subsys_initcall(rtc_dev_init);
-module_exit(rtc_dev_exit);
-
-MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
-MODULE_DESCRIPTION("RTC class dev interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index e27176c0e18..afa64c7fa2e 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -203,7 +203,7 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
events |= RTC_UF;
else
events |= RTC_AF;
- rtc_update_irq(&pdata->rtc->class_dev, 1, events);
+ rtc_update_irq(pdata->rtc, 1, events);
return IRQ_HANDLED;
}
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index 7bbc26a34bd..ba795a4db1e 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -117,85 +117,4 @@ int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
}
EXPORT_SYMBOL(rtc_tm_to_time);
-
-/* Merge the valid (i.e. non-negative) fields of alarm into the current
- * time. If the valid alarm fields are earlier than the equivalent
- * fields in the time, carry one into the least significant invalid
- * field, so that the alarm expiry is in the future. It assumes that the
- * least significant invalid field is more significant than the most
- * significant valid field, and that the seconds field is valid.
- *
- * This is used by alarms that take relative (rather than absolute)
- * times, and/or have a simple binary second counter instead of
- * day/hour/minute/sec registers.
- */
-void rtc_merge_alarm(struct rtc_time *now, struct rtc_time *alarm)
-{
- int *alarmp = &alarm->tm_sec;
- int *timep = &now->tm_sec;
- int carry_into, i;
-
- /* Ignore everything past the 6th element (tm_year). */
- for (i = 5; i > 0; i--) {
- if (alarmp[i] < 0)
- alarmp[i] = timep[i];
- else
- break;
- }
-
- /* No carry needed if all fields are valid. */
- if (i == 5)
- return;
-
- for (carry_into = i + 1; i >= 0; i--) {
- if (alarmp[i] < timep[i])
- break;
-
- if (alarmp[i] > timep[i])
- return;
- }
-
- switch (carry_into) {
- case 1:
- alarm->tm_min++;
-
- if (alarm->tm_min < 60)
- return;
-
- alarm->tm_min = 0;
- /* fall-through */
-
- case 2:
- alarm->tm_hour++;
-
- if (alarm->tm_hour < 60)
- return;
-
- alarm->tm_hour = 0;
- /* fall-through */
-
- case 3:
- alarm->tm_mday++;
-
- if (alarm->tm_mday <= rtc_days_in_month[alarm->tm_mon])
- return;
-
- alarm->tm_mday = 1;
- /* fall-through */
-
- case 4:
- alarm->tm_mon++;
-
- if (alarm->tm_mon <= 12)
- return;
-
- alarm->tm_mon = 1;
- /* fall-through */
-
- case 5:
- alarm->tm_year++;
- }
-}
-EXPORT_SYMBOL(rtc_merge_alarm);
-
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
new file mode 100644
index 00000000000..eee4ee5bb75
--- /dev/null
+++ b/drivers/rtc/rtc-max6900.c
@@ -0,0 +1,311 @@
+/*
+ * rtc class driver for the Maxim MAX6900 chip
+ *
+ * Author: Dale Farnsworth <dale@farnsworth.org>
+ *
+ * based on previously existing rtc class drivers
+ *
+ * 2007 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#define DRV_NAME "max6900"
+#define DRV_VERSION "0.1"
+
+/*
+ * register indices
+ */
+#define MAX6900_REG_SC 0 /* seconds 00-59 */
+#define MAX6900_REG_MN 1 /* minutes 00-59 */
+#define MAX6900_REG_HR 2 /* hours 00-23 */
+#define MAX6900_REG_DT 3 /* day of month 00-31 */
+#define MAX6900_REG_MO 4 /* month 01-12 */
+#define MAX6900_REG_DW 5 /* day of week 1-7 */
+#define MAX6900_REG_YR 6 /* year 00-99 */
+#define MAX6900_REG_CT 7 /* control */
+#define MAX6900_REG_LEN 8
+
+#define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */
+
+/*
+ * register read/write commands
+ */
+#define MAX6900_REG_CONTROL_WRITE 0x8e
+#define MAX6900_REG_BURST_READ 0xbf
+#define MAX6900_REG_BURST_WRITE 0xbe
+#define MAX6900_REG_RESERVED_READ 0x96
+
+#define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */
+
+#define MAX6900_I2C_ADDR 0xa0
+
+static unsigned short normal_i2c[] = {
+ MAX6900_I2C_ADDR >> 1,
+ I2C_CLIENT_END
+};
+
+I2C_CLIENT_INSMOD; /* defines addr_data */
+
+static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind);
+
+static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf)
+{
+ u8 reg_addr[1] = { MAX6900_REG_BURST_READ };
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = sizeof(reg_addr),
+ .buf = reg_addr
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = MAX6900_REG_LEN,
+ .buf = buf
+ }
+ };
+ int rc;
+
+ rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (rc != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: register read failed\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
+{
+ u8 i2c_buf[MAX6900_REG_LEN + 1] = { MAX6900_REG_BURST_WRITE };
+ struct i2c_msg msgs[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = MAX6900_REG_LEN + 1,
+ .buf = i2c_buf
+ }
+ };
+ int rc;
+
+ memcpy(&i2c_buf[1], buf, MAX6900_REG_LEN);
+
+ rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (rc != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: register write failed\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+ msleep(MAX6900_IDLE_TIME_AFTER_WRITE);
+ return 0;
+}
+
+static int max6900_i2c_validate_client(struct i2c_client *client)
+{
+ u8 regs[MAX6900_REG_LEN];
+ u8 zero_mask[MAX6900_REG_LEN] = {
+ 0x80, /* seconds */
+ 0x80, /* minutes */
+ 0x40, /* hours */
+ 0xc0, /* day of month */
+ 0xe0, /* month */
+ 0xf8, /* day of week */
+ 0x00, /* year */
+ 0x7f, /* control */
+ };
+ int i;
+ int rc;
+ int reserved;
+
+ reserved = i2c_smbus_read_byte_data(client, MAX6900_REG_RESERVED_READ);
+ if (reserved != 0x07)
+ return -ENODEV;
+
+ rc = max6900_i2c_read_regs(client, regs);
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < MAX6900_REG_LEN; ++i) {
+ if (regs[i] & zero_mask[i])
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ int rc;
+ u8 regs[MAX6900_REG_LEN];
+
+ rc = max6900_i2c_read_regs(client, regs);
+ if (rc < 0)
+ return rc;
+
+ tm->tm_sec = BCD2BIN(regs[MAX6900_REG_SC]);
+ tm->tm_min = BCD2BIN(regs[MAX6900_REG_MN]);
+ tm->tm_hour = BCD2BIN(regs[MAX6900_REG_HR] & 0x3f);
+ tm->tm_mday = BCD2BIN(regs[MAX6900_REG_DT]);
+ tm->tm_mon = BCD2BIN(regs[MAX6900_REG_MO]) - 1;
+ tm->tm_year = BCD2BIN(regs[MAX6900_REG_YR]) + 100;
+ tm->tm_wday = BCD2BIN(regs[MAX6900_REG_DW]);
+
+ return 0;
+}
+
+static int max6900_i2c_clear_write_protect(struct i2c_client *client)
+{
+ int rc;
+ rc = i2c_smbus_write_byte_data (client, MAX6900_REG_CONTROL_WRITE, 0);
+ if (rc < 0) {
+ dev_err(&client->dev, "%s: control register write failed\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int max6900_i2c_set_time(struct i2c_client *client,
+ struct rtc_time const *tm)
+{
+ u8 regs[MAX6900_REG_LEN];
+ int rc;
+
+ rc = max6900_i2c_clear_write_protect(client);
+ if (rc < 0)
+ return rc;
+
+ regs[MAX6900_REG_SC] = BIN2BCD(tm->tm_sec);
+ regs[MAX6900_REG_MN] = BIN2BCD(tm->tm_min);
+ regs[MAX6900_REG_HR] = BIN2BCD(tm->tm_hour);
+ regs[MAX6900_REG_DT] = BIN2BCD(tm->tm_mday);
+ regs[MAX6900_REG_MO] = BIN2BCD(tm->tm_mon + 1);
+ regs[MAX6900_REG_YR] = BIN2BCD(tm->tm_year - 100);
+ regs[MAX6900_REG_DW] = BIN2BCD(tm->tm_wday);
+ regs[MAX6900_REG_CT] = MAX6900_REG_CT_WP; /* set write protect */
+
+ rc = max6900_i2c_write_regs(client, regs);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6900_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6900_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static int max6900_attach_adapter(struct i2c_adapter *adapter)
+{
+ return i2c_probe(adapter, &addr_data, max6900_probe);
+}
+
+static int max6900_detach_client(struct i2c_client *client)
+{
+ struct rtc_device *const rtc = i2c_get_clientdata(client);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return i2c_detach_client(client);
+}
+
+static struct i2c_driver max6900_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .id = I2C_DRIVERID_MAX6900,
+ .attach_adapter = max6900_attach_adapter,
+ .detach_client = max6900_detach_client,
+};
+
+static const struct rtc_class_ops max6900_rtc_ops = {
+ .read_time = max6900_rtc_read_time,
+ .set_time = max6900_rtc_set_time,
+};
+
+static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind)
+{
+ int rc = 0;
+ struct i2c_client *client = NULL;
+ struct rtc_device *rtc = NULL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
+ rc = -ENODEV;
+ goto failout;
+ }
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == NULL) {
+ rc = -ENOMEM;
+ goto failout;
+ }
+
+ client->addr = addr;
+ client->adapter = adapter;
+ client->driver = &max6900_driver;
+ strlcpy(client->name, DRV_NAME, I2C_NAME_SIZE);
+
+ if (kind < 0) {
+ rc = max6900_i2c_validate_client(client);
+ if (rc < 0)
+ goto failout;
+ }
+
+ rc = i2c_attach_client(client);
+ if (rc < 0)
+ goto failout;
+
+ dev_info(&client->dev,
+ "chip found, driver version " DRV_VERSION "\n");
+
+ rtc = rtc_device_register(max6900_driver.driver.name,
+ &client->dev,
+ &max6900_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ rc = PTR_ERR(rtc);
+ goto failout_detach;
+ }
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+
+failout_detach:
+ i2c_detach_client(client);
+failout:
+ kfree(client);
+ return rc;
+}
+
+static int __init max6900_init(void)
+{
+ return i2c_add_driver(&max6900_driver);
+}
+
+static void __exit max6900_exit(void)
+{
+ i2c_del_driver(&max6900_driver);
+}
+
+MODULE_DESCRIPTION("Maxim MAX6900 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(max6900_init);
+module_exit(max6900_exit);
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 9de8d67f4f8..60a8a4bb8bd 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -124,7 +124,7 @@ static void rtc_wait_not_busy(void)
/* now we have ~15 usec to read/write various registers */
}
-static irqreturn_t rtc_irq(int irq, void *class_dev)
+static irqreturn_t rtc_irq(int irq, void *rtc)
{
unsigned long events = 0;
u8 irq_data;
@@ -141,7 +141,7 @@ static irqreturn_t rtc_irq(int irq, void *class_dev)
if (irq_data & OMAP_RTC_STATUS_1S_EVENT)
events |= RTC_IRQF | RTC_UF;
- rtc_update_irq(class_dev, 1, events);
+ rtc_update_irq(rtc, 1, events);
return IRQ_HANDLED;
}
@@ -289,34 +289,6 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
u8 reg;
- /* Much userspace code uses RTC_ALM_SET, thus "don't care" for
- * day/month/year specifies alarms up to 24 hours in the future.
- * So we need to handle that ... but let's ignore the "don't care"
- * values for hours/minutes/seconds.
- */
- if (alm->time.tm_mday <= 0
- && alm->time.tm_mon < 0
- && alm->time.tm_year < 0) {
- struct rtc_time tm;
- unsigned long now, then;
-
- omap_rtc_read_time(dev, &tm);
- rtc_tm_to_time(&tm, &now);
-
- alm->time.tm_mday = tm.tm_mday;
- alm->time.tm_mon = tm.tm_mon;
- alm->time.tm_year = tm.tm_year;
- rtc_tm_to_time(&alm->time, &then);
-
- /* sometimes the alarm wraps into tomorrow */
- if (then < now) {
- rtc_time_to_tm(now + 24 * 60 * 60, &tm);
- alm->time.tm_mday = tm.tm_mday;
- alm->time.tm_mon = tm.tm_mon;
- alm->time.tm_year = tm.tm_year;
- }
- }
-
if (tm2bcd(&alm->time) < 0)
return -EINVAL;
@@ -399,7 +371,7 @@ static int __devinit omap_rtc_probe(struct platform_device *pdev)
goto fail;
}
platform_set_drvdata(pdev, rtc);
- class_set_devdata(&rtc->class_dev, mem);
+ dev_set_devdata(&rtc->dev, mem);
/* clear pending irqs, and set 1/second periodic,
* which we'll use instead of update irqs
@@ -418,13 +390,13 @@ static int __devinit omap_rtc_probe(struct platform_device *pdev)
/* handle periodic and alarm irqs */
if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,
- rtc->class_dev.class_id, &rtc->class_dev)) {
+ rtc->dev.bus_id, rtc)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_timer);
goto fail0;
}
if (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
- rtc->class_dev.class_id, &rtc->class_dev)) {
+ rtc->dev.bus_id, rtc)) {
pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_alarm);
goto fail1;
@@ -481,26 +453,17 @@ static int __devexit omap_rtc_remove(struct platform_device *pdev)
free_irq(omap_rtc_timer, rtc);
free_irq(omap_rtc_alarm, rtc);
- release_resource(class_get_devdata(&rtc->class_dev));
+ release_resource(dev_get_devdata(&rtc->dev));
rtc_device_unregister(rtc);
return 0;
}
#ifdef CONFIG_PM
-static struct timespec rtc_delta;
static u8 irqstat;
static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct rtc_time rtc_tm;
- struct timespec time;
-
- time.tv_nsec = 0;
- omap_rtc_read_time(NULL, &rtc_tm);
- rtc_tm_to_time(&rtc_tm, &time.tv_sec);
-
- save_time_delta(&rtc_delta, &time);
irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);
/* FIXME the RTC alarm is not currently acting as a wakeup event
@@ -517,14 +480,6 @@ static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
static int omap_rtc_resume(struct platform_device *pdev)
{
- struct rtc_time rtc_tm;
- struct timespec time;
-
- time.tv_nsec = 0;
- omap_rtc_read_time(NULL, &rtc_tm);
- rtc_tm_to_time(&rtc_tm, &time.tv_sec);
-
- restore_time_delta(&rtc_delta, &time);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(omap_rtc_alarm);
else
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index f13daa9feca..e4bf68ca96f 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -51,7 +51,7 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)
{
struct rtc_device *rtc = dev_id;
- rtc_update_irq(&rtc->class_dev, 1, RTC_AF);
+ rtc_update_irq(rtc, 1, RTC_AF);
return IRQ_HANDLED;
}
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
index 1bd624fc685..8d300e6d0d9 100644
--- a/drivers/rtc/rtc-proc.c
+++ b/drivers/rtc/rtc-proc.c
@@ -16,18 +16,18 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-static struct class_device *rtc_dev = NULL;
-static DEFINE_MUTEX(rtc_lock);
+#include "rtc-core.h"
+
static int rtc_proc_show(struct seq_file *seq, void *offset)
{
int err;
- struct class_device *class_dev = seq->private;
- const struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+ struct rtc_device *rtc = seq->private;
+ const struct rtc_class_ops *ops = rtc->ops;
struct rtc_wkalrm alrm;
struct rtc_time tm;
- err = rtc_read_time(class_dev, &tm);
+ err = rtc_read_time(rtc, &tm);
if (err == 0) {
seq_printf(seq,
"rtc_time\t: %02d:%02d:%02d\n"
@@ -36,7 +36,7 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
}
- err = rtc_read_alarm(class_dev, &alrm);
+ err = rtc_read_alarm(rtc, &alrm);
if (err == 0) {
seq_printf(seq, "alrm_time\t: ");
if ((unsigned int)alrm.time.tm_hour <= 24)
@@ -74,19 +74,19 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)
seq_printf(seq, "24hr\t\t: yes\n");
if (ops->proc)
- ops->proc(class_dev->dev, seq);
+ ops->proc(rtc->dev.parent, seq);
return 0;
}
static int rtc_proc_open(struct inode *inode, struct file *file)
{
- struct class_device *class_dev = PDE(inode)->data;
+ struct rtc_device *rtc = PDE(inode)->data;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
- return single_open(file, rtc_proc_show, class_dev);
+ return single_open(file, rtc_proc_show, rtc);
}
static int rtc_proc_release(struct inode *inode, struct file *file)
@@ -103,62 +103,22 @@ static const struct file_operations rtc_proc_fops = {
.release = rtc_proc_release,
};
-static int rtc_proc_add_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_proc_add_device(struct rtc_device *rtc)
{
- mutex_lock(&rtc_lock);
- if (rtc_dev == NULL) {
+ if (rtc->id == 0) {
struct proc_dir_entry *ent;
- rtc_dev = class_dev;
-
ent = create_proc_entry("driver/rtc", 0, NULL);
if (ent) {
- struct rtc_device *rtc = to_rtc_device(class_dev);
-
ent->proc_fops = &rtc_proc_fops;
ent->owner = rtc->owner;
- ent->data = class_dev;
-
- dev_dbg(class_dev->dev, "rtc intf: proc\n");
+ ent->data = rtc;
}
- else
- rtc_dev = NULL;
}
- mutex_unlock(&rtc_lock);
-
- return 0;
}
-static void rtc_proc_remove_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_proc_del_device(struct rtc_device *rtc)
{
- mutex_lock(&rtc_lock);
- if (rtc_dev == class_dev) {
+ if (rtc->id == 0)
remove_proc_entry("driver/rtc", NULL);
- rtc_dev = NULL;
- }
- mutex_unlock(&rtc_lock);
-}
-
-static struct class_interface rtc_proc_interface = {
- .add = &rtc_proc_add_device,
- .remove = &rtc_proc_remove_device,
-};
-
-static int __init rtc_proc_init(void)
-{
- return rtc_interface_register(&rtc_proc_interface);
}
-
-static void __exit rtc_proc_exit(void)
-{
- class_interface_unregister(&rtc_proc_interface);
-}
-
-subsys_initcall(rtc_proc_init);
-module_exit(rtc_proc_exit);
-
-MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
-MODULE_DESCRIPTION("RTC class proc interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c
new file mode 100644
index 00000000000..9d6de371495
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c313.c
@@ -0,0 +1,405 @@
+/*
+ * Ricoh RS5C313 RTC device/driver
+ * Copyright (C) 2007 Nobuhiro Iwamatsu
+ *
+ * 2005-09-19 modifed by kogiidena
+ *
+ * Based on the old drivers/char/rs5c313_rtc.c by:
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ *
+ * Based on code written by Paul Gortmaker.
+ * Copyright (C) 1996 Paul Gortmaker
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Based on other minimal char device drivers, like Alan's
+ * watchdog, Ted's random, etc. etc.
+ *
+ * 1.07 Paul Gortmaker.
+ * 1.08 Miquel van Smoorenburg: disallow certain things on the
+ * DEC Alpha as the CMOS clock is also used for other things.
+ * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup.
+ * 1.09a Pete Zaitcev: Sun SPARC
+ * 1.09b Jeff Garzik: Modularize, init cleanup
+ * 1.09c Jeff Garzik: SMP cleanup
+ * 1.10 Paul Barton-Davis: add support for async I/O
+ * 1.10a Andrea Arcangeli: Alpha updates
+ * 1.10b Andrew Morton: SMP lock fix
+ * 1.10c Cesar Barros: SMP locking fixes and cleanup
+ * 1.10d Paul Gortmaker: delete paranoia check in rtc_exit
+ * 1.10e Maciej W. Rozycki: Handle DECstation's year weirdness.
+ * 1.11 Takashi Iwai: Kernel access functions
+ * rtc_register/rtc_unregister/rtc_control
+ * 1.11a Daniele Bellucci: Audit create_proc_read_entry in rtc_init
+ * 1.12 Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer
+ * CONFIG_HPET_EMULATE_RTC
+ * 1.13 Nobuhiro Iwamatsu: Updata driver.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#define DRV_NAME "rs5c313"
+#define DRV_VERSION "1.13"
+
+#ifdef CONFIG_SH_LANDISK
+/*****************************************************/
+/* LANDISK dependence part of RS5C313 */
+/*****************************************************/
+
+#define SCSMR1 0xFFE00000
+#define SCSCR1 0xFFE00008
+#define SCSMR1_CA 0x80
+#define SCSCR1_CKE 0x03
+#define SCSPTR1 0xFFE0001C
+#define SCSPTR1_EIO 0x80
+#define SCSPTR1_SPB1IO 0x08
+#define SCSPTR1_SPB1DT 0x04
+#define SCSPTR1_SPB0IO 0x02
+#define SCSPTR1_SPB0DT 0x01
+
+#define SDA_OEN SCSPTR1_SPB1IO
+#define SDA SCSPTR1_SPB1DT
+#define SCL_OEN SCSPTR1_SPB0IO
+#define SCL SCSPTR1_SPB0DT
+
+/* RICOH RS5C313 CE port */
+#define RS5C313_CE 0xB0000003
+
+/* RICOH RS5C313 CE port bit */
+#define RS5C313_CE_RTCCE 0x02
+
+/* SCSPTR1 data */
+unsigned char scsptr1_data;
+
+#define RS5C313_CEENABLE ctrl_outb(RS5C313_CE_RTCCE, RS5C313_CE);
+#define RS5C313_CEDISABLE ctrl_outb(0x00, RS5C313_CE)
+#define RS5C313_MISCOP ctrl_outb(0x02, 0xB0000008)
+
+static void rs5c313_init_port(void)
+{
+ /* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */
+ ctrl_outb(ctrl_inb(SCSMR1) & ~SCSMR1_CA, SCSMR1);
+ ctrl_outb(ctrl_inb(SCSCR1) & ~SCSCR1_CKE, SCSCR1);
+
+ /* And Initialize SCL for RS5C313 clock */
+ scsptr1_data = ctrl_inb(SCSPTR1) | SCL; /* SCL:H */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ scsptr1_data = ctrl_inb(SCSPTR1) | SCL_OEN; /* SCL output enable */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ RS5C313_CEDISABLE; /* CE:L */
+}
+
+static void rs5c313_write_data(unsigned char data)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ /* SDA:Write Data */
+ scsptr1_data = (scsptr1_data & ~SDA) |
+ ((((0x80 >> i) & data) >> (7 - i)) << 2);
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ if (i == 0) {
+ scsptr1_data |= SDA_OEN; /* SDA:output enable */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ }
+ ndelay(700);
+ scsptr1_data &= ~SCL; /* SCL:L */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ ndelay(700);
+ scsptr1_data |= SCL; /* SCL:H */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ }
+
+ scsptr1_data &= ~SDA_OEN; /* SDA:output disable */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+}
+
+static unsigned char rs5c313_read_data(void)
+{
+ int i;
+ unsigned char data;
+
+ for (i = 0; i < 8; i++) {
+ ndelay(700);
+ /* SDA:Read Data */
+ data |= ((ctrl_inb(SCSPTR1) & SDA) >> 2) << (7 - i);
+ scsptr1_data &= ~SCL; /* SCL:L */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ ndelay(700);
+ scsptr1_data |= SCL; /* SCL:H */
+ ctrl_outb(scsptr1_data, SCSPTR1);
+ }
+ return data & 0x0F;
+}
+
+#endif /* CONFIG_SH_LANDISK */
+
+/*****************************************************/
+/* machine independence part of RS5C313 */
+/*****************************************************/
+
+/* RICOH RS5C313 address */
+#define RS5C313_ADDR_SEC 0x00
+#define RS5C313_ADDR_SEC10 0x01
+#define RS5C313_ADDR_MIN 0x02
+#define RS5C313_ADDR_MIN10 0x03
+#define RS5C313_ADDR_HOUR 0x04
+#define RS5C313_ADDR_HOUR10 0x05
+#define RS5C313_ADDR_WEEK 0x06
+#define RS5C313_ADDR_INTINTVREG 0x07
+#define RS5C313_ADDR_DAY 0x08
+#define RS5C313_ADDR_DAY10 0x09
+#define RS5C313_ADDR_MON 0x0A
+#define RS5C313_ADDR_MON10 0x0B
+#define RS5C313_ADDR_YEAR 0x0C
+#define RS5C313_ADDR_YEAR10 0x0D
+#define RS5C313_ADDR_CNTREG 0x0E
+#define RS5C313_ADDR_TESTREG 0x0F
+
+/* RICOH RS5C313 control register */
+#define RS5C313_CNTREG_ADJ_BSY 0x01
+#define RS5C313_CNTREG_WTEN_XSTP 0x02
+#define RS5C313_CNTREG_12_24 0x04
+#define RS5C313_CNTREG_CTFG 0x08
+
+/* RICOH RS5C313 test register */
+#define RS5C313_TESTREG_TEST 0x01
+
+/* RICOH RS5C313 control bit */
+#define RS5C313_CNTBIT_READ 0x40
+#define RS5C313_CNTBIT_AD 0x20
+#define RS5C313_CNTBIT_DT 0x10
+
+static unsigned char rs5c313_read_reg(unsigned char addr)
+{
+
+ rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD);
+ return rs5c313_read_data();
+}
+
+static void rs5c313_write_reg(unsigned char addr, unsigned char data)
+{
+ data &= 0x0f;
+ rs5c313_write_data(addr | RS5C313_CNTBIT_AD);
+ rs5c313_write_data(data | RS5C313_CNTBIT_DT);
+ return;
+}
+
+static inline unsigned char rs5c313_read_cntreg(unsigned char addr)
+{
+ return rs5c313_read_reg(RS5C313_ADDR_CNTREG);
+}
+
+static inline void rs5c313_write_cntreg(unsigned char data)
+{
+ rs5c313_write_reg(RS5C313_ADDR_CNTREG, data);
+}
+
+static inline void rs5c313_write_intintvreg(unsigned char data)
+{
+ rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data);
+}
+
+static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int data;
+
+ while (1) {
+ RS5C313_CEENABLE; /* CE:H */
+
+ /* Initialize control reg. 24 hour */
+ rs5c313_write_cntreg(0x04);
+
+ if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
+ break;
+
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+
+ }
+
+ data = rs5c313_read_reg(RS5C313_ADDR_SEC);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4);
+ tm->tm_sec = BCD2BIN(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_MIN);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4);
+ tm->tm_min = BCD2BIN(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_HOUR);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4);
+ tm->tm_hour = BCD2BIN(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_DAY);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4);
+ tm->tm_mday = BCD2BIN(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_MON);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4);
+ tm->tm_mon = BCD2BIN(data) - 1;
+
+ data = rs5c313_read_reg(RS5C313_ADDR_YEAR);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4);
+ tm->tm_year = BCD2BIN(data);
+
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ data = rs5c313_read_reg(RS5C313_ADDR_WEEK);
+ tm->tm_wday = BCD2BIN(data);
+
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+
+ return 0;
+}
+
+static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int data;
+
+ /* busy check. */
+ while (1) {
+ RS5C313_CEENABLE; /* CE:H */
+
+ /* Initiatlize control reg. 24 hour */
+ rs5c313_write_cntreg(0x04);
+
+ if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
+ break;
+ RS5C313_MISCOP;
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+ }
+
+ data = BIN2BCD(tm->tm_sec);
+ rs5c313_write_reg(RS5C313_ADDR_SEC, data);
+ rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4));
+
+ data = BIN2BCD(tm->tm_min);
+ rs5c313_write_reg(RS5C313_ADDR_MIN, data );
+ rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4));
+
+ data = BIN2BCD(tm->tm_hour);
+ rs5c313_write_reg(RS5C313_ADDR_HOUR, data);
+ rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4));
+
+ data = BIN2BCD(tm->tm_mday);
+ rs5c313_write_reg(RS5C313_ADDR_DAY, data);
+ rs5c313_write_reg(RS5C313_ADDR_DAY10, (data>> 4));
+
+ data = BIN2BCD(tm->tm_mon + 1);
+ rs5c313_write_reg(RS5C313_ADDR_MON, data);
+ rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4));
+
+ data = BIN2BCD(tm->tm_year % 100);
+ rs5c313_write_reg(RS5C313_ADDR_YEAR, data);
+ rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4));
+
+ data = BIN2BCD(tm->tm_wday);
+ rs5c313_write_reg(RS5C313_ADDR_WEEK, data);
+
+ RS5C313_CEDISABLE; /* CE:H */
+ ndelay(700);
+
+ return 0;
+}
+
+static void rs5c313_check_xstp_bit(void)
+{
+ struct rtc_time tm;
+
+ RS5C313_CEENABLE; /* CE:H */
+ if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) {
+ /* INT interval reg. OFF */
+ rs5c313_write_intintvreg(0x00);
+ /* Initialize control reg. 24 hour & adjust */
+ rs5c313_write_cntreg(0x07);
+
+ /* busy check. */
+ while (rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY)
+ RS5C313_MISCOP;
+
+ memset(&tm, 0, sizeof(struct rtc_time));
+ tm.tm_mday = 1;
+ tm.tm_mon = 1;
+
+ rs5c313_rtc_set_time(NULL, &tm);
+ printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to "
+ "1 Jan 2000\n");
+ }
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+}
+
+static const struct rtc_class_ops rs5c313_rtc_ops = {
+ .read_time = rs5c313_rtc_read_time,
+ .set_time = rs5c313_rtc_set_time,
+};
+
+static int rs5c313_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = rtc_device_register("rs5c313", &pdev->dev,
+ &rs5c313_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+
+ return err;
+}
+
+static int __devexit rs5c313_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata( pdev );
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver rs5c313_rtc_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = rs5c313_rtc_probe,
+ .remove = __devexit_p( rs5c313_rtc_remove ),
+};
+
+static int __init rs5c313_rtc_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&rs5c313_rtc_platform_driver);
+ if (err)
+ return err;
+
+ rs5c313_init_port();
+ rs5c313_check_xstp_bit();
+
+ return 0;
+}
+
+static void __exit rs5c313_rtc_exit(void)
+{
+ platform_driver_unregister( &rs5c313_rtc_platform_driver );
+}
+
+module_init(rs5c313_rtc_init);
+module_exit(rs5c313_rtc_exit);
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
+MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 9a79a24a748..54b61305346 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -50,7 +50,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
{
struct rtc_device *rdev = id;
- rtc_update_irq(&rdev->class_dev, 1, RTC_AF | RTC_IRQF);
+ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
return IRQ_HANDLED;
}
@@ -58,7 +58,7 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
{
struct rtc_device *rdev = id;
- rtc_update_irq(&rdev->class_dev, tick_count++, RTC_PF | RTC_IRQF);
+ rtc_update_irq(rdev, tick_count++, RTC_PF | RTC_IRQF);
return IRQ_HANDLED;
}
@@ -548,37 +548,15 @@ static int ticnt_save;
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
/* save TICNT for anyone using periodic interrupts */
-
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
-
- /* calculate time delta for suspend */
-
- s3c_rtc_gettime(&pdev->dev, &tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- save_time_delta(&s3c_rtc_delta, &time);
s3c_rtc_enable(pdev, 0);
-
return 0;
}
static int s3c_rtc_resume(struct platform_device *pdev)
{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
s3c_rtc_enable(pdev, 1);
- s3c_rtc_gettime(&pdev->dev, &tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- restore_time_delta(&s3c_rtc_delta, &time);
-
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
return 0;
}
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 677bae820dc..0918b787c4d 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -93,7 +93,7 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
if (rtsr & RTSR_HZ)
events |= RTC_UF | RTC_IRQF;
- rtc_update_irq(&rtc->class_dev, 1, events);
+ rtc_update_irq(rtc, 1, events);
if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
rtc_update_alarm(&rtc_alarm);
@@ -119,7 +119,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)
*/
OSSR = OSSR_M1; /* clear match on timer1 */
- rtc_update_irq(&rtc->class_dev, rtc_timer1_count, RTC_PF | RTC_IRQF);
+ rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);
if (rtc_timer1_count == 1)
rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2)));
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 198b9f22fbf..6abf4811958 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -104,7 +104,7 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
writeb(tmp, rtc->regbase + RCR1);
- rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
+ rtc_update_irq(&rtc->rtc_dev, 1, events);
spin_unlock(&rtc->lock);
@@ -139,7 +139,7 @@ static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
rtc->rearm_aie = 1;
- rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
+ rtc_update_irq(&rtc->rtc_dev, 1, events);
}
spin_unlock(&rtc->lock);
@@ -153,7 +153,7 @@ static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
spin_lock(&rtc->lock);
- rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF);
+ rtc_update_irq(&rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);
spin_unlock(&rtc->lock);
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 899ab8c514f..69df94b4484 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -12,20 +12,26 @@
#include <linux/module.h>
#include <linux/rtc.h>
+#include "rtc-core.h"
+
+
/* device attributes */
-static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf)
+static ssize_t
+rtc_sysfs_show_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
}
-static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL);
-static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
+static ssize_t
+rtc_sysfs_show_date(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
ssize_t retval;
struct rtc_time tm;
- retval = rtc_read_time(dev, &tm);
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
if (retval == 0) {
retval = sprintf(buf, "%04d-%02d-%02d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
@@ -33,14 +39,15 @@ static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
return retval;
}
-static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL);
-static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
+static ssize_t
+rtc_sysfs_show_time(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
ssize_t retval;
struct rtc_time tm;
- retval = rtc_read_time(dev, &tm);
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
if (retval == 0) {
retval = sprintf(buf, "%02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
@@ -48,14 +55,15 @@ static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
return retval;
}
-static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL);
-static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
+static ssize_t
+rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
ssize_t retval;
struct rtc_time tm;
- retval = rtc_read_time(dev, &tm);
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
if (retval == 0) {
unsigned long time;
rtc_tm_to_time(&tm, &time);
@@ -64,23 +72,18 @@ static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
return retval;
}
-static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
-
-static struct attribute *rtc_attrs[] = {
- &class_device_attr_name.attr,
- &class_device_attr_date.attr,
- &class_device_attr_time.attr,
- &class_device_attr_since_epoch.attr,
- NULL,
-};
-static struct attribute_group rtc_attr_group = {
- .attrs = rtc_attrs,
+static struct device_attribute rtc_attrs[] = {
+ __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
+ __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
+ __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
+ __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
+ { },
};
-
static ssize_t
-rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
+rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
ssize_t retval;
unsigned long alarm;
@@ -94,7 +97,7 @@ rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
* REVISIT maybe we should require RTC implementations to
* disable the RTC alarm after it triggers, for uniformity.
*/
- retval = rtc_read_alarm(dev, &alm);
+ retval = rtc_read_alarm(to_rtc_device(dev), &alm);
if (retval == 0 && alm.enabled) {
rtc_tm_to_time(&alm.time, &alarm);
retval = sprintf(buf, "%lu\n", alarm);
@@ -104,16 +107,18 @@ rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
}
static ssize_t
-rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
+rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
{
ssize_t retval;
unsigned long now, alarm;
struct rtc_wkalrm alm;
+ struct rtc_device *rtc = to_rtc_device(dev);
/* Only request alarms that trigger in the future. Disable them
* by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
*/
- retval = rtc_read_time(dev, &alm.time);
+ retval = rtc_read_time(rtc, &alm.time);
if (retval < 0)
return retval;
rtc_tm_to_time(&alm.time, &now);
@@ -124,7 +129,7 @@ rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
* entirely prevent that here, without even the minimal
* locking from the /dev/rtcN api.
*/
- retval = rtc_read_alarm(dev, &alm);
+ retval = rtc_read_alarm(rtc, &alm);
if (retval < 0)
return retval;
if (alm.enabled)
@@ -141,10 +146,10 @@ rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
}
rtc_time_to_tm(alarm, &alm.time);
- retval = rtc_set_alarm(dev, &alm);
+ retval = rtc_set_alarm(rtc, &alm);
return (retval < 0) ? retval : n;
}
-static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
@@ -153,71 +158,37 @@ static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
* suspend-to-disk. So: no attribute unless that side effect is possible.
* (Userspace may disable that mechanism later.)
*/
-static inline int rtc_does_wakealarm(struct class_device *class_dev)
+static inline int rtc_does_wakealarm(struct rtc_device *rtc)
{
- struct rtc_device *rtc;
-
- if (!device_can_wakeup(class_dev->dev))
+ if (!device_can_wakeup(rtc->dev.parent))
return 0;
- rtc = to_rtc_device(class_dev);
return rtc->ops->set_alarm != NULL;
}
-static int rtc_sysfs_add_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_sysfs_add_device(struct rtc_device *rtc)
{
int err;
- dev_dbg(class_dev->dev, "rtc intf: sysfs\n");
+ /* not all RTCs support both alarms and wakeup */
+ if (!rtc_does_wakealarm(rtc))
+ return;
- err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group);
+ err = device_create_file(&rtc->dev, &dev_attr_wakealarm);
if (err)
- dev_err(class_dev->dev, "failed to create %s\n",
- "sysfs attributes");
- else if (rtc_does_wakealarm(class_dev)) {
- /* not all RTCs support both alarms and wakeup */
- err = class_device_create_file(class_dev,
- &class_device_attr_wakealarm);
- if (err) {
- dev_err(class_dev->dev, "failed to create %s\n",
- "alarm attribute");
- sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
- }
- }
-
- return err;
+ dev_err(rtc->dev.parent, "failed to create "
+ "alarm attribute, %d",
+ err);
}
-static void rtc_sysfs_remove_device(struct class_device *class_dev,
- struct class_interface *class_intf)
+void rtc_sysfs_del_device(struct rtc_device *rtc)
{
- if (rtc_does_wakealarm(class_dev))
- class_device_remove_file(class_dev,
- &class_device_attr_wakealarm);
- sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
+ /* REVISIT did we add it successfully? */
+ if (rtc_does_wakealarm(rtc))
+ device_remove_file(&rtc->dev, &dev_attr_wakealarm);
}
-/* interface registration */
-
-static struct class_interface rtc_sysfs_interface = {
- .add = &rtc_sysfs_add_device,
- .remove = &rtc_sysfs_remove_device,
-};
-
-static int __init rtc_sysfs_init(void)
+void __init rtc_sysfs_init(struct class *rtc_class)
{
- return rtc_interface_register(&rtc_sysfs_interface);
+ rtc_class->dev_attrs = rtc_attrs;
}
-
-static void __exit rtc_sysfs_exit(void)
-{
- class_interface_unregister(&rtc_sysfs_interface);
-}
-
-subsys_initcall(rtc_sysfs_init);
-module_exit(rtc_sysfs_exit);
-
-MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
-MODULE_DESCRIPTION("RTC class sysfs interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index f50a1b8e160..254c9fce27d 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -101,11 +101,11 @@ static ssize_t test_irq_store(struct device *dev,
retval = count;
local_irq_disable();
if (strncmp(buf, "tick", 4) == 0)
- rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
+ rtc_update_irq(rtc, 1, RTC_PF | RTC_IRQF);
else if (strncmp(buf, "alarm", 5) == 0)
- rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF);
+ rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
else if (strncmp(buf, "update", 6) == 0)
- rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
+ rtc_update_irq(rtc, 1, RTC_UF | RTC_IRQF);
else
retval = -EINVAL;
local_irq_enable();
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index e40322b7193..af7596ef29e 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -97,6 +97,7 @@ static DEFINE_SPINLOCK(rtc_lock);
static char rtc_name[] = "RTC";
static unsigned long periodic_frequency;
static unsigned long periodic_count;
+static unsigned int alarm_enabled;
struct resource rtc_resource[2] = {
{ .name = rtc_name,
@@ -188,6 +189,7 @@ static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
low = rtc1_read(ECMPLREG);
mid = rtc1_read(ECMPMREG);
high = rtc1_read(ECMPHREG);
+ wkalrm->enabled = alarm_enabled;
spin_unlock_irq(&rtc_lock);
@@ -206,10 +208,18 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
spin_lock_irq(&rtc_lock);
+ if (alarm_enabled)
+ disable_irq(ELAPSEDTIME_IRQ);
+
rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));
rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));
rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17));
+ if (wkalrm->enabled)
+ enable_irq(ELAPSEDTIME_IRQ);
+
+ alarm_enabled = wkalrm->enabled;
+
spin_unlock_irq(&rtc_lock);
return 0;
@@ -221,10 +231,24 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long
switch (cmd) {
case RTC_AIE_ON:
- enable_irq(ELAPSEDTIME_IRQ);
+ spin_lock_irq(&rtc_lock);
+
+ if (!alarm_enabled) {
+ enable_irq(ELAPSEDTIME_IRQ);
+ alarm_enabled = 1;
+ }
+
+ spin_unlock_irq(&rtc_lock);
break;
case RTC_AIE_OFF:
- disable_irq(ELAPSEDTIME_IRQ);
+ spin_lock_irq(&rtc_lock);
+
+ if (alarm_enabled) {
+ disable_irq(ELAPSEDTIME_IRQ);
+ alarm_enabled = 0;
+ }
+
+ spin_unlock_irq(&rtc_lock);
break;
case RTC_PIE_ON:
enable_irq(RTCLONG1_IRQ);
@@ -275,7 +299,7 @@ static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id)
rtc2_write(RTCINTREG, ELAPSEDTIME_INT);
- rtc_update_irq(&rtc->class_dev, 1, RTC_AF);
+ rtc_update_irq(rtc, 1, RTC_AF);
return IRQ_HANDLED;
}
@@ -291,7 +315,7 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
rtc1_write(RTCL1LREG, count);
rtc1_write(RTCL1HREG, count >> 16);
- rtc_update_irq(&rtc->class_dev, 1, RTC_PF);
+ rtc_update_irq(rtc, 1, RTC_PF);
return IRQ_HANDLED;
}