summaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig119
-rw-r--r--drivers/rtc/Makefile10
-rw-r--r--drivers/rtc/class.c3
-rw-r--r--drivers/rtc/interface.c22
-rw-r--r--drivers/rtc/rtc-at91.c407
-rw-r--r--drivers/rtc/rtc-dev.c131
-rw-r--r--drivers/rtc/rtc-ds1307.c388
-rw-r--r--drivers/rtc/rtc-ds1553.c414
-rw-r--r--drivers/rtc/rtc-ds1742.c259
-rw-r--r--drivers/rtc/rtc-lib.c19
-rw-r--r--drivers/rtc/rtc-max6902.c286
-rw-r--r--drivers/rtc/rtc-pcf8583.c394
-rw-r--r--drivers/rtc/rtc-pl031.c233
-rw-r--r--drivers/rtc/rtc-rs5c348.c246
-rw-r--r--drivers/rtc/rtc-s3c.c607
-rw-r--r--drivers/rtc/rtc-sa1100.c12
-rw-r--r--drivers/rtc/rtc-v3020.c264
-rw-r--r--drivers/rtc/rtc-vr41xx.c14
18 files changed, 3785 insertions, 43 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 65d090dbef4..f5b9f187a93 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -15,7 +15,7 @@ config RTC_CLASS
help
Generic RTC class support. If you say yes here, you will
be allowed to plug one or more RTCs to your system. You will
- probably want to enable one of more of the interfaces below.
+ probably want to enable one or more of the interfaces below.
This driver can also be built as a module. If so, the module
will be called rtc-class.
@@ -73,6 +73,13 @@ config RTC_INTF_DEV
This driver can also be built as a module. If so, the module
will be called rtc-dev.
+config RTC_INTF_DEV_UIE_EMUL
+ bool "RTC UIE emulation on dev interface"
+ depends on RTC_INTF_DEV
+ help
+ Provides an emulation for RTC_UIE if the underlaying rtc chip
+ driver did not provide RTC_UIE ioctls.
+
comment "RTC drivers"
depends on RTC_CLASS
@@ -86,6 +93,34 @@ config RTC_DRV_X1205
This driver can also be built as a module. If so, the module
will be called rtc-x1205.
+config RTC_DRV_DS1307
+ tristate "Dallas/Maxim DS1307 and similar I2C RTC chips"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for various compatible RTC
+ chips (often with battery backup) connected with I2C. This driver
+ should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
+ and probably other chips. In some cases the RTC must already
+ have been initialized (by manufacturing or a bootloader).
+
+ The first seven registers on these chips hold an RTC, and other
+ registers may add features such as NVRAM, a trickle charger for
+ the RTC/NVRAM backup power, and alarms. This driver may not
+ expose all those available chip features.
+
+ 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
+ help
+ If you say yes here you get support for the
+ Dallas DS1553 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1553.
+
config RTC_DRV_DS1672
tristate "Dallas/Maxim DS1672"
depends on RTC_CLASS && I2C
@@ -96,6 +131,16 @@ config RTC_DRV_DS1672
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS1742
+ tristate "Dallas DS1742"
+ depends on RTC_CLASS
+ help
+ If you say yes here you get support for the
+ Dallas DS1742 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1742.
+
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
depends on RTC_CLASS && I2C
@@ -107,6 +152,26 @@ config RTC_DRV_PCF8563
This driver can also be built as a module. If so, the module
will be called rtc-pcf8563.
+config RTC_DRV_PCF8583
+ tristate "Philips PCF8583"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for the
+ Philips PCF8583 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf8583.
+
+config RTC_DRV_RS5C348
+ tristate "Ricoh RS5C348A/B"
+ depends on RTC_CLASS && SPI
+ help
+ If you say yes here you get support for the
+ Ricoh RS5C348A and RS5C348B RTC chips.
+
+ 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
@@ -117,6 +182,22 @@ config RTC_DRV_RS5C372
This driver can also be built as a module. If so, the module
will be called rtc-rs5c372.
+config RTC_DRV_S3C
+ tristate "Samsung S3C series SoC RTC"
+ depends on RTC_CLASS && ARCH_S3C2410
+ help
+ RTC (Realtime Clock) driver for the clock inbuilt into the
+ Samsung S3C24XX series of SoCs. This can provide periodic
+ interrupt rates from 1Hz to 64Hz for user programs, and
+ wakeup from Alarm.
+
+ The driver currently supports the common features on all the
+ S3C24XX range, such as the S3C2410, S3C2412, S3C2413, S3C2440
+ and S3C2442.
+
+ 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
@@ -157,6 +238,22 @@ config RTC_DRV_VR41XX
To compile this driver as a module, choose M here: the
module will be called rtc-vr41xx.
+config RTC_DRV_PL031
+ tristate "ARM AMBA PL031 RTC"
+ 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.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-pl031.
+
+config RTC_DRV_AT91
+ tristate "AT91RM9200"
+ depends on RTC_CLASS && ARCH_AT91RM9200
+ help
+ Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock).
+
config RTC_DRV_TEST
tristate "Test driver/device"
depends on RTC_CLASS
@@ -172,4 +269,24 @@ config RTC_DRV_TEST
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.
+
endmenu
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index a9ca0f17168..54220714ff4 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -13,10 +13,20 @@ obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
+obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
+obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
+obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+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_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_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
+obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 413c7d54ea1..1cb61a761cb 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -69,6 +69,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc->id = id;
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;
@@ -93,7 +94,9 @@ exit_kfree:
kfree(rtc);
exit_idr:
+ mutex_lock(&idr_lock);
idr_remove(&rtc_idr, id);
+ mutex_unlock(&idr_lock);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 56e490709b8..579cd667b16 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -229,6 +229,9 @@ int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int
unsigned long flags;
struct rtc_device *rtc = to_rtc_device(class_dev);
+ if (rtc->ops->irq_set_state == NULL)
+ return -ENXIO;
+
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != task)
err = -ENXIO;
@@ -243,25 +246,12 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_state);
int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
{
- int err = 0, tmp = 0;
+ int err = 0;
unsigned long flags;
struct rtc_device *rtc = to_rtc_device(class_dev);
- /* allowed range is 2-8192 */
- if (freq < 2 || freq > 8192)
- return -EINVAL;
-/*
- FIXME: this does not belong here, will move where appropriate
- at a later stage. It cannot hurt right now, trust me :)
- if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE)))
- return -EACCES;
-*/
- /* check if freq is a power of 2 */
- while (freq > (1 << tmp))
- tmp++;
-
- if (freq != (1 << tmp))
- return -EINVAL;
+ if (rtc->ops->irq_set_freq == NULL)
+ return -ENXIO;
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != task)
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91.c
new file mode 100644
index 00000000000..dfd0ce86f6a
--- /dev/null
+++ b/drivers/rtc/rtc-at91.c
@@ -0,0 +1,407 @@
+/*
+ * Real Time Clock interface for Linux on Atmel AT91RM9200
+ *
+ * Copyright (C) 2002 Rick Bronson
+ *
+ * Converted to RTC class model by Andrew Victor
+ *
+ * Ported to Linux 2.6 by Steven Scholz
+ * Based on s3c2410-rtc.c Simtec Electronics
+ *
+ * Based on sa1100-rtc.c by Nils Faerber
+ * Based on rtc.c by Paul Gortmaker
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+
+#include <asm/uaccess.h>
+#include <asm/rtc.h>
+
+#include <asm/mach/time.h>
+
+
+#define AT91_RTC_FREQ 1
+#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
+
+static DECLARE_COMPLETION(at91_rtc_updated);
+static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
+
+/*
+ * Decode time/date into rtc_time structure
+ */
+static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
+ struct rtc_time *tm)
+{
+ unsigned int time, date;
+
+ /* must read twice in case it changes */
+ do {
+ time = at91_sys_read(timereg);
+ date = at91_sys_read(calreg);
+ } while ((time != at91_sys_read(timereg)) ||
+ (date != at91_sys_read(calreg)));
+
+ tm->tm_sec = BCD2BIN((time & AT91_RTC_SEC) >> 0);
+ tm->tm_min = BCD2BIN((time & AT91_RTC_MIN) >> 8);
+ tm->tm_hour = BCD2BIN((time & AT91_RTC_HOUR) >> 16);
+
+ /*
+ * The Calendar Alarm register does not have a field for
+ * the year - so these will return an invalid value. When an
+ * alarm is set, at91_alarm_year wille store the current year.
+ */
+ tm->tm_year = BCD2BIN(date & AT91_RTC_CENT) * 100; /* century */
+ tm->tm_year += BCD2BIN((date & AT91_RTC_YEAR) >> 8); /* year */
+
+ tm->tm_wday = BCD2BIN((date & AT91_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */
+ tm->tm_mon = BCD2BIN((date & AT91_RTC_MONTH) >> 16) - 1;
+ tm->tm_mday = BCD2BIN((date & AT91_RTC_DATE) >> 24);
+}
+
+/*
+ * Read current time and date in RTC
+ */
+static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year = tm->tm_year - 1900;
+
+ 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;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long cr;
+
+ 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);
+
+ /* Stop Time/Calendar from counting */
+ cr = at91_sys_read(AT91_RTC_CR);
+ at91_sys_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM);
+
+ at91_sys_write(AT91_RTC_IER, AT91_RTC_ACKUPD);
+ wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */
+ at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD);
+
+ at91_sys_write(AT91_RTC_TIMR,
+ BIN2BCD(tm->tm_sec) << 0
+ | BIN2BCD(tm->tm_min) << 8
+ | BIN2BCD(tm->tm_hour) << 16);
+
+ at91_sys_write(AT91_RTC_CALR,
+ BIN2BCD((tm->tm_year + 1900) / 100) /* century */
+ | BIN2BCD(tm->tm_year % 100) << 8 /* year */
+ | BIN2BCD(tm->tm_mon + 1) << 16 /* tm_mon starts at zero */
+ | BIN2BCD(tm->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */
+ | BIN2BCD(tm->tm_mday) << 24);
+
+ /* Restart Time/Calendar */
+ cr = at91_sys_read(AT91_RTC_CR);
+ at91_sys_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM));
+
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+
+ at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year = at91_alarm_year - 1900;
+
+ 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;
+}
+
+/*
+ * Set alarm time and date in RTC
+ */
+static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time tm;
+
+ at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
+
+ at91_alarm_year = tm.tm_year;
+
+ tm.tm_hour = alrm->time.tm_hour;
+ tm.tm_min = alrm->time.tm_min;
+ tm.tm_sec = alrm->time.tm_sec;
+
+ at91_sys_write(AT91_RTC_TIMALR,
+ BIN2BCD(tm.tm_sec) << 0
+ | BIN2BCD(tm.tm_min) << 8
+ | BIN2BCD(tm.tm_hour) << 16
+ | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN);
+ at91_sys_write(AT91_RTC_CALALR,
+ BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */
+ | BIN2BCD(tm.tm_mday) << 24
+ | AT91_RTC_DATEEN | AT91_RTC_MTHEN);
+
+ pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
+ at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+
+ return 0;
+}
+
+/*
+ * Handle commands from user-space
+ */
+static int at91_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = 0;
+
+ pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __FUNCTION__, cmd, arg);
+
+ switch (cmd) {
+ case RTC_AIE_OFF: /* alarm off */
+ at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM);
+ break;
+ case RTC_AIE_ON: /* alarm on */
+ at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
+ break;
+ case RTC_UIE_OFF: /* update off */
+ case RTC_PIE_OFF: /* periodic off */
+ at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV);
+ break;
+ case RTC_UIE_ON: /* update on */
+ case RTC_PIE_ON: /* periodic on */
+ at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV);
+ break;
+ case RTC_IRQP_READ: /* read periodic alarm frequency */
+ ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg);
+ break;
+ case RTC_IRQP_SET: /* set periodic alarm frequency */
+ if (arg != AT91_RTC_FREQ)
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Provide additional RTC information in /proc/driver/rtc
+ */
+static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned long imr = at91_sys_read(AT91_RTC_IMR);
+
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (imr & AT91_RTC_ALARM) ? "yes" : "no");
+ seq_printf(seq, "update_IRQ\t: %s\n",
+ (imr & AT91_RTC_ACKUPD) ? "yes" : "no");
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (imr & AT91_RTC_SECEV) ? "yes" : "no");
+ seq_printf(seq, "periodic_freq\t: %ld\n",
+ (unsigned long) AT91_RTC_FREQ);
+
+ return 0;
+}
+
+/*
+ * IRQ handler for the RTC
+ */
+static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ unsigned int rtsr;
+ unsigned long events = 0;
+
+ rtsr = at91_sys_read(AT91_RTC_SR) & at91_sys_read(AT91_RTC_IMR);
+ if (rtsr) { /* this interrupt is shared! Is it ours? */
+ if (rtsr & AT91_RTC_ALARM)
+ events |= (RTC_AF | RTC_IRQF);
+ if (rtsr & AT91_RTC_SECEV)
+ events |= (RTC_UF | RTC_IRQF);
+ if (rtsr & AT91_RTC_ACKUPD)
+ complete(&at91_rtc_updated);
+
+ at91_sys_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
+
+ rtc_update_irq(&rtc->class_dev, 1, events);
+
+ pr_debug("%s(): num=%ld, events=0x%02lx\n", __FUNCTION__,
+ events >> 8, events & 0x000000FF);
+
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE; /* not handled */
+}
+
+static struct rtc_class_ops at91_rtc_ops = {
+ .ioctl = at91_rtc_ioctl,
+ .read_time = at91_rtc_readtime,
+ .set_time = at91_rtc_settime,
+ .read_alarm = at91_rtc_readalarm,
+ .set_alarm = at91_rtc_setalarm,
+ .proc = at91_rtc_proc,
+};
+
+/*
+ * Initialize and install RTC driver
+ */
+static int __init at91_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ int ret;
+
+ at91_sys_write(AT91_RTC_CR, 0);
+ at91_sys_write(AT91_RTC_MR, 0); /* 24 hour mode */
+
+ /* Disable all interrupts */
+ at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+ AT91_RTC_SECEV | AT91_RTC_TIMEV |
+ AT91_RTC_CALEV);
+
+ ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
+ IRQF_SHARED, "at91_rtc", pdev);
+ if (ret) {
+ printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
+ AT91_ID_SYS);
+ return ret;
+ }
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &at91_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ free_irq(AT91_ID_SYS, pdev);
+ return PTR_ERR(rtc);
+ }
+ platform_set_drvdata(pdev, rtc);
+
+ printk(KERN_INFO "AT91 Real Time Clock driver.\n");
+ return 0;
+}
+
+/*
+ * Disable and remove the RTC driver
+ */
+static int __devexit at91_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+ /* Disable all interrupts */
+ at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+ AT91_RTC_SECEV | AT91_RTC_TIMEV |
+ AT91_RTC_CALEV);
+ free_irq(AT91_ID_SYS, pdev);
+
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* AT91RM9200 RTC Power management control */
+
+static struct timespec at91_rtc_delta;
+
+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);
+
+ 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);
+
+ 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
+#define at91_rtc_suspend NULL
+#define at91_rtc_resume NULL
+#endif
+
+static struct platform_driver at91_rtc_driver = {
+ .probe = at91_rtc_probe,
+ .remove = at91_rtc_remove,
+ .suspend = at91_rtc_suspend,
+ .resume = at91_rtc_resume,
+ .driver = {
+ .name = "at91_rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_rtc_init(void)
+{
+ return platform_driver_register(&at91_rtc_driver);
+}
+
+static void __exit at91_rtc_exit(void)
+{
+ platform_driver_unregister(&at91_rtc_driver);
+}
+
+module_init(at91_rtc_init);
+module_exit(at91_rtc_exit);
+
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 2011567005f..61a58259c93 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -48,6 +48,93 @@ static int rtc_dev_open(struct inode *inode, struct file *file)
return err;
}
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+/*
+ * Routine to poll RTC seconds field for change as often as possible,
+ * after first RTC_UIE use timer to reduce polling
+ */
+static void rtc_uie_task(void *data)
+{
+ struct rtc_device *rtc = data;
+ struct rtc_time tm;
+ int num = 0;
+ int err;
+
+ err = rtc_read_time(&rtc->class_dev, &tm);
+ spin_lock_irq(&rtc->irq_lock);
+ if (rtc->stop_uie_polling || err) {
+ rtc->uie_task_active = 0;
+ } else if (rtc->oldsecs != tm.tm_sec) {
+ num = (tm.tm_sec + 60 - rtc->oldsecs) % 60;
+ rtc->oldsecs = tm.tm_sec;
+ rtc->uie_timer.expires = jiffies + HZ - (HZ/10);
+ rtc->uie_timer_active = 1;
+ rtc->uie_task_active = 0;
+ add_timer(&rtc->uie_timer);
+ } else if (schedule_work(&rtc->uie_task) == 0) {
+ rtc->uie_task_active = 0;
+ }
+ spin_unlock_irq(&rtc->irq_lock);
+ if (num)
+ rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
+}
+
+static void rtc_uie_timer(unsigned long data)
+{
+ struct rtc_device *rtc = (struct rtc_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->irq_lock, flags);
+ rtc->uie_timer_active = 0;
+ rtc->uie_task_active = 1;
+ if ((schedule_work(&rtc->uie_task) == 0))
+ rtc->uie_task_active = 0;
+ spin_unlock_irqrestore(&rtc->irq_lock, flags);
+}
+
+static void clear_uie(struct rtc_device *rtc)
+{
+ spin_lock_irq(&rtc->irq_lock);
+ if (rtc->irq_active) {
+ rtc->stop_uie_polling = 1;
+ if (rtc->uie_timer_active) {
+ spin_unlock_irq(&rtc->irq_lock);
+ del_timer_sync(&rtc->uie_timer);
+ spin_lock_irq(&rtc->irq_lock);
+ rtc->uie_timer_active = 0;
+ }
+ if (rtc->uie_task_active) {
+ spin_unlock_irq(&rtc->irq_lock);
+ flush_scheduled_work();
+ spin_lock_irq(&rtc->irq_lock);
+ }
+ rtc->irq_active = 0;
+ }
+ spin_unlock_irq(&rtc->irq_lock);
+}
+
+static int set_uie(struct rtc_device *rtc)
+{
+ struct rtc_time tm;
+ int err;
+
+ err = rtc_read_time(&rtc->class_dev, &tm);
+ if (err)
+ return err;
+ spin_lock_irq(&rtc->irq_lock);
+ if (!rtc->irq_active) {
+ rtc->irq_active = 1;
+ rtc->stop_uie_polling = 0;
+ rtc->oldsecs = tm.tm_sec;
+ rtc->uie_task_active = 1;
+ if (schedule_work(&rtc->uie_task) == 0)
+ rtc->uie_task_active = 0;
+ }
+ rtc->irq_data = 0;
+ spin_unlock_irq(&rtc->irq_lock);
+ return 0;
+}
+#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */
static ssize_t
rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
@@ -127,6 +214,28 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg;
+ /* check that the calles has appropriate permissions
+ * for certain ioctls. doing this check here is useful
+ * to avoid duplicate code in each driver.
+ */
+ switch (cmd) {
+ case RTC_EPOCH_SET:
+ case RTC_SET_TIME:
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+ break;
+
+ case RTC_IRQP_SET:
+ if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
+ return -EACCES;
+ break;
+
+ case RTC_PIE_ON:
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EACCES;
+ break;
+ }
+
/* avoid conflicting IRQ users */
if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
spin_lock(&rtc->irq_task_lock);
@@ -185,9 +294,6 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
break;
case RTC_SET_TIME:
- if (!capable(CAP_SYS_TIME))
- return -EACCES;
-
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
@@ -203,10 +309,6 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
err = -EINVAL;
break;
}
- if (!capable(CAP_SYS_TIME)) {
- err = -EACCES;
- break;
- }
rtc_epoch = arg;
err = 0;
#endif
@@ -232,6 +334,14 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
return -EFAULT;
break;
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ case RTC_UIE_OFF:
+ clear_uie(rtc);
+ return 0;
+
+ case RTC_UIE_ON:
+ return set_uie(rtc);
+#endif
default:
err = -ENOTTY;
break;
@@ -244,6 +354,9 @@ static int rtc_dev_release(struct inode *inode, struct file *file)
{
struct rtc_device *rtc = to_rtc_device(file->private_data);
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ clear_uie(rtc);
+#endif
if (rtc->ops->release)
rtc->ops->release(rtc->class_dev.dev);
@@ -284,6 +397,10 @@ static int rtc_dev_add_device(struct class_device *class_dev,
mutex_init(&rtc->char_lock);
spin_lock_init(&rtc->irq_lock);
init_waitqueue_head(&rtc->irq_queue);
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ INIT_WORK(&rtc->uie_task, rtc_uie_task, rtc);
+ setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
+#endif
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc->char_dev.owner = rtc->owner;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
new file mode 100644
index 00000000000..e8afb938478
--- /dev/null
+++ b/drivers/rtc/rtc-ds1307.c
@@ -0,0 +1,388 @@
+/*
+ * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
+ *
+ * Copyright (C) 2005 James Chapman (ds1337 core)
+ * Copyright (C) 2006 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+
+
+/* We can't determine type by probing, but if we expect pre-Linux code
+ * to have set the chip up as a clock (turning on the oscillator and
+ * setting the date and time), Linux can ignore the non-clock features.
+ * That's a natural job for a factory or repair bench.
+ *
+ * If the I2C "force" mechanism is used, we assume the chip is a ds1337.
+ * (Much better would be board-specific tables of I2C devices, along with
+ * the platform_data drivers would use to sort such issues out.)
+ */
+enum ds_type {
+ unknown = 0,
+ ds_1307, /* or ds1338, ... */
+ ds_1337, /* or ds1339, ... */
+ ds_1340, /* or st m41t00, ... */
+ // rs5c372 too? different address...
+};
+
+static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+
+
+/* RTC registers don't differ much, except for the century flag */
+#define DS1307_REG_SECS 0x00 /* 00-59 */
+# define DS1307_BIT_CH 0x80
+#define DS1307_REG_MIN 0x01 /* 00-59 */
+#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
+# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
+# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
+#define DS1307_REG_WDAY 0x03 /* 01-07 */
+#define DS1307_REG_MDAY 0x04 /* 01-31 */
+#define DS1307_REG_MONTH 0x05 /* 01-12 */
+# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
+#define DS1307_REG_YEAR 0x06 /* 00-99 */
+
+/* Other registers (control, status, alarms, trickle charge, NVRAM, etc)
+ * start at 7, and they differ a lot. Only control and status matter for RTC;
+ * be careful using them.
+ */
+#define DS1307_REG_CONTROL 0x07
+# define DS1307_BIT_OUT 0x80
+# define DS1307_BIT_SQWE 0x10
+# define DS1307_BIT_RS1 0x02
+# define DS1307_BIT_RS0 0x01
+#define DS1337_REG_CONTROL 0x0e
+# define DS1337_BIT_nEOSC 0x80
+# define DS1337_BIT_RS2 0x10
+# define DS1337_BIT_RS1 0x08
+# define DS1337_BIT_INTCN 0x04
+# define DS1337_BIT_A2IE 0x02
+# define DS1337_BIT_A1IE 0x01
+#define DS1337_REG_STATUS 0x0f
+# define DS1337_BIT_OSF 0x80
+# define DS1337_BIT_A2I 0x02
+# define DS1337_BIT_A1I 0x01
+#define DS1339_REG_TRICKLE 0x10
+
+
+
+struct ds1307 {
+ u8 reg_addr;
+ u8 regs[8];
+ enum ds_type type;
+ struct i2c_msg msg[2];
+ struct i2c_client client;
+ struct rtc_device *rtc;
+};
+
+
+static int ds1307_get_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int tmp;
+
+ /* read the RTC registers all at once */
+ ds1307->msg[1].flags = I2C_M_RD;
+ ds1307->msg[1].len = 7;
+
+ tmp = i2c_transfer(ds1307->client.adapter, ds1307->msg, 2);
+ if (tmp != 2) {
+ dev_err(dev, "%s error %d\n", "read", tmp);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",
+ "read",
+ ds1307->regs[0], ds1307->regs[1],
+ ds1307->regs[2], ds1307->regs[3],
+ ds1307->regs[4], ds1307->regs[5],
+ ds1307->regs[6]);
+
+ t->tm_sec = BCD2BIN(ds1307->regs[DS1307_REG_SECS] & 0x7f);
+ t->tm_min = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f);
+ tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
+ t->tm_hour = BCD2BIN(tmp);
+ t->tm_wday = BCD2BIN(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;
+ t->tm_mday = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
+ tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
+ t->tm_mon = BCD2BIN(tmp) - 1;
+
+ /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
+ t->tm_year = BCD2BIN(ds1307->regs[DS1307_REG_YEAR]) + 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ return 0;
+}
+
+static int ds1307_set_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int result;
+ int tmp;
+ u8 *buf = ds1307->regs;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", dt->tm_sec, dt->tm_min,
+ dt->tm_hour, dt->tm_mday,
+ dt->tm_mon, dt->tm_year, dt->tm_wday);
+
+ *buf++ = 0; /* first register addr */
+ buf[DS1307_REG_SECS] = BIN2BCD(t->tm_sec);
+ buf[DS1307_REG_MIN] = BIN2BCD(t->tm_min);
+ buf[DS1307_REG_HOUR] = BIN2BCD(t->tm_hour);
+ buf[DS1307_REG_WDAY] = BIN2BCD(t->tm_wday + 1);
+ buf[DS1307_REG_MDAY] = BIN2BCD(t->tm_mday);
+ buf[DS1307_REG_MONTH] = BIN2BCD(t->tm_mon + 1);
+
+ /* assume 20YY not 19YY */
+ tmp = t->tm_year - 100;
+ buf[DS1307_REG_YEAR] = BIN2BCD(tmp);
+
+ if (ds1307->type == ds_1337)
+ buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
+ else if (ds1307->type == ds_1340)
+ buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
+ | DS1340_BIT_CENTURY;
+
+ ds1307->msg[1].flags = 0;
+ ds1307->msg[1].len = 8;
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",
+ "write", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6]);
+
+ result = i2c_transfer(ds1307->client.adapter, &ds1307->msg[1], 1);
+ if (result != 1) {
+ dev_err(dev, "%s error %d\n", "write", tmp);
+ return -EIO;
+ }
+ return 0;
+}
+
+static struct rtc_class_ops ds13xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+};
+
+static struct i2c_driver ds1307_driver;
+
+static int __devinit
+ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct ds1307 *ds1307;
+ int err = -ENODEV;
+ struct i2c_client *client;
+ int tmp;
+
+ if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ client = &ds1307->client;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &ds1307_driver;
+ client->flags = 0;
+
+ i2c_set_clientdata(client, ds1307);
+
+ ds1307->msg[0].addr = client->addr;
+ ds1307->msg[0].flags = 0;
+ ds1307->msg[0].len = 1;
+ ds1307->msg[0].buf = &ds1307->reg_addr;
+
+ ds1307->msg[1].addr = client->addr;
+ ds1307->msg[1].flags = I2C_M_RD;
+ ds1307->msg[1].len = sizeof(ds1307->regs);
+ ds1307->msg[1].buf = ds1307->regs;
+
+ /* HACK: "force" implies "needs ds1337-style-oscillator setup" */
+ if (kind >= 0) {
+ ds1307->type = ds_1337;
+
+ ds1307->reg_addr = DS1337_REG_CONTROL;
+ ds1307->msg[1].len = 2;
+
+ tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
+ if (tmp != 2) {
+ pr_debug("read error %d\n", tmp);
+ err = -EIO;
+ goto exit_free;
+ }
+
+ ds1307->reg_addr = 0;
+ ds1307->msg[1].len = sizeof(ds1307->regs);
+
+ /* oscillator is off; need to turn it on */
+ if ((ds1307->regs[0] & DS1337_BIT_nEOSC)
+ || (ds1307->regs[1] & DS1337_BIT_OSF)) {
+ printk(KERN_ERR "no ds1337 oscillator code\n");
+ goto exit_free;
+ }
+ } else
+ ds1307->type = ds_1307;
+
+read_rtc:
+ /* read RTC registers */
+
+ tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
+ if (tmp != 2) {
+ pr_debug("read error %d\n", tmp);
+ err = -EIO;
+ goto exit_free;
+ }
+
+ /* minimal sanity checking; some chips (like DS1340) don't
+ * specify the extra bits as must-be-zero, but there are
+ * still a few values that are clearly out-of-range.
+ */
+ tmp = ds1307->regs[DS1307_REG_SECS];
+ if (tmp & DS1307_BIT_CH) {
+ if (ds1307->type && ds1307->type != ds_1307) {
+ pr_debug("not a ds1307?\n");
+ goto exit_free;
+ }
+ ds1307->type = ds_1307;
+
+ /* this partial initialization should work for ds1307,
+ * ds1338, ds1340, st m41t00, and more.
+ */
+ dev_warn(&client->dev, "oscillator started; SET TIME!\n");
+ i2c_smbus_write_byte_data(client, 0, 0);
+ goto read_rtc;
+ }
+ tmp = BCD2BIN(tmp & 0x7f);
+ if (tmp > 60)
+ goto exit_free;
+ tmp = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f);
+ if (tmp > 60)
+ goto exit_free;
+
+ tmp = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto exit_free;
+
+ tmp = BCD2BIN(ds1307->regs[DS1307_REG_MONTH] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto exit_free;
+
+ /* force into in 24 hour mode (most chips) or
+ * disable century bit (ds1340)
+ */
+ tmp = ds1307->regs[DS1307_REG_HOUR];
+ if (tmp & (1 << 6)) {
+ if (tmp & (1 << 5))
+ tmp = BCD2BIN(tmp & 0x1f) + 12;
+ else
+ tmp = BCD2BIN(tmp);
+ i2c_smbus_write_byte_data(client,
+ DS1307_REG_HOUR,
+ BIN2BCD(tmp));
+ }
+
+ /* FIXME chips like 1337 can generate alarm irqs too; those are
+ * worth exposing through the API (especially when the irq is
+ * wakeup-capable).
+ */
+
+ switch (ds1307->type) {
+ case unknown:
+ strlcpy(client->name, "unknown", I2C_NAME_SIZE);
+ break;
+ case ds_1307:
+ strlcpy(client->name, "ds1307", I2C_NAME_SIZE);
+ break;
+ case ds_1337:
+ strlcpy(client->name, "ds1337", I2C_NAME_SIZE);
+ break;
+ case ds_1340:
+ strlcpy(client->name, "ds1340", I2C_NAME_SIZE);
+ break;
+ }
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(client)))
+ goto exit_free;
+
+ ds1307->rtc = rtc_device_register(client->name, &client->dev,
+ &ds13xx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds1307->rtc)) {
+ err = PTR_ERR(ds1307->rtc);
+ dev_err(&client->dev,
+ "unable to register the class device\n");
+ goto exit_detach;
+ }
+
+ return 0;
+
+exit_detach:
+ i2c_detach_client(client);
+exit_free:
+ kfree(ds1307);
+exit:
+ return err;
+}
+
+static int __devinit
+ds1307_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return 0;
+ return i2c_probe(adapter, &addr_data, ds1307_detect);
+}
+
+static int __devexit ds1307_detach_client(struct i2c_client *client)
+{
+ int err;
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+
+ rtc_device_unregister(ds1307->rtc);
+ if ((err = i2c_detach_client(client)))
+ return err;
+ kfree(ds1307);
+ return 0;
+}
+
+static struct i2c_driver ds1307_driver = {
+ .driver = {
+ .name = "ds1307",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = ds1307_attach_adapter,
+ .detach_client = __devexit_p(ds1307_detach_client),
+};
+
+static int __init ds1307_init(void)
+{
+ return i2c_add_driver(&ds1307_driver);
+}
+module_init(ds1307_init);
+
+static void __exit ds1307_exit(void)
+{
+ i2c_del_driver(&ds1307_driver);
+}
+module_exit(ds1307_exit);
+
+MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
new file mode 100644
index 00000000000..20900149547
--- /dev/null
+++ b/drivers/rtc/rtc-ds1553.c
@@ -0,0 +1,414 @@
+/*
+ * An rtc driver for the Dallas DS1553
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRV_VERSION "0.1"
+
+#define RTC_REG_SIZE 0x2000
+#define RTC_OFFSET 0x1ff0
+
+#define RTC_FLAGS (RTC_OFFSET + 0)
+#define RTC_SECONDS_ALARM (RTC_OFFSET + 2)
+#define RTC_MINUTES_ALARM (RTC_OFFSET + 3)
+#define RTC_HOURS_ALARM (RTC_OFFSET + 4)
+#define RTC_DATE_ALARM (RTC_OFFSET + 5)
+#define RTC_INTERRUPTS (RTC_OFFSET + 6)
+#define RTC_WATCHDOG (RTC_OFFSET + 7)
+#define RTC_CONTROL (RTC_OFFSET + 8)
+#define RTC_CENTURY (RTC_OFFSET + 8)
+#define RTC_SECONDS (RTC_OFFSET + 9)
+#define RTC_MINUTES (RTC_OFFSET + 10)
+#define RTC_HOURS (RTC_OFFSET + 11)
+#define RTC_DAY (RTC_OFFSET + 12)
+#define RTC_DATE (RTC_OFFSET + 13)
+#define RTC_MONTH (RTC_OFFSET + 14)
+#define RTC_YEAR (RTC_OFFSET + 15)
+
+#define RTC_CENTURY_MASK 0x3f
+#define RTC_SECONDS_MASK 0x7f
+#define RTC_DAY_MASK 0x07
+
+/* Bits in the Control/Century register */
+#define RTC_WRITE 0x80
+#define RTC_READ 0x40
+
+/* Bits in the Seconds register */
+#define RTC_STOP 0x80
+
+/* Bits in the Flags register */
+#define RTC_FLAGS_AF 0x40
+#define RTC_FLAGS_BLF 0x10
+
+/* Bits in the Interrupts register */
+#define RTC_INTS_AE 0x80
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ unsigned long last_jiffies;
+ int irq;
+ unsigned int irqen;
+ int alrm_sec;
+ int alrm_min;
+ int alrm_hour;
+ int alrm_mday;
+};
+
+static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u8 century;
+
+ century = BIN2BCD((tm->tm_year + 1900) / 100);
+
+ writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL);
+
+ writeb(BIN2BCD(tm->tm_year % 100), ioaddr + RTC_YEAR);
+ writeb(BIN2BCD(tm->tm_mon + 1), ioaddr + RTC_MONTH);
+ writeb(BIN2BCD(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
+ writeb(BIN2BCD(tm->tm_mday), ioaddr + RTC_DATE);
+ writeb(BIN2BCD(tm->tm_hour), ioaddr + RTC_HOURS);
+ writeb(BIN2BCD(tm->tm_min), ioaddr + RTC_MINUTES);
+ writeb(BIN2BCD(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
+
+ /* RTC_CENTURY and RTC_CONTROL share same register */
+ writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY);
+ writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ return 0;
+}
+
+static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned int year, month, day, hour, minute, second, week;
+ unsigned int century;
+
+ /* give enough time to update RTC in case of continuous read */
+ if (pdata->last_jiffies == jiffies)
+ msleep(1);
+ pdata->last_jiffies = jiffies;
+ writeb(RTC_READ, ioaddr + RTC_CONTROL);
+ second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
+ minute = readb(ioaddr + RTC_MINUTES);
+ hour = readb(ioaddr + RTC_HOURS);
+ day = readb(ioaddr + RTC_DATE);
+ week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
+ month = readb(ioaddr + RTC_MONTH);
+ year = readb(ioaddr + RTC_YEAR);
+ century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(0, ioaddr + RTC_CONTROL);
+ tm->tm_sec = BCD2BIN(second);
+ tm->tm_min = BCD2BIN(minute);
+ tm->tm_hour = BCD2BIN(hour);
+ tm->tm_mday = BCD2BIN(day);
+ tm->tm_wday = BCD2BIN(week);
+ tm->tm_mon = BCD2BIN(month) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = BCD2BIN(year) + BCD2BIN(century) * 100 - 1900;
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+ return 0;
+}
+
+static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
+{
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
+ writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : BIN2BCD(pdata->alrm_mday),
+ ioaddr + RTC_DATE_ALARM);
+ writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : BIN2BCD(pdata->alrm_hour),
+ ioaddr + RTC_HOURS_ALARM);
+ writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : BIN2BCD(pdata->alrm_min),
+ ioaddr + RTC_MINUTES_ALARM);
+ writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : BIN2BCD(pdata->alrm_sec),
+ ioaddr + RTC_SECONDS_ALARM);
+ writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
+ readb(ioaddr + RTC_FLAGS); /* clear interrupts */
+ spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
+}
+
+static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq < 0)
+ return -EINVAL;
+ pdata->alrm_mday = alrm->time.tm_mday;
+ pdata->alrm_hour = alrm->time.tm_hour;
+ pdata->alrm_min = alrm->time.tm_min;
+ pdata->alrm_sec = alrm->time.tm_sec;
+ if (alrm->enabled)
+ pdata->irqen |= RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int ds1553_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq < 0)
+ return -EINVAL;
+ alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
+ alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
+ alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
+ alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
+ alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
+ return 0;
+}
+
+static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long events = RTC_IRQF;
+
+ /* read and clear interrupt */
+ if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
+ return IRQ_NONE;
+ if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ rtc_update_irq(&pdata->rtc->class_dev, 1, events);
+ return IRQ_HANDLED;
+}
+
+static void ds1553_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq >= 0) {
+ pdata->irqen = 0;
+ ds1553_rtc_update_alarm(pdata);
+ }
+}
+
+static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq < 0)
+ return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ pdata->irqen &= ~RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ break;
+ case RTC_AIE_ON:
+ pdata->irqen |= RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ break;
+ case RTC_UIE_OFF:
+ pdata->irqen &= ~RTC_UF;
+ ds1553_rtc_update_alarm(pdata);
+ break;
+ case RTC_UIE_ON:
+ pdata->irqen |= RTC_UF;
+ ds1553_rtc_update_alarm(pdata);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static struct rtc_class_ops ds1553_rtc_ops = {
+ .read_time = ds1553_rtc_read_time,
+ .set_time = ds1553_rtc_set_time,
+ .read_alarm = ds1553_rtc_read_alarm,
+ .set_alarm = ds1553_rtc_set_alarm,
+ .release = ds1553_rtc_release,
+ .ioctl = ds1553_rtc_ioctl,
+};
+
+static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ *buf++ = readb(ioaddr + pos++);
+ return count;
+}
+
+static ssize_t ds1553_nvram_write(struct kobject *kobj, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ writeb(*buf++, ioaddr + pos++);
+ return count;
+}
+
+static struct bin_attribute ds1553_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUGO,
+ .owner = THIS_MODULE,
+ },
+ .size = RTC_OFFSET,
+ .read = ds1553_nvram_read,
+ .write = ds1553_nvram_write,
+};
+
+static int __init ds1553_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ unsigned int cen, sec;
+ struct rtc_plat_data *pdata = NULL;
+ void __iomem *ioaddr = NULL;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->irq = -1;
+ if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ pdata->baseaddr = res->start;
+ ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
+ if (!ioaddr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pdata->ioaddr = ioaddr;
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ /* turn RTC on if it was not on */
+ sec = readb(ioaddr + RTC_SECONDS);
+ if (sec & RTC_STOP) {
+ sec &= RTC_SECONDS_MASK;
+ cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+ writeb(sec, ioaddr + RTC_SECONDS);
+ writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ }
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ if (pdata->irq >= 0) {
+ writeb(0, ioaddr + RTC_INTERRUPTS);
+ if (request_irq(pdata->irq, ds1553_rtc_interrupt, IRQF_SHARED,
+ pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ }
+ }
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &ds1553_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto out;
+ }
+ pdata->rtc = rtc;
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
+ sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ return 0;
+ out:
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+ if (ioaddr)
+ iounmap(ioaddr);
+ if (pdata->baseaddr)
+ release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
+ kfree(pdata);
+ return ret;
+}
+
+static int __devexit ds1553_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ rtc_device_unregister(pdata->rtc);
+ if (pdata->irq >= 0) {
+ writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
+ free_irq(pdata->irq, pdev);
+ }
+ iounmap(pdata->ioaddr);
+ release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
+ kfree(pdata);
+ return 0;
+}
+
+static struct platform_driver ds1553_rtc_driver = {
+ .probe = ds1553_rtc_probe,
+ .remove = __devexit_p(ds1553_rtc_remove),
+ .driver = {
+ .name = "ds1553",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int ds1553_init(void)
+{
+ return platform_driver_register(&ds1553_rtc_driver);
+}
+
+static __exit void ds1553_exit(void)
+{
+ return platform_driver_unregister(&ds1553_rtc_driver);
+}
+
+module_init(ds1553_init);
+module_exit(ds1553_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Dallas DS1553 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
new file mode 100644
index 00000000000..8e47e5a06d2
--- /dev/null
+++ b/drivers/rtc/rtc-ds1742.c
@@ -0,0 +1,259 @@
+/*
+ * An rtc driver for the Dallas DS1742
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRV_VERSION "0.1"
+
+#define RTC_REG_SIZE 0x800
+#define RTC_OFFSET 0x7f8
+
+#define RTC_CONTROL (RTC_OFFSET + 0)
+#define RTC_CENTURY (RTC_OFFSET + 0)
+#define RTC_SECONDS (RTC_OFFSET + 1)
+#define RTC_MINUTES (RTC_OFFSET + 2)
+#define RTC_HOURS (RTC_OFFSET + 3)
+#define RTC_DAY (RTC_OFFSET + 4)
+#define RTC_DATE (RTC_OFFSET + 5)
+#define RTC_MONTH (RTC_OFFSET + 6)
+#define RTC_YEAR (RTC_OFFSET + 7)
+
+#define RTC_CENTURY_MASK 0x3f
+#define RTC_SECONDS_MASK 0x7f
+#define RTC_DAY_MASK 0x07
+
+/* Bits in the Control/Century register */
+#define RTC_WRITE 0x80
+#define RTC_READ 0x40
+
+/* Bits in the Seconds register */
+#define RTC_STOP 0x80
+
+/* Bits in the Day register */
+#define RTC_BATT_FLAG 0x80
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ unsigned long last_jiffies;
+};
+
+static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u8 century;
+
+ century = BIN2BCD((tm->tm_year + 1900) / 100);
+
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+
+ writeb(BIN2BCD(tm->tm_year % 100), ioaddr + RTC_YEAR);
+ writeb(BIN2BCD(tm->tm_mon + 1), ioaddr + RTC_MONTH);
+ writeb(BIN2BCD(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
+ writeb(BIN2BCD(tm->tm_mday), ioaddr + RTC_DATE);
+ writeb(BIN2BCD(tm->tm_hour), ioaddr + RTC_HOURS);
+ writeb(BIN2BCD(tm->tm_min), ioaddr + RTC_MINUTES);
+ writeb(BIN2BCD(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
+
+ /* RTC_CENTURY and RTC_CONTROL share same register */
+ writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY);
+ writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ return 0;
+}
+
+static int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned int year, month, day, hour, minute, second, week;
+ unsigned int century;
+
+ /* give enough time to update RTC in case of continuous read */
+ if (pdata->last_jiffies == jiffies)
+ msleep(1);
+ pdata->last_jiffies = jiffies;
+ writeb(RTC_READ, ioaddr + RTC_CONTROL);
+ second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
+ minute = readb(ioaddr + RTC_MINUTES);
+ hour = readb(ioaddr + RTC_HOURS);
+ day = readb(ioaddr + RTC_DATE);
+ week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
+ month = readb(ioaddr + RTC_MONTH);
+ year = readb(ioaddr + RTC_YEAR);
+ century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(0, ioaddr + RTC_CONTROL);
+ tm->tm_sec = BCD2BIN(second);
+ tm->tm_min = BCD2BIN(minute);
+ tm->tm_hour = BCD2BIN(hour);
+ tm->tm_mday = BCD2BIN(day);
+ tm->tm_wday = BCD2BIN(week);
+ tm->tm_mon = BCD2BIN(month) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = BCD2BIN(year) + BCD2BIN(century) * 100 - 1900;
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+ return 0;
+}
+
+static struct rtc_class_ops ds1742_rtc_ops = {
+ .read_time = ds1742_rtc_read_time,
+ .set_time = ds1742_rtc_set_time,
+};
+
+static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ *buf++ = readb(ioaddr + pos++);
+ return count;
+}
+
+static ssize_t ds1742_nvram_write(struct kobject *kobj, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ writeb(*buf++, ioaddr + pos++);
+ return count;
+}
+
+static struct bin_attribute ds1742_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUGO,
+ .owner = THIS_MODULE,
+ },
+ .size = RTC_OFFSET,
+ .read = ds1742_nvram_read,
+ .write = ds1742_nvram_write,
+};
+
+static int __init ds1742_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ unsigned int cen, sec;
+ struct rtc_plat_data *pdata = NULL;
+ void __iomem *ioaddr = NULL;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ pdata->baseaddr = res->start;
+ ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
+ if (!ioaddr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pdata->ioaddr = ioaddr;
+
+ /* turn RTC on if it was not on */
+ sec = readb(ioaddr + RTC_SECONDS);
+ if (sec & RTC_STOP) {
+ sec &= RTC_SECONDS_MASK;
+ cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+ writeb(sec, ioaddr + RTC_SECONDS);
+ writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ }
+ if (readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG)
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &ds1742_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto out;
+ }
+ pdata->rtc = rtc;
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
+ sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
+ return 0;
+ out:
+ if (ioaddr)
+ iounmap(ioaddr);
+ if (pdata->baseaddr)
+ release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
+ kfree(pdata);
+ return ret;
+}
+
+static int __devexit ds1742_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
+ rtc_device_unregister(pdata->rtc);
+ iounmap(pdata->ioaddr);
+ release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
+ kfree(pdata);
+ return 0;
+}
+
+static struct platform_driver ds1742_rtc_driver = {
+ .probe = ds1742_rtc_probe,
+ .remove = __devexit_p(ds1742_rtc_remove),
+ .driver = {
+ .name = "ds1742",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int ds1742_init(void)
+{
+ return platform_driver_register(&ds1742_rtc_driver);
+}
+
+static __exit void ds1742_exit(void)
+{
+ return platform_driver_unregister(&ds1742_rtc_driver);
+}
+
+module_init(ds1742_init);
+module_exit(ds1742_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Dallas DS1742 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index cfedc1d28ee..9812120f3a7 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -18,9 +18,19 @@ static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
+static const unsigned short rtc_ydays[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 }
+};
+
#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);
@@ -28,6 +38,15 @@ int rtc_month_days(unsigned int month, unsigned int year)
EXPORT_SYMBOL(rtc_month_days);
/*
+ * The number of days since January 1. (0 to 365)
+ */
+int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
+{
+ return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
+}
+EXPORT_SYMBOL(rtc_year_days);
+
+/*
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
new file mode 100644
index 00000000000..2c9739562b5
--- /dev/null
+++ b/drivers/rtc/rtc-max6902.c
@@ -0,0 +1,286 @@
+/* drivers/char/max6902.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for MAX6902 spi RTC
+ *
+ * Changelog:
+ *
+ * 24-May-2006: Raphael Assenat <raph@8d.com>
+ * - Major rework
+ * Converted to rtc_device and uses the SPI layer.
+ *
+ * ??-???-2005: Someone at Compulab
+ * - Initial driver creation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+
+#define MAX6902_REG_SECONDS 0x01
+#define MAX6902_REG_MINUTES 0x03
+#define MAX6902_REG_HOURS 0x05
+#define MAX6902_REG_DATE 0x07
+#define MAX6902_REG_MONTH 0x09
+#define MAX6902_REG_DAY 0x0B
+#define MAX6902_REG_YEAR 0x0D
+#define MAX6902_REG_CONTROL 0x0F
+#define MAX6902_REG_CENTURY 0x13
+
+#undef MAX6902_DEBUG
+
+struct max6902 {
+ struct rtc_device *rtc;
+ u8 buf[9]; /* Burst read cmd + 8 registers */
+ u8 tx_buf[2];
+ u8 rx_buf[2];
+};
+
+static void max6902_set_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ /* MSB must be '0' to write */
+ buf[0] = address & 0x7f;
+ buf[1] = data;
+
+ spi_write(spi, buf, 2);
+}
+
+static int max6902_get_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct max6902 *chip = dev_get_drvdata(dev);
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int status;
+
+ if (!data)
+ return -EINVAL;
+
+ /* Build our spi message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.len = 2;
+ /* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */
+ xfer.tx_buf = chip->tx_buf;
+ xfer.rx_buf = chip->rx_buf;
+
+ /* Set MSB to indicate read */
+ chip->tx_buf[0] = address | 0x80;
+
+ spi_message_add_tail(&xfer, &message);
+
+ /* do the i/o */
+ status = spi_sync(spi, &message);
+ if (status == 0)
+ status = message.status;
+ else
+ return status;
+
+ *data = chip->rx_buf[1];
+
+ return status;
+}
+
+static int max6902_get_datetime(struct device *dev, struct rtc_time *dt)
+{
+ unsigned char tmp;
+ int century;
+ int err;
+ struct spi_device *spi = to_spi_device(dev);
+ struct max6902 *chip = dev_get_drvdata(dev);
+ struct spi_message message;
+ struct spi_transfer xfer;
+ int status;
+
+ err = max6902_get_reg(dev, MAX6902_REG_CENTURY, &tmp);
+ if (err)
+ return err;
+
+ /* build the message */
+ spi_message_init(&message);
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.len = 1 + 7; /* Burst read command + 7 registers */
+ xfer.tx_buf = chip->buf;
+ xfer.rx_buf = chip->buf;
+ chip->buf[0] = 0xbf; /* Burst read */
+ spi_message_add_tail(&xfer, &message);
+
+ /* do the i/o */
+ status = spi_sync(spi, &message);
+ if (status == 0)
+ status = message.status;
+ else
+ return status;
+
+ /* The chip sends data in this order:
+ * Seconds, Minutes, Hours, Date, Month, Day, Year */
+ dt->tm_sec = BCD2BIN(chip->buf[1]);
+ dt->tm_min = BCD2BIN(chip->buf[2]);
+ dt->tm_hour = BCD2BIN(chip->buf[3]);
+ dt->tm_mday = BCD2BIN(chip->buf[4]);
+ dt->tm_mon = BCD2BIN(chip->buf[5] - 1);
+ dt->tm_wday = BCD2BIN(chip->buf[6]);
+ dt->tm_year = BCD2BIN(chip->buf[7]);
+
+ century = BCD2BIN(tmp) * 100;
+
+ dt->tm_year += century;
+ dt->tm_year -= 1900;
+
+#ifdef MAX6902_DEBUG
+ printk("\n%s : Read RTC values\n",__FUNCTION__);
+ printk("tm_hour: %i\n",dt->tm_hour);
+ printk("tm_min : %i\n",dt->tm_min);
+ printk("tm_sec : %i\n",dt->tm_sec);
+ printk("tm_year: %i\n",dt->tm_year);
+ printk("tm_mon : %i\n",dt->tm_mon);
+ printk("tm_mday: %i\n",dt->tm_mday);
+ printk("tm_wday: %i\n",dt->tm_wday);
+#endif
+
+ return 0;
+}
+
+static int max6902_set_datetime(struct device *dev, struct rtc_time *dt)
+{
+ dt->tm_year = dt->tm_year+1900;
+
+#ifdef MAX6902_DEBUG
+ printk("\n%s : Setting RTC values\n",__FUNCTION__);
+ printk("tm_sec : %i\n",dt->tm_sec);
+ printk("tm_min : %i\n",dt->tm_min);
+ printk("tm_hour: %i\n",dt->tm_hour);
+ printk("tm_mday: %i\n",dt->tm_mday);
+ printk("tm_wday: %i\n",dt->tm_wday);
+ printk("tm_year: %i\n",dt->tm_year);
+#endif
+
+ /* Remove write protection */
+ max6902_set_reg(dev, 0xF, 0);
+
+ max6902_set_reg(dev, 0x01, BIN2BCD(dt->tm_sec));
+ max6902_set_reg(dev, 0x03, BIN2BCD(dt->tm_min));
+ max6902_set_reg(dev, 0x05, BIN2BCD(dt->tm_hour));
+
+ max6902_set_reg(dev, 0x07, BIN2BCD(dt->tm_mday));
+ max6902_set_reg(dev, 0x09, BIN2BCD(dt->tm_mon+1));
+ max6902_set_reg(dev, 0x0B, BIN2BCD(dt->tm_wday));
+ max6902_set_reg(dev, 0x0D, BIN2BCD(dt->tm_year%100));
+ max6902_set_reg(dev, 0x13, BIN2BCD(dt->tm_year/100));
+
+ /* Compulab used a delay here. However, the datasheet
+ * does not mention a delay being required anywhere... */
+ /* delay(2000); */
+
+ /* Write protect */
+ max6902_set_reg(dev, 0xF, 0x80);
+
+ return 0;
+}
+
+static int max6902_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6902_get_datetime(dev, tm);
+}
+
+static int max6902_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6902_set_datetime(dev, tm);
+}
+
+static struct rtc_class_ops max6902_rtc_ops = {
+ .read_time = max6902_read_time,
+ .set_time = max6902_set_time,
+};
+
+static int __devinit max6902_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char tmp;
+ struct max6902 *chip;
+ int res;
+
+ rtc = rtc_device_register("max6902",
+ &spi->dev, &max6902_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (!chip) {
+ rtc_device_unregister(rtc);
+ return -ENOMEM;
+ }
+ chip->rtc = rtc;
+ dev_set_drvdata(&spi->dev, chip);
+
+ res = max6902_get_reg(&spi->dev, MAX6902_REG_SECONDS, &tmp);
+ if (res) {
+ rtc_device_unregister(rtc);
+ return res;
+ }
+
+ return 0;
+}
+
+static int __devexit max6902_remove(struct spi_device *spi)
+{
+ struct max6902 *chip = platform_get_drvdata(spi);
+ struct rtc_device *rtc = chip->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ kfree(chip);
+
+ return 0;
+}
+
+static struct spi_driver max6902_driver = {
+ .driver = {
+ .name = "max6902",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = max6902_probe,
+ .remove = __devexit_p(max6902_remove),
+};
+
+static __init int max6902_init(void)
+{
+ printk("max6902 spi driver\n");
+ return spi_register_driver(&max6902_driver);
+}
+module_init(max6902_init);
+
+static __exit void max6902_exit(void)
+{
+ spi_unregister_driver(&max6902_driver);
+}
+module_exit(max6902_exit);
+
+MODULE_DESCRIPTION ("max6902 spi RTC driver");
+MODULE_AUTHOR ("Raphael Assenat");
+MODULE_LICENSE ("GPL");
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
new file mode 100644
index 00000000000..b235a30cb66
--- /dev/null
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -0,0 +1,394 @@
+/*
+ * drivers/rtc/rtc-pcf8583.c
+ *
+ * Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for PCF8583 RTC & RAM chip
+ *
+ * Converted to the generic RTC susbsystem by G. Liakhovetski (2006)
+ */
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/mc146818rtc.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+
+struct rtc_mem {
+ unsigned int loc;
+ unsigned int nr;
+ unsigned char *data;
+};
+
+struct pcf8583 {
+ struct i2c_client client;
+ struct rtc_device *rtc;
+ unsigned char ctrl;
+};
+
+#define CTRL_STOP 0x80
+#define CTRL_HOLD 0x40
+#define CTRL_32KHZ 0x00
+#define CTRL_MASK 0x08
+#define CTRL_ALARMEN 0x04
+#define CTRL_ALARM 0x02
+#define CTRL_TIMER 0x01
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Module parameters */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver pcf8583_driver;
+
+#define get_ctrl(x) ((struct pcf8583 *)i2c_get_clientdata(x))->ctrl
+#define set_ctrl(x, v) get_ctrl(x) = v
+
+#define CMOS_YEAR (64 + 128)
+#define CMOS_CHECKSUM (63)
+
+static int pcf8583_get_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+ unsigned char buf[8], addr[1] = { 1 };
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 6,
+ .buf = buf,
+ }
+ };
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret == 2) {
+ dt->tm_year = buf[4] >> 6;
+ dt->tm_wday = buf[5] >> 5;
+
+ buf[4] &= 0x3f;
+ buf[5] &= 0x1f;
+
+ dt->tm_sec = BCD_TO_BIN(buf[1]);
+ dt->tm_min = BCD_TO_BIN(buf[2]);
+ dt->tm_hour = BCD_TO_BIN(buf[3]);
+ dt->tm_mday = BCD_TO_BIN(buf[4]);
+ dt->tm_mon = BCD_TO_BIN(buf[5]);
+ }
+
+ return ret == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
+{
+ unsigned char buf[8];
+ int ret, len = 6;
+
+ buf[0] = 0;
+ buf[1] = get_ctrl(client) | 0x80;
+ buf[2] = 0;
+ buf[3] = BIN_TO_BCD(dt->tm_sec);
+ buf[4] = BIN_TO_BCD(dt->tm_min);
+ buf[5] = BIN_TO_BCD(dt->tm_hour);
+
+ if (datetoo) {
+ len = 8;
+ buf[6] = BIN_TO_BCD(dt->tm_mday) | (dt->tm_year << 6);
+ buf[7] = BIN_TO_BCD(dt->tm_mon) | (dt->tm_wday << 5);
+ }
+
+ ret = i2c_master_send(client, (char *)buf, len);
+ if (ret != len)
+ return -EIO;
+
+ buf[1] = get_ctrl(client);
+ ret = i2c_master_send(client, (char *)buf, 2);
+
+ return ret == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
+{
+ *ctrl = get_ctrl(client);
+ return 0;
+}
+
+static int pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl)
+{
+ unsigned char buf[2];
+
+ buf[0] = 0;
+ buf[1] = *ctrl;
+ set_ctrl(client, *ctrl);
+
+ return i2c_master_send(client, (char *)buf, 2);
+}
+
+static int pcf8583_read_mem(struct i2c_client *client, struct rtc_mem *mem)
+{
+ unsigned char addr[1];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = mem->nr,
+ .buf = mem->data,
+ }
+ };
+
+ if (mem->loc < 8)
+ return -EINVAL;
+
+ addr[0] = mem->loc;
+
+ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_write_mem(struct i2c_client *client, struct rtc_mem *mem)
+{
+ unsigned char addr[1];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_NOSTART,
+ .len = mem->nr,
+ .buf = mem->data,
+ }
+ };
+
+ if (mem->loc < 8)
+ return -EINVAL;
+
+ addr[0] = mem->loc;
+
+ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char ctrl, year[2];
+ struct rtc_mem mem = { CMOS_YEAR, sizeof(year), year };
+ int real_year, year_offset, err;
+
+ /*
+ * Ensure that the RTC is running.
+ */
+ pcf8583_get_ctrl(client, &ctrl);
+ if (ctrl & (CTRL_STOP | CTRL_HOLD)) {
+ unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD);
+
+ printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n",
+ ctrl, new_ctrl);
+
+ if ((err = pcf8583_set_ctrl(client, &new_ctrl)) < 0)
+ return err;
+ }
+
+ if (pcf8583_get_datetime(client, tm) ||
+ pcf8583_read_mem(client, &mem))
+ return -EIO;
+
+ real_year = year[0];
+
+ /*
+ * The RTC year holds the LSB two bits of the current
+ * year, which should reflect the LSB two bits of the
+ * CMOS copy of the year. Any difference indicates
+ * that we have to correct the CMOS version.
+ */
+ year_offset = tm->tm_year - (real_year & 3);
+ if (year_offset < 0)
+ /*
+ * RTC year wrapped. Adjust it appropriately.
+ */
+ year_offset += 4;
+
+ tm->tm_year = real_year + year_offset + year[1] * 100;
+
+ return 0;
+}
+
+static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char year[2], chk;
+ struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year };
+ struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
+ int ret;
+
+ /*
+ * The RTC's own 2-bit year must reflect the least
+ * significant two bits of the CMOS year.
+ */
+
+ ret = pcf8583_set_datetime(client, tm, 1);
+ if (ret)
+ return ret;
+
+ ret = pcf8583_read_mem(client, &cmos_check);
+ if (ret)
+ return ret;
+
+ ret = pcf8583_read_mem(client, &cmos_year);
+ if (ret)
+ return ret;
+
+ chk -= year[1] + year[0];
+
+ year[1] = tm->tm_year / 100;
+ year[0] = tm->tm_year % 100;
+
+ chk += year[1] + year[0];
+
+ ret = pcf8583_write_mem(client, &cmos_year);
+
+ if (ret)
+ return ret;
+
+ ret = pcf8583_write_mem(client, &cmos_check);
+
+ return ret;
+}
+
+static struct rtc_class_ops pcf8583_rtc_ops = {
+ .read_time = pcf8583_rtc_read_time,
+ .set_time = pcf8583_rtc_set_time,
+};
+
+static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind);
+
+static int pcf8583_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, pcf8583_probe);
+}
+
+static int pcf8583_detach(struct i2c_client *client)
+{
+ int err;
+ struct pcf8583 *pcf = i2c_get_clientdata(client);
+ struct rtc_device *rtc = pcf->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+
+ kfree(pcf);
+ return 0;
+}
+
+static struct i2c_driver pcf8583_driver = {
+ .driver = {
+ .name = "pcf8583",
+ },
+ .id = I2C_DRIVERID_PCF8583,
+ .attach_adapter = pcf8583_attach,
+ .detach_client = pcf8583_detach,
+};
+
+static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct pcf8583 *pcf;
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ unsigned char buf[1], ad[1] = { 0 };
+ int err;
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = ad,
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
+ if (!pcf)
+ return -ENOMEM;
+
+ client = &pcf->client;
+
+ client->addr = addr;
+ client->adapter = adap;
+ client->driver = &pcf8583_driver;
+
+ strlcpy(client->name, pcf8583_driver.driver.name, I2C_NAME_SIZE);
+
+ if (i2c_transfer(client->adapter, msgs, 2) != 2) {
+ err = -EIO;
+ goto exit_kfree;
+ }
+
+ err = i2c_attach_client(client);
+
+ if (err)
+ goto exit_kfree;
+
+ rtc = rtc_device_register(pcf8583_driver.driver.name, &client->dev,
+ &pcf8583_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ err = PTR_ERR(rtc);
+ goto exit_detach;
+ }
+
+ pcf->rtc = rtc;
+ i2c_set_clientdata(client, pcf);
+ set_ctrl(client, buf[0]);
+
+ return 0;
+
+exit_detach:
+ i2c_detach_client(client);
+
+exit_kfree:
+ kfree(pcf);
+
+ return err;
+}
+
+static __init int pcf8583_init(void)
+{
+ return i2c_add_driver(&pcf8583_driver);
+}
+
+static __exit void pcf8583_exit(void)
+{
+ i2c_del_driver(&pcf8583_driver);
+}
+
+module_init(pcf8583_init);
+module_exit(pcf8583_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("PCF8583 I2C RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
new file mode 100644
index 00000000000..d6d1c5726b0
--- /dev/null
+++ b/drivers/rtc/rtc-pl031.c
@@ -0,0 +1,233 @@
+/*
+ * drivers/rtc/rtc-pl031.c
+ *
+ * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2006 (c) MontaVista Software, Inc.
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+
+#include <linux/amba/bus.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+/*
+ * Register definitions
+ */
+#define RTC_DR 0x00 /* Data read register */
+#define RTC_MR 0x04 /* Match register */
+#define RTC_LR 0x08 /* Data load register */
+#define RTC_CR 0x0c /* Control register */
+#define RTC_IMSC 0x10 /* Interrupt mask and set register */
+#define RTC_RIS 0x14 /* Raw interrupt status register */
+#define RTC_MIS 0x18 /* Masked interrupt status register */
+#define RTC_ICR 0x1c /* Interrupt clear register */
+
+struct pl031_local {
+ struct rtc_device *rtc;
+ void __iomem *base;
+};
+
+static irqreturn_t pl031_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct rtc_device *rtc = dev_id;
+
+ rtc_update_irq(&rtc->class_dev, 1, RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int pl031_open(struct device *dev)
+{
+ /*
+ * We request IRQ in pl031_probe, so nothing to do here...
+ */
+ return 0;
+}
+
+static void pl031_release(struct device *dev)
+{
+}
+
+static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ __raw_writel(1, ldata->base + RTC_MIS);
+ return 0;
+ case RTC_AIE_ON:
+ __raw_writel(0, ldata->base + RTC_MIS);
+ return 0;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int pl031_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(__raw_readl(ldata->base + RTC_DR), tm);
+
+ return 0;
+}
+
+static int pl031_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ rtc_tm_to_time(tm, &time);
+ __raw_writel(time, ldata->base + RTC_LR);
+
+ return 0;
+}
+
+static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(__raw_readl(ldata->base + RTC_MR), &alarm->time);
+ alarm->pending = __raw_readl(ldata->base + RTC_RIS);
+ alarm->enabled = __raw_readl(ldata->base + RTC_IMSC);
+
+ return 0;
+}
+
+static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long time;
+
+ rtc_tm_to_time(&alarm->time, &time);
+
+ __raw_writel(time, ldata->base + RTC_MR);
+ __raw_writel(!alarm->enabled, ldata->base + RTC_MIS);
+
+ return 0;
+}
+
+static struct rtc_class_ops pl031_ops = {
+ .open = pl031_open,
+ .release = pl031_release,
+ .ioctl = pl031_ioctl,
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+};
+
+static int pl031_remove(struct amba_device *adev)
+{
+ struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
+
+ if (ldata) {
+ dev_set_drvdata(&adev->dev, NULL);
+ free_irq(adev->irq[0], ldata->rtc);
+ rtc_device_unregister(ldata->rtc);
+ iounmap(ldata->base);
+ kfree(ldata);
+ }
+
+ return 0;
+}
+
+static int pl031_probe(struct amba_device *adev, void *id)
+{
+ int ret;
+ struct pl031_local *ldata;
+
+
+ ldata = kmalloc(sizeof(struct pl031_local), GFP_KERNEL);
+ if (!ldata) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ dev_set_drvdata(&adev->dev, ldata);
+
+ ldata->base = ioremap(adev->res.start,
+ adev->res.end - adev->res.start + 1);
+ if (!ldata->base) {
+ ret = -ENOMEM;
+ goto out_no_remap;
+ }
+
+ if (request_irq(adev->irq[0], pl031_interrupt, IRQF_DISABLED,
+ "rtc-pl031", ldata->rtc)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+
+ ldata->rtc = rtc_device_register("pl031", &adev->dev, &pl031_ops,
+ THIS_MODULE);
+ if (IS_ERR(ldata->rtc)) {
+ ret = PTR_ERR(ldata->rtc);
+ goto out_no_rtc;
+ }
+
+ return 0;
+
+out_no_rtc:
+ free_irq(adev->irq[0], ldata->rtc);
+out_no_irq:
+ iounmap(ldata->base);
+out_no_remap:
+ dev_set_drvdata(&adev->dev, NULL);
+ kfree(ldata);
+out:
+ return ret;
+}
+
+static struct amba_id pl031_ids[] __initdata = {
+ {
+ .id = 0x00041031,
+ .mask = 0x000fffff, },
+ {0, 0},
+};
+
+static struct amba_driver pl031_driver = {
+ .drv = {
+ .name = "rtc-pl031",
+ },
+ .id_table = pl031_ids,
+ .probe = pl031_probe,
+ .remove = pl031_remove,
+};
+
+static int __init pl031_init(void)
+{
+ return amba_driver_register(&pl031_driver);
+}
+
+static void __exit pl031_exit(void)
+{
+ amba_driver_unregister(&pl031_driver);
+}
+
+module_init(pl031_init);
+module_exit(pl031_exit);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
+MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
new file mode 100644
index 00000000000..0964d1dba92
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -0,0 +1,246 @@
+/*
+ * A SPI driver for the Ricoh RS5C348 RTC
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The board specific init code should provide characteristics of this
+ * device:
+ * Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
+ */
+
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+#define DRV_VERSION "0.1"
+
+#define RS5C348_REG_SECS 0
+#define RS5C348_REG_MINS 1
+#define RS5C348_REG_HOURS 2
+#define RS5C348_REG_WDAY 3
+#define RS5C348_REG_DAY 4
+#define RS5C348_REG_MONTH 5
+#define RS5C348_REG_YEAR 6
+#define RS5C348_REG_CTL1 14
+#define RS5C348_REG_CTL2 15
+
+#define RS5C348_SECS_MASK 0x7f
+#define RS5C348_MINS_MASK 0x7f
+#define RS5C348_HOURS_MASK 0x3f
+#define RS5C348_WDAY_MASK 0x03
+#define RS5C348_DAY_MASK 0x3f
+#define RS5C348_MONTH_MASK 0x1f
+
+#define RS5C348_BIT_PM 0x20 /* REG_HOURS */
+#define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */
+#define RS5C348_BIT_24H 0x20 /* REG_CTL1 */
+#define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */
+#define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */
+
+#define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */
+#define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */
+#define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */
+#define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */
+
+struct rs5c348_plat_data {
+ struct rtc_device *rtc;
+ int rtc_24h;
+};
+
+static int
+rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ u8 txbuf[5+7], *txp;
+ int ret;
+
+ /* Transfer 5 bytes before writing SEC. This gives 31us for carry. */
+ txp = txbuf;
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */
+ txp = &txbuf[5];
+ txp[RS5C348_REG_SECS] = BIN2BCD(tm->tm_sec);
+ txp[RS5C348_REG_MINS] = BIN2BCD(tm->tm_min);
+ if (pdata->rtc_24h) {
+ txp[RS5C348_REG_HOURS] = BIN2BCD(tm->tm_hour);
+ } else {
+ /* hour 0 is AM12, noon is PM12 */
+ txp[RS5C348_REG_HOURS] = BIN2BCD((tm->tm_hour + 11) % 12 + 1) |
+ (tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0);
+ }
+ txp[RS5C348_REG_WDAY] = BIN2BCD(tm->tm_wday);
+ txp[RS5C348_REG_DAY] = BIN2BCD(tm->tm_mday);
+ txp[RS5C348_REG_MONTH] = BIN2BCD(tm->tm_mon + 1) |
+ (tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0);
+ txp[RS5C348_REG_YEAR] = BIN2BCD(tm->tm_year % 100);
+ /* write in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0);
+ udelay(62); /* Tcsr 62us */
+ return ret;
+}
+
+static int
+rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ u8 txbuf[5], rxbuf[7];
+ int ret;
+
+ /* Transfer 5 byte befores reading SEC. This gives 31us for carry. */
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */
+
+ /* read in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
+ rxbuf, sizeof(rxbuf));
+ udelay(62); /* Tcsr 62us */
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = BCD2BIN(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK);
+ tm->tm_min = BCD2BIN(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
+ tm->tm_hour = BCD2BIN(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
+ if (!pdata->rtc_24h) {
+ tm->tm_hour %= 12;
+ if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM)
+ tm->tm_hour += 12;
+ }
+ tm->tm_wday = BCD2BIN(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
+ tm->tm_mday = BCD2BIN(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
+ tm->tm_mon =
+ BCD2BIN(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = BCD2BIN(rxbuf[RS5C348_REG_YEAR]) +
+ ((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(&spi->dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+
+ return 0;
+}
+
+static struct rtc_class_ops rs5c348_rtc_ops = {
+ .read_time = rs5c348_rtc_read_time,
+ .set_time = rs5c348_rtc_set_time,
+};
+
+static struct spi_driver rs5c348_driver;
+
+static int __devinit rs5c348_probe(struct spi_device *spi)
+{
+ int ret;
+ struct rtc_device *rtc;
+ struct rs5c348_plat_data *pdata;
+
+ pdata = kzalloc(sizeof(struct rs5c348_plat_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ spi->dev.platform_data = pdata;
+
+ /* Check D7 of SECOND register */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
+ if (ret < 0 || (ret & 0x80)) {
+ dev_err(&spi->dev, "not found.\n");
+ goto kfree_exit;
+ }
+
+ dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
+ dev_info(&spi->dev, "spiclk %u KHz.\n",
+ (spi->max_speed_hz + 500) / 1000);
+
+ /* turn RTC on if it was not on */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
+ u8 buf[2];
+ if (ret & RS5C348_BIT_VDET)
+ dev_warn(&spi->dev, "voltage-low detected.\n");
+ buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
+ buf[1] = 0;
+ ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+ if (ret < 0)
+ goto kfree_exit;
+ }
+
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & RS5C348_BIT_24H)
+ pdata->rtc_24h = 1;
+
+ rtc = rtc_device_register(rs5c348_driver.driver.name, &spi->dev,
+ &rs5c348_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto kfree_exit;
+ }
+
+ pdata->rtc = rtc;
+
+ return 0;
+ kfree_exit:
+ kfree(pdata);
+ return ret;
+}
+
+static int __devexit rs5c348_remove(struct spi_device *spi)
+{
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ struct rtc_device *rtc = pdata->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+ kfree(pdata);
+ return 0;
+}
+
+static struct spi_driver rs5c348_driver = {
+ .driver = {
+ .name = "rs5c348",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = rs5c348_probe,
+ .remove = __devexit_p(rs5c348_remove),
+};
+
+static __init int rs5c348_init(void)
+{
+ return spi_register_driver(&rs5c348_driver);
+}
+
+static __exit void rs5c348_exit(void)
+{
+ spi_unregister_driver(&rs5c348_driver);
+}
+
+module_init(rs5c348_init);
+module_exit(rs5c348_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
new file mode 100644
index 00000000000..d6d1bff52b8
--- /dev/null
+++ b/drivers/rtc/rtc-s3c.c
@@ -0,0 +1,607 @@
+/* drivers/rtc/rtc-s3c.c
+ *
+ * Copyright (c) 2004,2006 Simtec Electronics
+ * Ben Dooks, <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C2410/S3C2440/S3C24XX Internal RTC Driver
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+#include <asm/mach/time.h>
+
+#include <asm/arch/regs-rtc.h>
+
+/* I have yet to find an S3C implementation with more than one
+ * of these rtc blocks in */
+
+static struct resource *s3c_rtc_mem;
+
+static void __iomem *s3c_rtc_base;
+static int s3c_rtc_alarmno = NO_IRQ;
+static int s3c_rtc_tickno = NO_IRQ;
+static int s3c_rtc_freq = 1;
+
+static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
+static unsigned int tick_count;
+
+/* IRQ Handlers */
+
+static irqreturn_t s3c_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
+{
+ struct rtc_device *rdev = id;
+
+ rtc_update_irq(&rdev->class_dev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c_rtc_tickirq(int irq, void *id, struct pt_regs *r)
+{
+ struct rtc_device *rdev = id;
+
+ rtc_update_irq(&rdev->class_dev, tick_count++, RTC_PF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+/* Update control registers */
+static void s3c_rtc_setaie(int to)
+{
+ unsigned int tmp;
+
+ pr_debug("%s: aie=%d\n", __FUNCTION__, to);
+
+ tmp = readb(S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
+
+ if (to)
+ tmp |= S3C2410_RTCALM_ALMEN;
+
+ writeb(tmp, S3C2410_RTCALM);
+}
+
+static void s3c_rtc_setpie(int to)
+{
+ unsigned int tmp;
+
+ pr_debug("%s: pie=%d\n", __FUNCTION__, to);
+
+ spin_lock_irq(&s3c_rtc_pie_lock);
+ tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
+
+ if (to)
+ tmp |= S3C2410_TICNT_ENABLE;
+
+ writeb(tmp, S3C2410_TICNT);
+ spin_unlock_irq(&s3c_rtc_pie_lock);
+}
+
+static void s3c_rtc_setfreq(int freq)
+{
+ unsigned int tmp;
+
+ spin_lock_irq(&s3c_rtc_pie_lock);
+ tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
+
+ s3c_rtc_freq = freq;
+
+ tmp |= (128 / freq)-1;
+
+ writeb(tmp, S3C2410_TICNT);
+ spin_unlock_irq(&s3c_rtc_pie_lock);
+}
+
+/* Time read/write */
+
+static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ unsigned int have_retried = 0;
+
+ retry_get_time:
+ rtc_tm->tm_min = readb(S3C2410_RTCMIN);
+ rtc_tm->tm_hour = readb(S3C2410_RTCHOUR);
+ rtc_tm->tm_mday = readb(S3C2410_RTCDATE);
+ rtc_tm->tm_mon = readb(S3C2410_RTCMON);
+ rtc_tm->tm_year = readb(S3C2410_RTCYEAR);
+ rtc_tm->tm_sec = readb(S3C2410_RTCSEC);
+
+ /* the only way to work out wether the system was mid-update
+ * when we read it is to check the second counter, and if it
+ * is zero, then we re-try the entire read
+ */
+
+ if (rtc_tm->tm_sec == 0 && !have_retried) {
+ have_retried = 1;
+ goto retry_get_time;
+ }
+
+ pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
+ rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+ BCD_TO_BIN(rtc_tm->tm_sec);
+ BCD_TO_BIN(rtc_tm->tm_min);
+ BCD_TO_BIN(rtc_tm->tm_hour);
+ BCD_TO_BIN(rtc_tm->tm_mday);
+ BCD_TO_BIN(rtc_tm->tm_mon);
+ BCD_TO_BIN(rtc_tm->tm_year);
+
+ rtc_tm->tm_year += 100;
+ rtc_tm->tm_mon -= 1;
+
+ return 0;
+}
+
+static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ /* the rtc gets round the y2k problem by just not supporting it */
+
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC);
+ writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN);
+ writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR);
+ writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE);
+ writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON);
+ writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR);
+
+ return 0;
+}
+
+static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *alm_tm = &alrm->time;
+ unsigned int alm_en;
+
+ alm_tm->tm_sec = readb(S3C2410_ALMSEC);
+ alm_tm->tm_min = readb(S3C2410_ALMMIN);
+ alm_tm->tm_hour = readb(S3C2410_ALMHOUR);
+ alm_tm->tm_mon = readb(S3C2410_ALMMON);
+ alm_tm->tm_mday = readb(S3C2410_ALMDATE);
+ alm_tm->tm_year = readb(S3C2410_ALMYEAR);
+
+ alm_en = readb(S3C2410_RTCALM);
+
+ pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+ alm_en,
+ alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+
+ /* decode the alarm enable field */
+
+ if (alm_en & S3C2410_RTCALM_SECEN)
+ BCD_TO_BIN(alm_tm->tm_sec);
+ else
+ alm_tm->tm_sec = 0xff;
+
+ if (alm_en & S3C2410_RTCALM_MINEN)
+ BCD_TO_BIN(alm_tm->tm_min);
+ else
+ alm_tm->tm_min = 0xff;
+
+ if (alm_en & S3C2410_RTCALM_HOUREN)
+ BCD_TO_BIN(alm_tm->tm_hour);
+ else
+ alm_tm->tm_hour = 0xff;
+
+ if (alm_en & S3C2410_RTCALM_DAYEN)
+ BCD_TO_BIN(alm_tm->tm_mday);
+ else
+ alm_tm->tm_mday = 0xff;
+
+ if (alm_en & S3C2410_RTCALM_MONEN) {
+ BCD_TO_BIN(alm_tm->tm_mon);
+ alm_tm->tm_mon -= 1;
+ } else {
+ alm_tm->tm_mon = 0xff;
+ }
+
+ if (alm_en & S3C2410_RTCALM_YEAREN)
+ BCD_TO_BIN(alm_tm->tm_year);
+ else
+ alm_tm->tm_year = 0xffff;
+
+ return 0;
+}
+
+static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+ unsigned int alrm_en;
+
+ pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+ alrm->enabled,
+ tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
+ tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+
+
+ alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
+ writeb(0x00, S3C2410_RTCALM);
+
+ if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
+ alrm_en |= S3C2410_RTCALM_SECEN;
+ writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC);
+ }
+
+ if (tm->tm_min < 60 && tm->tm_min >= 0) {
+ alrm_en |= S3C2410_RTCALM_MINEN;
+ writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN);
+ }
+
+ if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
+ alrm_en |= S3C2410_RTCALM_HOUREN;
+ writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR);
+ }
+
+ pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
+
+ writeb(alrm_en, S3C2410_RTCALM);
+
+ if (0) {
+ alrm_en = readb(S3C2410_RTCALM);
+ alrm_en &= ~S3C2410_RTCALM_ALMEN;
+ writeb(alrm_en, S3C2410_RTCALM);
+ disable_irq_wake(s3c_rtc_alarmno);
+ }
+
+ if (alrm->enabled)
+ enable_irq_wake(s3c_rtc_alarmno);
+ else
+ disable_irq_wake(s3c_rtc_alarmno);
+
+ return 0;
+}
+
+static int s3c_rtc_ioctl(struct device *dev,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ case RTC_AIE_ON:
+ s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
+ ret = 0;
+ break;
+
+ case RTC_PIE_OFF:
+ case RTC_PIE_ON:
+ tick_count = 0;
+ s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
+ ret = 0;
+ break;
+
+ case RTC_IRQP_READ:
+ ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg);
+ break;
+
+ case RTC_IRQP_SET:
+ /* check for power of 2 */
+
+ if ((arg & (arg-1)) != 0 || arg < 1) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
+
+ s3c_rtc_setfreq(arg);
+ ret = 0;
+ break;
+
+ case RTC_UIE_ON:
+ case RTC_UIE_OFF:
+ ret = -EINVAL;
+ }
+
+ exit:
+ return ret;
+}
+
+static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned int rtcalm = readb(S3C2410_RTCALM);
+ unsigned int ticnt = readb (S3C2410_TICNT);
+
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
+
+ seq_printf(seq, "periodic_freq\t: %d\n", s3c_rtc_freq);
+
+ return 0;
+}
+
+static int s3c_rtc_open(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
+ SA_INTERRUPT, "s3c2410-rtc alarm", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
+ return ret;
+ }
+
+ ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
+ SA_INTERRUPT, "s3c2410-rtc tick", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
+ goto tick_err;
+ }
+
+ return ret;
+
+ tick_err:
+ free_irq(s3c_rtc_alarmno, rtc_dev);
+ return ret;
+}
+
+static void s3c_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+
+ /* do not clear AIE here, it may be needed for wake */
+
+ s3c_rtc_setpie(0);
+ free_irq(s3c_rtc_alarmno, rtc_dev);
+ free_irq(s3c_rtc_tickno, rtc_dev);
+}
+
+static struct rtc_class_ops s3c_rtcops = {
+ .open = s3c_rtc_open,
+ .release = s3c_rtc_release,
+ .ioctl = s3c_rtc_ioctl,
+ .read_time = s3c_rtc_gettime,
+ .set_time = s3c_rtc_settime,
+ .read_alarm = s3c_rtc_getalarm,
+ .set_alarm = s3c_rtc_setalarm,
+ .proc = s3c_rtc_proc,
+};
+
+static void s3c_rtc_enable(struct platform_device *pdev, int en)
+{
+ unsigned int tmp;
+
+ if (s3c_rtc_base == NULL)
+ return;
+
+ if (!en) {
+ tmp = readb(S3C2410_RTCCON);
+ writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
+
+ tmp = readb(S3C2410_TICNT);
+ writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT);
+ } else {
+ /* re-enable the device, and check it is ok */
+
+ if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
+ dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
+
+ tmp = readb(S3C2410_RTCCON);
+ writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON);
+ }
+
+ if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
+ dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
+
+ tmp = readb(S3C2410_RTCCON);
+ writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON);
+ }
+
+ if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
+ dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
+
+ tmp = readb(S3C2410_RTCCON);
+ writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON);
+ }
+ }
+}
+
+static int s3c_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+ rtc_device_unregister(rtc);
+
+ s3c_rtc_setpie(0);
+ s3c_rtc_setaie(0);
+
+ iounmap(s3c_rtc_base);
+ release_resource(s3c_rtc_mem);
+ kfree(s3c_rtc_mem);
+
+ return 0;
+}
+
+static int s3c_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ int ret;
+
+ pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);
+
+ /* find the IRQs */
+
+ s3c_rtc_tickno = platform_get_irq(pdev, 1);
+ if (s3c_rtc_tickno < 0) {
+ dev_err(&pdev->dev, "no irq for rtc tick\n");
+ return -ENOENT;
+ }
+
+ s3c_rtc_alarmno = platform_get_irq(pdev, 0);
+ if (s3c_rtc_alarmno < 0) {
+ dev_err(&pdev->dev, "no irq for alarm\n");
+ return -ENOENT;
+ }
+
+ pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
+ s3c_rtc_tickno, s3c_rtc_alarmno);
+
+ /* get the memory region */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ s3c_rtc_mem = request_mem_region(res->start,
+ res->end-res->start+1,
+ pdev->name);
+
+ if (s3c_rtc_mem == NULL) {
+ dev_err(&pdev->dev, "failed to reserve memory region\n");
+ ret = -ENOENT;
+ goto err_nores;
+ }
+
+ s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
+ if (s3c_rtc_base == NULL) {
+ dev_err(&pdev->dev, "failed ioremap()\n");
+ ret = -EINVAL;
+ goto err_nomap;
+ }
+
+ /* check to see if everything is setup correctly */
+
+ s3c_rtc_enable(pdev, 1);
+
+ pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
+
+ s3c_rtc_setfreq(s3c_rtc_freq);
+
+ /* register RTC and exit */
+
+ rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "cannot attach rtc\n");
+ ret = PTR_ERR(rtc);
+ goto err_nortc;
+ }
+
+ rtc->max_user_freq = 128;
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+
+ err_nortc:
+ s3c_rtc_enable(pdev, 0);
+ iounmap(s3c_rtc_base);
+
+ err_nomap:
+ release_resource(s3c_rtc_mem);
+
+ err_nores:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+
+/* RTC Power management control */
+
+static struct timespec s3c_rtc_delta;
+
+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(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, S3C2410_TICNT);
+ return 0;
+}
+#else
+#define s3c_rtc_suspend NULL
+#define s3c_rtc_resume NULL
+#endif
+
+static struct platform_driver s3c2410_rtcdrv = {
+ .probe = s3c_rtc_probe,
+ .remove = s3c_rtc_remove,
+ .suspend = s3c_rtc_suspend,
+ .resume = s3c_rtc_resume,
+ .driver = {
+ .name = "s3c2410-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
+
+static int __init s3c_rtc_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&s3c2410_rtcdrv);
+}
+
+static void __exit s3c_rtc_exit(void)
+{
+ platform_driver_unregister(&s3c2410_rtcdrv);
+}
+
+module_init(s3c_rtc_init);
+module_exit(s3c_rtc_exit);
+
+MODULE_DESCRIPTION("Samsung S3C RTC Driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index a997529f892..ee4b61ee67b 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -45,7 +45,7 @@
static unsigned long rtc_freq = 1024;
static struct rtc_time rtc_alarm;
-static spinlock_t sa1100_rtc_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sa1100_rtc_lock);
static int rtc_update_alarm(struct rtc_time *alrm)
{
@@ -157,19 +157,19 @@ static int sa1100_rtc_open(struct device *dev)
{
int ret;
- ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, SA_INTERRUPT,
+ ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
"rtc 1Hz", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz);
goto fail_ui;
}
- ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, SA_INTERRUPT,
+ ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED,
"rtc Alrm", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm);
goto fail_ai;
}
- ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT,
+ ret = request_irq(IRQ_OST1, timer1_interrupt, IRQF_DISABLED,
"rtc timer", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1);
@@ -229,8 +229,6 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_PIE_ON:
- if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE))
- return -EACCES;
spin_lock_irq(&sa1100_rtc_lock);
OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
OIER |= OIER_E1;
@@ -242,8 +240,6 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
case RTC_IRQP_SET:
if (arg < 1 || arg > TIMER_FREQ)
return -EINVAL;
- if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
- return -EACCES;
rtc_freq = arg;
return 0;
}
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
new file mode 100644
index 00000000000..a40f400acff
--- /dev/null
+++ b/drivers/rtc/rtc-v3020.c
@@ -0,0 +1,264 @@
+/* drivers/rtc/rtc-v3020.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for the V3020 RTC
+ *
+ * Changelog:
+ *
+ * 10-May-2006: Raphael Assenat <raph@8d.com>
+ * - Converted to platform driver
+ * - Use the generic rtc class
+ *
+ * ??-???-2004: Someone at Compulab
+ * - Initial driver creation.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+#include <linux/bcd.h>
+#include <linux/rtc-v3020.h>
+
+#include <asm/io.h>
+
+#undef DEBUG
+
+struct v3020 {
+ void __iomem *ioaddress;
+ int leftshift;
+ struct rtc_device *rtc;
+};
+
+static void v3020_set_reg(struct v3020 *chip, unsigned char address,
+ unsigned char data)
+{
+ int i;
+ unsigned char tmp;
+
+ tmp = address;
+ for (i = 0; i < 4; i++) {
+ writel((tmp & 1) << chip->leftshift, chip->ioaddress);
+ tmp >>= 1;
+ }
+
+ /* Commands dont have data */
+ if (!V3020_IS_COMMAND(address)) {
+ for (i = 0; i < 8; i++) {
+ writel((data & 1) << chip->leftshift, chip->ioaddress);
+ data >>= 1;
+ }
+ }
+}
+
+static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
+{
+ unsigned int data=0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ writel((address & 1) << chip->leftshift, chip->ioaddress);
+ address >>= 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ data >>= 1;
+ if (readl(chip->ioaddress) & (1 << chip->leftshift))
+ data |= 0x80;
+ }
+
+ return data;
+}
+
+static int v3020_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct v3020 *chip = dev_get_drvdata(dev);
+ int tmp;
+
+ /* Copy the current time to ram... */
+ v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0);
+
+ /* ...and then read constant values. */
+ tmp = v3020_get_reg(chip, V3020_SECONDS);
+ dt->tm_sec = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_MINUTES);
+ dt->tm_min = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_HOURS);
+ dt->tm_hour = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_MONTH_DAY);
+ dt->tm_mday = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_MONTH);
+ dt->tm_mon = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_WEEK_DAY);
+ dt->tm_wday = BCD2BIN(tmp);
+ tmp = v3020_get_reg(chip, V3020_YEAR);
+ dt->tm_year = BCD2BIN(tmp)+100;
+
+#ifdef DEBUG
+ printk("\n%s : Read RTC values\n",__FUNCTION__);
+ printk("tm_hour: %i\n",dt->tm_hour);
+ printk("tm_min : %i\n",dt->tm_min);
+ printk("tm_sec : %i\n",dt->tm_sec);
+ printk("tm_year: %i\n",dt->tm_year);
+ printk("tm_mon : %i\n",dt->tm_mon);
+ printk("tm_mday: %i\n",dt->tm_mday);
+ printk("tm_wday: %i\n",dt->tm_wday);
+#endif
+
+ return 0;
+}
+
+
+static int v3020_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct v3020 *chip = dev_get_drvdata(dev);
+
+#ifdef DEBUG
+ printk("\n%s : Setting RTC values\n",__FUNCTION__);
+ printk("tm_sec : %i\n",dt->tm_sec);
+ printk("tm_min : %i\n",dt->tm_min);
+ printk("tm_hour: %i\n",dt->tm_hour);
+ printk("tm_mday: %i\n",dt->tm_mday);
+ printk("tm_wday: %i\n",dt->tm_wday);
+ printk("tm_year: %i\n",dt->tm_year);
+#endif
+
+ /* Write all the values to ram... */
+ v3020_set_reg(chip, V3020_SECONDS, BIN2BCD(dt->tm_sec));
+ v3020_set_reg(chip, V3020_MINUTES, BIN2BCD(dt->tm_min));
+ v3020_set_reg(chip, V3020_HOURS, BIN2BCD(dt->tm_hour));
+ v3020_set_reg(chip, V3020_MONTH_DAY, BIN2BCD(dt->tm_mday));
+ v3020_set_reg(chip, V3020_MONTH, BIN2BCD(dt->tm_mon));
+ v3020_set_reg(chip, V3020_WEEK_DAY, BIN2BCD(dt->tm_wday));
+ v3020_set_reg(chip, V3020_YEAR, BIN2BCD(dt->tm_year % 100));
+
+ /* ...and set the clock. */
+ v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0);
+
+ /* Compulab used this delay here. I dont know why,
+ * the datasheet does not specify a delay. */
+ /*mdelay(5);*/
+
+ return 0;
+}
+
+static struct rtc_class_ops v3020_rtc_ops = {
+ .read_time = v3020_read_time,
+ .set_time = v3020_set_time,
+};
+
+static int rtc_probe(struct platform_device *pdev)
+{
+ struct v3020_platform_data *pdata = pdev->dev.platform_data;
+ struct v3020 *chip;
+ struct rtc_device *rtc;
+ int retval = -EBUSY;
+ int i;
+ int temp;
+
+ if (pdev->num_resources != 1)
+ return -EBUSY;
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM)
+ return -EBUSY;
+
+ if (pdev == NULL)
+ return -EBUSY;
+
+ chip = kzalloc(sizeof *chip, GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->leftshift = pdata->leftshift;
+ chip->ioaddress = ioremap(pdev->resource[0].start, 1);
+ if (chip->ioaddress == NULL)
+ goto err_chip;
+
+ /* Make sure the v3020 expects a communication cycle
+ * by reading 8 times */
+ for (i = 0; i < 8; i++)
+ temp = readl(chip->ioaddress);
+
+ /* Test chip by doing a write/read sequence
+ * to the chip ram */
+ v3020_set_reg(chip, V3020_SECONDS, 0x33);
+ if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) {
+ retval = -ENODEV;
+ goto err_io;
+ }
+
+ /* Make sure frequency measurment mode, test modes, and lock
+ * are all disabled */
+ v3020_set_reg(chip, V3020_STATUS_0, 0x0);
+
+ dev_info(&pdev->dev, "Chip available at physical address 0x%p,"
+ "data connected to D%d\n",
+ (void*)pdev->resource[0].start,
+ chip->leftshift);
+
+ platform_set_drvdata(pdev, chip);
+
+ rtc = rtc_device_register("v3020",
+ &pdev->dev, &v3020_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ retval = PTR_ERR(rtc);
+ goto err_io;
+ }
+ chip->rtc = rtc;
+
+ return 0;
+
+err_io:
+ iounmap(chip->ioaddress);
+err_chip:
+ kfree(chip);
+
+ return retval;
+}
+
+static int rtc_remove(struct platform_device *dev)
+{
+ struct v3020 *chip = platform_get_drvdata(dev);
+ struct rtc_device *rtc = chip->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ iounmap(chip->ioaddress);
+ kfree(chip);
+
+ return 0;
+}
+
+static struct platform_driver rtc_device_driver = {
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+ .driver = {
+ .name = "v3020",
+ .owner = THIS_MODULE,
+ },
+};
+
+static __init int v3020_init(void)
+{
+ return platform_driver_register(&rtc_device_driver);
+}
+
+static __exit void v3020_exit(void)
+{
+ platform_driver_unregister(&rtc_device_driver);
+}
+
+module_init(v3020_init);
+module_exit(v3020_exit);
+
+MODULE_DESCRIPTION("V3020 RTC");
+MODULE_AUTHOR("Raphael Assenat");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 277596c302e..bb6d5ff24fd 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -81,7 +81,6 @@ MODULE_LICENSE("GPL");
#define RTC_FREQUENCY 32768
#define MAX_PERIODIC_RATE 6553
-#define MAX_USER_PERIODIC_RATE 64
static void __iomem *rtc1_base;
static void __iomem *rtc2_base;
@@ -94,7 +93,7 @@ static void __iomem *rtc2_base;
static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
-static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(rtc_lock);
static char rtc_name[] = "RTC";
static unsigned long periodic_frequency;
static unsigned long periodic_count;
@@ -240,9 +239,6 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long
if (arg > MAX_PERIODIC_RATE)
return -EINVAL;
- if (arg > MAX_USER_PERIODIC_RATE && capable(CAP_SYS_RESOURCE) == 0)
- return -EACCES;
-
periodic_frequency = arg;
count = RTC_FREQUENCY;
@@ -263,10 +259,6 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long
/* Doesn't support before 1900 */
if (arg < 1900)
return -EINVAL;
-
- if (capable(CAP_SYS_TIME) == 0)
- return -EACCES;
-
epoch = arg;
break;
default:
@@ -353,11 +345,11 @@ static int __devinit rtc_probe(struct platform_device *pdev)
spin_unlock_irq(&rtc_lock);
irq = ELAPSEDTIME_IRQ;
- retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT,
+ retval = request_irq(irq, elapsedtime_interrupt, IRQF_DISABLED,
"elapsed_time", pdev);
if (retval == 0) {
irq = RTCLONG1_IRQ;
- retval = request_irq(irq, rtclong1_interrupt, SA_INTERRUPT,
+ retval = request_irq(irq, rtclong1_interrupt, IRQF_DISABLED,
"rtclong1", pdev);
}