aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@insightbb.com>2006-12-08 01:07:56 -0500
committerDmitry Torokhov <dtor@insightbb.com>2006-12-08 01:07:56 -0500
commitbef986502fa398b1785a3979b1aa17cd902d3527 (patch)
treeb59c1afe7b1dfcc001b86e54863f550d7ddc8c34 /drivers/rtc
parent4bdbd2807deeccc0793d57fb5120d7a53f2c0b3c (diff)
parentc99767974ebd2a719d849fdeaaa1674456f5283f (diff)
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/usb/input/hid.h
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig12
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/interface.c16
-rw-r--r--drivers/rtc/rtc-at91.c3
-rw-r--r--drivers/rtc/rtc-dev.c32
-rw-r--r--drivers/rtc/rtc-ds1553.c3
-rw-r--r--drivers/rtc/rtc-ds1672.c9
-rw-r--r--drivers/rtc/rtc-ds1742.c67
-rw-r--r--drivers/rtc/rtc-omap.c572
-rw-r--r--drivers/rtc/rtc-rs5c372.c96
-rw-r--r--drivers/rtc/rtc-test.c11
-rw-r--r--drivers/rtc/rtc-x1205.c12
12 files changed, 742 insertions, 92 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index fc766a7a611..2a63ab2b47f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -154,15 +154,23 @@ config RTC_DRV_DS1672
will be called rtc-ds1672.
config RTC_DRV_DS1742
- tristate "Dallas DS1742"
+ tristate "Dallas DS1742/1743"
depends on RTC_CLASS
help
If you say yes here you get support for the
- Dallas DS1742 timekeeping chip.
+ Dallas DS1742/1743 timekeeping chip.
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.
+config RTC_DRV_OMAP
+ tristate "TI OMAP1"
+ depends on RTC_CLASS && ( \
+ ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 )
+ help
+ Say "yes" here to support the real time clock on TI OMAP1 chips.
+ This driver can also be built as a module called rtc-omap.
+
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
depends on RTC_CLASS && I2C
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 3ba5ff6e680..bd4c45d333f 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -21,6 +21,7 @@ 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_OMAP) += rtc-omap.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
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 579cd667b16..6f11f6dfdd9 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -145,6 +145,13 @@ int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
+/**
+ * rtc_update_irq - report RTC periodic, alarm, and/or update irqs
+ * @class_dev: the rtc's class device
+ * @num: how many irqs are being reported (usually one)
+ * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
+ * Context: in_interrupt(), irqs blocked
+ */
void rtc_update_irq(struct class_device *class_dev,
unsigned long num, unsigned long events)
{
@@ -201,12 +208,12 @@ int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
if (task == NULL || task->func == NULL)
return -EINVAL;
- spin_lock(&rtc->irq_task_lock);
+ spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == NULL) {
rtc->irq_task = task;
retval = 0;
}
- spin_unlock(&rtc->irq_task_lock);
+ spin_unlock_irq(&rtc->irq_task_lock);
return retval;
}
@@ -216,10 +223,10 @@ void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
{
struct rtc_device *rtc = to_rtc_device(class_dev);
- spin_lock(&rtc->irq_task_lock);
+ spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == task)
rtc->irq_task = NULL;
- spin_unlock(&rtc->irq_task_lock);
+ spin_unlock_irq(&rtc->irq_task_lock);
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);
@@ -265,3 +272,4 @@ int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int
}
return err;
}
+EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91.c
index bd61e99540a..5c8addcaf1f 100644
--- a/drivers/rtc/rtc-at91.c
+++ b/drivers/rtc/rtc-at91.c
@@ -292,7 +292,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
AT91_RTC_CALEV);
ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
- IRQF_SHARED, "at91_rtc", pdev);
+ IRQF_DISABLED | IRQF_SHARED,
+ "at91_rtc", pdev);
if (ret) {
printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
AT91_ID_SYS);
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 583789c66cd..828b329e08e 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -53,15 +53,18 @@ static int rtc_dev_open(struct inode *inode, struct file *file)
* 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)
+static void rtc_uie_task(struct work_struct *work)
{
- struct rtc_device *rtc = data;
+ struct rtc_device *rtc =
+ container_of(work, struct rtc_device, uie_task);
struct rtc_time tm;
int num = 0;
int err;
err = rtc_read_time(&rtc->class_dev, &tm);
- spin_lock_irq(&rtc->irq_lock);
+
+ local_irq_disable();
+ spin_lock(&rtc->irq_lock);
if (rtc->stop_uie_polling || err) {
rtc->uie_task_active = 0;
} else if (rtc->oldsecs != tm.tm_sec) {
@@ -74,11 +77,11 @@ static void rtc_uie_task(void *data)
} else if (schedule_work(&rtc->uie_task) == 0) {
rtc->uie_task_active = 0;
}
- spin_unlock_irq(&rtc->irq_lock);
+ spin_unlock(&rtc->irq_lock);
if (num)
rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
+ local_irq_enable();
}
-
static void rtc_uie_timer(unsigned long data)
{
struct rtc_device *rtc = (struct rtc_device *)data;
@@ -214,7 +217,7 @@ 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
+ /* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver.
*/
@@ -238,10 +241,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
/* avoid conflicting IRQ users */
if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
- spin_lock(&rtc->irq_task_lock);
+ spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task)
err = -EBUSY;
- spin_unlock(&rtc->irq_task_lock);
+ spin_unlock_irq(&rtc->irq_task_lock);
if (err < 0)
return err;
@@ -299,6 +302,17 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
err = rtc_set_time(class_dev, &tm);
break;
+
+ case RTC_IRQP_READ:
+ if (ops->irq_set_freq)
+ err = put_user(rtc->irq_freq, (unsigned long *) arg);
+ break;
+
+ case RTC_IRQP_SET:
+ if (ops->irq_set_freq)
+ err = rtc_irq_set_freq(class_dev, rtc->irq_task, arg);
+ break;
+
#if 0
case RTC_EPOCH_SET:
#ifndef rtc_epoch
@@ -398,7 +412,7 @@ static int rtc_dev_add_device(struct class_device *class_dev,
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);
+ INIT_WORK(&rtc->uie_task, rtc_uie_task);
setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endif
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 78552e6e76a..001eb1123a6 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -340,7 +340,8 @@ static int __init ds1553_rtc_probe(struct platform_device *pdev)
if (pdata->irq >= 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
- if (request_irq(pdata->irq, ds1553_rtc_interrupt, IRQF_SHARED,
+ if (request_irq(pdata->irq, ds1553_rtc_interrupt,
+ IRQF_DISABLED | IRQF_SHARED,
pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = -1;
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 67e816a9a39..dfef1637bfb 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -237,17 +237,22 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
/* read control register */
err = ds1672_get_control(client, &control);
if (err)
- goto exit_detach;
+ goto exit_devreg;
if (control & DS1672_REG_CONTROL_EOSC)
dev_warn(&client->dev, "Oscillator not enabled. "
"Set time to enable.\n");
/* Register sysfs hooks */
- device_create_file(&client->dev, &dev_attr_control);
+ err = device_create_file(&client->dev, &dev_attr_control);
+ if (err)
+ goto exit_devreg;
return 0;
+exit_devreg:
+ rtc_device_unregister(rtc);
+
exit_detach:
i2c_detach_client(client);
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 6273a3d240a..17633bfa848 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -6,6 +6,10 @@
* 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.
+ *
+ * Copyright (C) 2006 Torsten Ertbjerg Rasmussen <tr@newtec.dk>
+ * - nvram size determined from resource
+ * - this ds1742 driver now supports ds1743.
*/
#include <linux/bcd.h>
@@ -17,20 +21,19 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#define DRV_VERSION "0.2"
+#define DRV_VERSION "0.3"
-#define RTC_REG_SIZE 0x800
-#define RTC_OFFSET 0x7f8
+#define RTC_SIZE 8
-#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_CONTROL 0
+#define RTC_CENTURY 0
+#define RTC_SECONDS 1
+#define RTC_MINUTES 2
+#define RTC_HOURS 3
+#define RTC_DAY 4
+#define RTC_DATE 5
+#define RTC_MONTH 6
+#define RTC_YEAR 7
#define RTC_CENTURY_MASK 0x3f
#define RTC_SECONDS_MASK 0x7f
@@ -48,7 +51,10 @@
struct rtc_plat_data {
struct rtc_device *rtc;
- void __iomem *ioaddr;
+ void __iomem *ioaddr_nvram;
+ void __iomem *ioaddr_rtc;
+ size_t size_nvram;
+ size_t size;
unsigned long baseaddr;
unsigned long last_jiffies;
};
@@ -57,7 +63,7 @@ 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;
+ void __iomem *ioaddr = pdata->ioaddr_rtc;
u8 century;
century = BIN2BCD((tm->tm_year + 1900) / 100);
@@ -82,7 +88,7 @@ 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;
+ void __iomem *ioaddr = pdata->ioaddr_rtc;
unsigned int year, month, day, hour, minute, second, week;
unsigned int century;
@@ -127,10 +133,10 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf,
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;
+ void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
- for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
*buf++ = readb(ioaddr + pos++);
return count;
}
@@ -141,10 +147,10 @@ static ssize_t ds1742_nvram_write(struct kobject *kobj, char *buf,
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;
+ void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
- for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
writeb(*buf++, ioaddr + pos++);
return count;
}
@@ -155,7 +161,6 @@ static struct bin_attribute ds1742_nvram_attr = {
.mode = S_IRUGO | S_IWUGO,
.owner = THIS_MODULE,
},
- .size = RTC_OFFSET,
.read = ds1742_nvram_read,
.write = ds1742_nvram_write,
};
@@ -175,19 +180,23 @@ static int __init ds1742_rtc_probe(struct platform_device *pdev)
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
- if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
+ pdata->size = res->end - res->start + 1;
+ if (!request_mem_region(res->start, pdata->size, pdev->name)) {
ret = -EBUSY;
goto out;
}
pdata->baseaddr = res->start;
- ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
+ ioaddr = ioremap(pdata->baseaddr, pdata->size);
if (!ioaddr) {
ret = -ENOMEM;
goto out;
}
- pdata->ioaddr = ioaddr;
+ pdata->ioaddr_nvram = ioaddr;
+ pdata->size_nvram = pdata->size - RTC_SIZE;
+ pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
/* turn RTC on if it was not on */
+ ioaddr = pdata->ioaddr_rtc;
sec = readb(ioaddr + RTC_SECONDS);
if (sec & RTC_STOP) {
sec &= RTC_SECONDS_MASK;
@@ -208,6 +217,8 @@ static int __init ds1742_rtc_probe(struct platform_device *pdev)
pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
+ ds1742_nvram_attr.size = max(ds1742_nvram_attr.size,
+ pdata->size_nvram);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
if (ret)
goto out;
@@ -215,10 +226,10 @@ static int __init ds1742_rtc_probe(struct platform_device *pdev)
out:
if (pdata->rtc)
rtc_device_unregister(pdata->rtc);
- if (ioaddr)
- iounmap(ioaddr);
+ if (pdata->ioaddr_nvram)
+ iounmap(pdata->ioaddr_nvram);
if (pdata->baseaddr)
- release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
+ release_mem_region(pdata->baseaddr, pdata->size);
kfree(pdata);
return ret;
}
@@ -229,8 +240,8 @@ static int __devexit ds1742_rtc_remove(struct platform_device *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);
+ iounmap(pdata->ioaddr_nvram);
+ release_mem_region(pdata->baseaddr, pdata->size);
kfree(pdata);
return 0;
}
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
new file mode 100644
index 00000000000..eac5fb1fc02
--- /dev/null
+++ b/drivers/rtc/rtc-omap.c
@@ -0,0 +1,572 @@
+/*
+ * TI OMAP1 Real Time Clock interface for Linux
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * Copyright (C) 2006 David Brownell (new RTC framework)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/mach/time.h>
+
+
+/* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock
+ * with century-range alarm matching, driven by the 32kHz clock.
+ *
+ * The main user-visible ways it differs from PC RTCs are by omitting
+ * "don't care" alarm fields and sub-second periodic IRQs, and having
+ * an autoadjust mechanism to calibrate to the true oscillator rate.
+ *
+ * Board-specific wiring options include using split power mode with
+ * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset),
+ * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from
+ * low power modes). See the BOARD-SPECIFIC CUSTOMIZATION comment.
+ */
+
+#define OMAP_RTC_BASE 0xfffb4800
+
+/* RTC registers */
+#define OMAP_RTC_SECONDS_REG 0x00
+#define OMAP_RTC_MINUTES_REG 0x04
+#define OMAP_RTC_HOURS_REG 0x08
+#define OMAP_RTC_DAYS_REG 0x0C
+#define OMAP_RTC_MONTHS_REG 0x10
+#define OMAP_RTC_YEARS_REG 0x14
+#define OMAP_RTC_WEEKS_REG 0x18
+
+#define OMAP_RTC_ALARM_SECONDS_REG 0x20
+#define OMAP_RTC_ALARM_MINUTES_REG 0x24
+#define OMAP_RTC_ALARM_HOURS_REG 0x28
+#define OMAP_RTC_ALARM_DAYS_REG 0x2c
+#define OMAP_RTC_ALARM_MONTHS_REG 0x30
+#define OMAP_RTC_ALARM_YEARS_REG 0x34
+
+#define OMAP_RTC_CTRL_REG 0x40
+#define OMAP_RTC_STATUS_REG 0x44
+#define OMAP_RTC_INTERRUPTS_REG 0x48
+
+#define OMAP_RTC_COMP_LSB_REG 0x4c
+#define OMAP_RTC_COMP_MSB_REG 0x50
+#define OMAP_RTC_OSC_REG 0x54
+
+/* OMAP_RTC_CTRL_REG bit fields: */
+#define OMAP_RTC_CTRL_SPLIT (1<<7)
+#define OMAP_RTC_CTRL_DISABLE (1<<6)
+#define OMAP_RTC_CTRL_SET_32_COUNTER (1<<5)
+#define OMAP_RTC_CTRL_TEST (1<<4)
+#define OMAP_RTC_CTRL_MODE_12_24 (1<<3)
+#define OMAP_RTC_CTRL_AUTO_COMP (1<<2)
+#define OMAP_RTC_CTRL_ROUND_30S (1<<1)
+#define OMAP_RTC_CTRL_STOP (1<<0)
+
+/* OMAP_RTC_STATUS_REG bit fields: */
+#define OMAP_RTC_STATUS_POWER_UP (1<<7)
+#define OMAP_RTC_STATUS_ALARM (1<<6)
+#define OMAP_RTC_STATUS_1D_EVENT (1<<5)
+#define OMAP_RTC_STATUS_1H_EVENT (1<<4)
+#define OMAP_RTC_STATUS_1M_EVENT (1<<3)
+#define OMAP_RTC_STATUS_1S_EVENT (1<<2)
+#define OMAP_RTC_STATUS_RUN (1<<1)
+#define OMAP_RTC_STATUS_BUSY (1<<0)
+
+/* OMAP_RTC_INTERRUPTS_REG bit fields: */
+#define OMAP_RTC_INTERRUPTS_IT_ALARM (1<<3)
+#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2)
+
+
+#define rtc_read(addr) omap_readb(OMAP_RTC_BASE + (addr))
+#define rtc_write(val, addr) omap_writeb(val, OMAP_RTC_BASE + (addr))
+
+
+/* platform_bus isn't hotpluggable, so for static linkage it'd be safe
+ * to get rid of probe() and remove() code ... too bad the driver struct
+ * remembers probe(), that's about 25% of the runtime footprint!!
+ */
+#ifndef MODULE
+#undef __devexit
+#undef __devexit_p
+#define __devexit __exit
+#define __devexit_p __exit_p
+#endif
+
+
+/* we rely on the rtc framework to handle locking (rtc->ops_lock),
+ * so the only other requirement is that register accesses which
+ * require BUSY to be clear are made with IRQs locally disabled
+ */
+static void rtc_wait_not_busy(void)
+{
+ int count = 0;
+ u8 status;
+
+ /* BUSY may stay active for 1/32768 second (~30 usec) */
+ for (count = 0; count < 50; count++) {
+ status = rtc_read(OMAP_RTC_STATUS_REG);
+ if ((status & (u8)OMAP_RTC_STATUS_BUSY) == 0)
+ break;
+ udelay(1);
+ }
+ /* now we have ~15 usec to read/write various registers */
+}
+
+static irqreturn_t rtc_irq(int irq, void *class_dev)
+{
+ unsigned long events = 0;
+ u8 irq_data;
+
+ irq_data = rtc_read(OMAP_RTC_STATUS_REG);
+
+ /* alarm irq? */
+ if (irq_data & OMAP_RTC_STATUS_ALARM) {
+ rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
+ events |= RTC_IRQF | RTC_AF;
+ }
+
+ /* 1/sec periodic/update irq? */
+ if (irq_data & OMAP_RTC_STATUS_1S_EVENT)
+ events |= RTC_IRQF | RTC_UF;
+
+ rtc_update_irq(class_dev, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static int
+omap_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ u8 reg;
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ case RTC_AIE_ON:
+ case RTC_UIE_OFF:
+ case RTC_UIE_ON:
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ local_irq_disable();
+ rtc_wait_not_busy();
+ reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
+ switch (cmd) {
+ /* AIE = Alarm Interrupt Enable */
+ case RTC_AIE_OFF:
+ reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
+ break;
+ case RTC_AIE_ON:
+ reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
+ break;
+ /* UIE = Update Interrupt Enable (1/second) */
+ case RTC_UIE_OFF:
+ reg &= ~OMAP_RTC_INTERRUPTS_IT_TIMER;
+ break;
+ case RTC_UIE_ON:
+ reg |= OMAP_RTC_INTERRUPTS_IT_TIMER;
+ break;
+ }
+ rtc_wait_not_busy();
+ rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
+ local_irq_enable();
+
+ return 0;
+}
+
+#else
+#define omap_rtc_ioctl NULL
+#endif
+
+/* this hardware doesn't support "don't care" alarm fields */
+static int tm2bcd(struct rtc_time *tm)
+{
+ if (rtc_valid_tm(tm) != 0)
+ return -EINVAL;
+
+ tm->tm_sec = BIN2BCD(tm->tm_sec);
+ tm->tm_min = BIN2BCD(tm->tm_min);
+ tm->tm_hour = BIN2BCD(tm->tm_hour);
+ tm->tm_mday = BIN2BCD(tm->tm_mday);
+
+ tm->tm_mon = BIN2BCD(tm->tm_mon + 1);
+
+ /* epoch == 1900 */
+ if (tm->tm_year < 100 || tm->tm_year > 199)
+ return -EINVAL;
+ tm->tm_year = BIN2BCD(tm->tm_year - 100);
+
+ return 0;
+}
+
+static void bcd2tm(struct rtc_time *tm)
+{
+ tm->tm_sec = BCD2BIN(tm->tm_sec);
+ tm->tm_min = BCD2BIN(tm->tm_min);
+ tm->tm_hour = BCD2BIN(tm->tm_hour);
+ tm->tm_mday = BCD2BIN(tm->tm_mday);
+ tm->tm_mon = BCD2BIN(tm->tm_mon) - 1;
+ /* epoch == 1900 */
+ tm->tm_year = BCD2BIN(tm->tm_year) + 100;
+}
+
+
+static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ /* we don't report wday/yday/isdst ... */
+ local_irq_disable();
+ rtc_wait_not_busy();
+
+ tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG);
+ tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG);
+ tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG);
+ tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG);
+ tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG);
+ tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG);
+
+ local_irq_enable();
+
+ bcd2tm(tm);
+ return 0;
+}
+
+static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ if (tm2bcd(tm) < 0)
+ return -EINVAL;
+ local_irq_disable();
+ rtc_wait_not_busy();
+
+ rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG);
+ rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG);
+ rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG);
+ rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG);
+ rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG);
+ rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG);
+
+ local_irq_enable();
+
+ return 0;
+}
+
+static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ local_irq_disable();
+ rtc_wait_not_busy();
+
+ alm->time.tm_sec = rtc_read(OMAP_RTC_ALARM_SECONDS_REG);
+ alm->time.tm_min = rtc_read(OMAP_RTC_ALARM_MINUTES_REG);
+ alm->time.tm_hour = rtc_read(OMAP_RTC_ALARM_HOURS_REG);
+ alm->time.tm_mday = rtc_read(OMAP_RTC_ALARM_DAYS_REG);
+ alm->time.tm_mon = rtc_read(OMAP_RTC_ALARM_MONTHS_REG);
+ alm->time.tm_year = rtc_read(OMAP_RTC_ALARM_YEARS_REG);
+
+ local_irq_enable();
+
+ bcd2tm(&alm->time);
+ alm->pending = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG)
+ & OMAP_RTC_INTERRUPTS_IT_ALARM);
+ alm->enabled = alm->pending && device_may_wakeup(dev);
+
+ return 0;
+}
+
+static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ u8 reg;
+
+ /* Much userspace code uses RTC_ALM_SET, thus "don't care" for
+ * day/month/year specifies alarms up to 24 hours in the future.
+ * So we need to handle that ... but let's ignore the "don't care"
+ * values for hours/minutes/seconds.
+ */
+ if (alm->time.tm_mday <= 0
+ && alm->time.tm_mon < 0
+ && alm->time.tm_year < 0) {
+ struct rtc_time tm;
+ unsigned long now, then;
+
+ omap_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &now);
+
+ alm->time.tm_mday = tm.tm_mday;
+ alm->time.tm_mon = tm.tm_mon;
+ alm->time.tm_year = tm.tm_year;
+ rtc_tm_to_time(&alm->time, &then);
+
+ /* sometimes the alarm wraps into tomorrow */
+ if (then < now) {
+ rtc_time_to_tm(now + 24 * 60 * 60, &tm);
+ alm->time.tm_mday = tm.tm_mday;
+ alm->time.tm_mon = tm.tm_mon;
+ alm->time.tm_year = tm.tm_year;
+ }
+ }
+
+ if (tm2bcd(&alm->time) < 0)
+ return -EINVAL;
+
+ local_irq_disable();
+ rtc_wait_not_busy();
+
+ rtc_write(alm->time.tm_year, OMAP_RTC_ALARM_YEARS_REG);
+ rtc_write(alm->time.tm_mon, OMAP_RTC_ALARM_MONTHS_REG);
+ rtc_write(alm->time.tm_mday, OMAP_RTC_ALARM_DAYS_REG);
+ rtc_write(alm->time.tm_hour, OMAP_RTC_ALARM_HOURS_REG);
+ rtc_write(alm->time.tm_min, OMAP_RTC_ALARM_MINUTES_REG);
+ rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG);
+
+ reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
+ if (alm->enabled)
+ reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
+ else
+ reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
+ rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
+
+ local_irq_enable();
+
+ return 0;
+}
+
+static struct rtc_class_ops omap_rtc_ops = {
+ .ioctl = omap_rtc_ioctl,
+ .read_time = omap_rtc_read_time,
+ .set_time = omap_rtc_set_time,
+ .read_alarm = omap_rtc_read_alarm,
+ .set_alarm = omap_rtc_set_alarm,
+};
+
+static int omap_rtc_alarm;
+static int omap_rtc_timer;
+
+static int __devinit omap_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem;
+ struct rtc_device *rtc;
+ u8 reg, new_ctrl;
+
+ omap_rtc_timer = platform_get_irq(pdev, 0);
+ if (omap_rtc_timer <= 0) {
+ pr_debug("%s: no update irq?\n", pdev->name);
+ return -ENOENT;
+ }
+
+ omap_rtc_alarm = platform_get_irq(pdev, 1);
+ if (omap_rtc_alarm <= 0) {
+ pr_debug("%s: no alarm irq?\n", pdev->name);
+ return -ENOENT;
+ }
+
+ /* NOTE: using static mapping for RTC registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res && res->start != OMAP_RTC_BASE) {
+ pr_debug("%s: RTC registers at %08x, expected %08x\n",
+ pdev->name, (unsigned) res->start, OMAP_RTC_BASE);
+ return -ENOENT;
+ }
+
+ if (res)
+ mem = request_mem_region(res->start,
+ res->end - res->start + 1,
+ pdev->name);
+ else
+ mem = NULL;
+ if (!mem) {
+ pr_debug("%s: RTC registers at %08x are not free\n",
+ pdev->name, OMAP_RTC_BASE);
+ return -EBUSY;
+ }
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &omap_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ pr_debug("%s: can't register RTC device, err %ld\n",
+ pdev->name, PTR_ERR(rtc));
+ goto fail;
+ }
+ platform_set_drvdata(pdev, rtc);
+ class_set_devdata(&rtc->class_dev, mem);
+
+ /* clear pending irqs, and set 1/second periodic,
+ * which we'll use instead of update irqs
+ */
+ rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
+
+ /* clear old status */
+ reg = rtc_read(OMAP_RTC_STATUS_REG);
+ if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {
+ pr_info("%s: RTC power up reset detected\n",
+ pdev->name);
+ rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG);
+ }
+ if (reg & (u8) OMAP_RTC_STATUS_ALARM)
+ rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
+
+ /* handle periodic and alarm irqs */
+ if (request_irq(omap_rtc_timer, rtc_irq, SA_INTERRUPT,
+ rtc->class_dev.class_id, &rtc->class_dev)) {
+ pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
+ pdev->name, omap_rtc_timer);
+ goto fail0;
+ }
+ if (request_irq(omap_rtc_alarm, rtc_irq, SA_INTERRUPT,
+ rtc->class_dev.class_id, &rtc->class_dev)) {
+ pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",
+ pdev->name, omap_rtc_alarm);
+ goto fail1;
+ }
+
+ /* On boards with split power, RTC_ON_NOFF won't reset the RTC */
+ reg = rtc_read(OMAP_RTC_CTRL_REG);
+ if (reg & (u8) OMAP_RTC_CTRL_STOP)
+ pr_info("%s: already running\n", pdev->name);
+
+ /* force to 24 hour mode */
+ new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
+ new_ctrl |= OMAP_RTC_CTRL_STOP;
+
+ /* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
+ *
+ * - Boards wired so that RTC_WAKE_INT does something, and muxed
+ * right (W13_1610_RTC_WAKE_INT is the default after chip reset),
+ * should initialize the device wakeup flag appropriately.
+ *
+ * - Boards wired so RTC_ON_nOFF is used as the reset signal,
+ * rather than nPWRON_RESET, should forcibly enable split
+ * power mode. (Some chip errata report that RTC_CTRL_SPLIT
+ * is write-only, and always reads as zero...)
+ */
+ device_init_wakeup(&pdev->dev, 0);
+
+ if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
+ pr_info("%s: split power mode\n", pdev->name);
+
+ if (reg != new_ctrl)
+ rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);
+
+ return 0;
+
+fail1:
+ free_irq(omap_rtc_timer, NULL);
+fail0:
+ rtc_device_unregister(rtc);
+fail:
+ release_resource(mem);
+ return -EIO;
+}
+
+static int __devexit omap_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(pdev);;
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ /* leave rtc running, but disable irqs */
+ rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
+
+ free_irq(omap_rtc_timer, rtc);
+ free_irq(omap_rtc_alarm, rtc);
+
+ release_resource(class_get_devdata(&rtc->class_dev));
+ rtc_device_unregister(rtc);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static struct timespec rtc_delta;
+static u8 irqstat;
+
+static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rtc_time rtc_tm;
+ struct timespec time;
+
+ time.tv_nsec = 0;
+ omap_rtc_read_time(NULL, &rtc_tm);
+ rtc_tm_to_time(&rtc_tm, &time.tv_sec);
+
+ save_time_delta(&rtc_delta, &time);
+ irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);
+
+ /* FIXME the RTC alarm is not currently acting as a wakeup event
+ * source, and in fact this enable() call is just saving a flag
+ * that's never used...
+ */
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(omap_rtc_alarm);
+ else
+ rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
+
+ return 0;
+}
+
+static int omap_rtc_resume(struct platform_device *pdev)
+{
+ struct rtc_time rtc_tm;
+ struct timespec time;
+
+ time.tv_nsec = 0;
+ omap_rtc_read_time(NULL, &rtc_tm);
+ rtc_tm_to_time(&rtc_tm, &time.tv_sec);
+
+ restore_time_delta(&rtc_delta, &time);
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(omap_rtc_alarm);
+ else
+ rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG);
+ return 0;
+}
+
+#else
+#define omap_rtc_suspend NULL
+#define omap_rtc_resume NULL
+#endif
+
+static void omap_rtc_shutdown(struct platform_device *pdev)
+{
+ rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
+}
+
+MODULE_ALIAS("omap_rtc");
+static struct platform_driver omap_rtc_driver = {
+ .probe = omap_rtc_probe,
+ .remove = __devexit_p(omap_rtc_remove),
+ .suspend = omap_rtc_suspend,
+ .resume = omap_rtc_resume,
+ .shutdown = omap_rtc_shutdown,
+ .driver = {
+ .name = "omap_rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rtc_init(void)
+{
+ return platform_driver_register(&omap_rtc_driver);
+}
+module_init(rtc_init);
+
+static void __exit rtc_exit(void)
+{
+ platform_driver_unregister(&omap_rtc_driver);
+}
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("George G. Davis (and others)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index 2a86632580f..e2c7698fdba 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -13,7 +13,7 @@
#include <linux/rtc.h>
#include <linux/bcd.h>
-#define DRV_VERSION "0.2"
+#define DRV_VERSION "0.3"
/* Addresses to scan */
static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END };
@@ -39,6 +39,14 @@ static int rs5c372_attach(struct i2c_adapter *adapter);
static int rs5c372_detach(struct i2c_client *client);
static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind);
+struct rs5c372 {
+ u8 reg_addr;
+ u8 regs[17];
+ struct i2c_msg msg[1];
+ struct i2c_client client;
+ struct rtc_device *rtc;
+};
+
static struct i2c_driver rs5c372_driver = {
.driver = {
.name = "rs5c372",
@@ -49,18 +57,16 @@ static struct i2c_driver rs5c372_driver = {
static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
- unsigned char buf[7] = { RS5C372_REG_BASE };
- /* this implements the 1st reading method, according
- * to the datasheet. buf[0] is initialized with
- * address ptr and transmission format register.
+ struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
+ u8 *buf = &(rs5c372->regs[1]);
+
+ /* this implements the 3rd reading method, according
+ * to the datasheet. rs5c372 defaults to internal
+ * address 0xF, so 0x0 is in regs[1]
*/
- struct i2c_msg msgs[] = {
- { client->addr, 0, 1, buf },
- { client->addr, I2C_M_RD, 7, buf },
- };
- if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+ if ((i2c_transfer(client->adapter, rs5c372->msg, 1)) != 1) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
@@ -114,25 +120,16 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
{
- unsigned char buf = RS5C372_REG_TRIM;
-
- struct i2c_msg msgs[] = {
- { client->addr, 0, 1, &buf },
- { client->addr, I2C_M_RD, 1, &buf },
- };
-
- if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
- dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
- return -EIO;
- }
-
- dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, *trim);
+ struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
+ u8 tmp = rs5c372->regs[RS5C372_REG_TRIM + 1];
if (osc)
- *osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768;
+ *osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
- if (trim)
- *trim = buf & RS5C372_TRIM_MASK;
+ if (trim) {
+ *trim = tmp & RS5C372_TRIM_MASK;
+ dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, *trim);
+ }
return 0;
}
@@ -201,7 +198,7 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
struct i2c_client *client;
- struct rtc_device *rtc;
+ struct rs5c372 *rs5c372;
dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
@@ -210,10 +207,11 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
goto exit;
}
- if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
+ if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
+ client = &rs5c372->client;
/* I2C client */
client->addr = address;
@@ -222,32 +220,47 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE);
+ i2c_set_clientdata(client, rs5c372);
+
+ rs5c372->msg[0].addr = address;
+ rs5c372->msg[0].flags = I2C_M_RD;
+ rs5c372->msg[0].len = sizeof(rs5c372->regs);
+ rs5c372->msg[0].buf = rs5c372->regs;
+
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
- rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev,
- &rs5c372_rtc_ops, THIS_MODULE);
+ rs5c372->rtc = rtc_device_register(rs5c372_driver.driver.name,
+ &client->dev, &rs5c372_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- err = PTR_ERR(rtc);
+ if (IS_ERR(rs5c372->rtc)) {
+ err = PTR_ERR(rs5c372->rtc);
goto exit_detach;
}
- i2c_set_clientdata(client, rtc);
-
- device_create_file(&client->dev, &dev_attr_trim);
- device_create_file(&client->dev, &dev_attr_osc);
+ err = device_create_file(&client->dev, &dev_attr_trim);
+ if (err)
+ goto exit_devreg;
+ err = device_create_file(&client->dev, &dev_attr_osc);
+ if (err)
+ goto exit_trim;
return 0;
+exit_trim:
+ device_remove_file(&client->dev, &dev_attr_trim);
+
+exit_devreg:
+ rtc_device_unregister(rs5c372->rtc);
+
exit_detach:
i2c_detach_client(client);
exit_kfree:
- kfree(client);
+ kfree(rs5c372);
exit:
return err;
@@ -256,16 +269,15 @@ exit:
static int rs5c372_detach(struct i2c_client *client)
{
int err;
- struct rtc_device *rtc = i2c_get_clientdata(client);
+ struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
- if (rtc)
- rtc_device_unregister(rtc);
+ if (rs5c372->rtc)
+ rtc_device_unregister(rs5c372->rtc);
if ((err = i2c_detach_client(client)))
return err;
- kfree(client);
-
+ kfree(rs5c372);
return 0;
}
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index bc4bd24508a..f50a1b8e160 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -99,6 +99,7 @@ static ssize_t test_irq_store(struct device *dev,
struct rtc_device *rtc = platform_get_drvdata(plat_dev);
retval = count;
+ local_irq_disable();
if (strncmp(buf, "tick", 4) == 0)
rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
else if (strncmp(buf, "alarm", 5) == 0)
@@ -107,6 +108,7 @@ static ssize_t test_irq_store(struct device *dev,
rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
else
retval = -EINVAL;
+ local_irq_enable();
return retval;
}
@@ -121,11 +123,18 @@ static int test_probe(struct platform_device *plat_dev)
err = PTR_ERR(rtc);
return err;
}
- device_create_file(&plat_dev->dev, &dev_attr_irq);
+
+ err = device_create_file(&plat_dev->dev, &dev_attr_irq);
+ if (err)
+ goto err;
platform_set_drvdata(plat_dev, rtc);
return 0;
+
+err:
+ rtc_device_unregister(rtc);
+ return err;
}
static int __devexit test_remove(struct platform_device *plat_dev)
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 522c69753bb..9a67487d086 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -562,11 +562,19 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
else
dev_err(&client->dev, "couldn't read status\n");
- device_create_file(&client->dev, &dev_attr_atrim);
- device_create_file(&client->dev, &dev_attr_dtrim);
+ err = device_create_file(&client->dev, &dev_attr_atrim);
+ if (err) goto exit_devreg;
+ err = device_create_file(&client->dev, &dev_attr_dtrim);
+ if (err) goto exit_atrim;
return 0;
+exit_atrim:
+ device_remove_file(&client->dev, &dev_attr_atrim);
+
+exit_devreg:
+ rtc_device_unregister(rtc);
+
exit_detach:
i2c_detach_client(client);