aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/class.c1
-rw-r--r--drivers/rtc/interface.c122
-rw-r--r--drivers/rtc/rtc-cmos.c64
-rw-r--r--drivers/rtc/rtc-dev.c44
-rw-r--r--drivers/rtc/rtc-ds1374.c449
-rw-r--r--drivers/rtc/rtc-ds1553.c2
-rw-r--r--drivers/rtc/rtc-ds1742.c2
-rw-r--r--drivers/rtc/rtc-pcf8583.c3
-rw-r--r--drivers/rtc/rtc-sh.c51
-rw-r--r--drivers/rtc/rtc-sysfs.c24
12 files changed, 693 insertions, 81 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ff9e35cb308d..6420a90a4a92 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -139,6 +139,17 @@ config RTC_DRV_DS1307
This driver can also be built as a module. If so, the module
will be called rtc-ds1307.
+config RTC_DRV_DS1374
+ tristate "Maxim/Dallas Semiconductor DS1374 Real Time Clock"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS1374 real-time clock chips. If an interrupt is associated
+ with the device, the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1374.
+
config RTC_DRV_DS1672
tristate "Dallas/Maxim DS1672"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index d3a33aa2696f..465db4dd50b2 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
+obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 10ab3b71ffc6..4dfdf019fccc 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -153,6 +153,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
+ init_waitqueue_head(&rtc->irq_queue);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index ad66c6ecf365..de0da545c7a1 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -12,6 +12,7 @@
*/
#include <linux/rtc.h>
+#include <linux/log2.h>
int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{
@@ -99,7 +100,7 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);
-int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
@@ -119,6 +120,87 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
mutex_unlock(&rtc->ops_lock);
return err;
}
+
+int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+ struct rtc_time before, now;
+ int first_time = 1;
+
+ /* The lower level RTC driver may not be capable of filling
+ * in all fields of the rtc_time struct (eg. rtc-cmos),
+ * and so might instead return -1 in some fields.
+ * We deal with that here by grabbing a current RTC timestamp
+ * and using values from that for any missing (-1) values.
+ *
+ * But this can be racey, because some fields of the RTC timestamp
+ * may have wrapped in the interval since we read the RTC alarm,
+ * which would lead to us inserting inconsistent values in place
+ * of the -1 fields.
+ *
+ * Reading the alarm and timestamp in the reverse sequence
+ * would have the same race condition, and not solve the issue.
+ *
+ * So, we must first read the RTC timestamp,
+ * then read the RTC alarm value,
+ * and then read a second RTC timestamp.
+ *
+ * If any fields of the second timestamp have changed
+ * when compared with the first timestamp, then we know
+ * our timestamp may be inconsistent with that used by
+ * the low-level rtc_read_alarm_internal() function.
+ *
+ * So, when the two timestamps disagree, we just loop and do
+ * the process again to get a fully consistent set of values.
+ *
+ * This could all instead be done in the lower level driver,
+ * but since more than one lower level RTC implementation needs it,
+ * then it's probably best best to do it here instead of there..
+ */
+
+ /* Get the "before" timestamp */
+ err = rtc_read_time(rtc, &before);
+ if (err < 0)
+ return err;
+ do {
+ if (!first_time)
+ memcpy(&before, &now, sizeof(struct rtc_time));
+ first_time = 0;
+
+ /* get the RTC alarm values, which may be incomplete */
+ err = rtc_read_alarm_internal(rtc, alarm);
+ if (err)
+ return err;
+ if (!alarm->enabled)
+ return 0;
+
+ /* get the "after" timestamp, to detect wrapped fields */
+ err = rtc_read_time(rtc, &now);
+ if (err < 0)
+ return err;
+
+ /* note that tm_sec is a "don't care" value here: */
+ } while ( before.tm_min != now.tm_min
+ || before.tm_hour != now.tm_hour
+ || before.tm_mon != now.tm_mon
+ || before.tm_year != now.tm_year
+ || before.tm_isdst != now.tm_isdst);
+
+ /* Fill in any missing alarm fields using the timestamp */
+ if (alarm->time.tm_sec == -1)
+ alarm->time.tm_sec = now.tm_sec;
+ if (alarm->time.tm_min == -1)
+ alarm->time.tm_min = now.tm_min;
+ if (alarm->time.tm_hour == -1)
+ alarm->time.tm_hour = now.tm_hour;
+ if (alarm->time.tm_mday == -1)
+ alarm->time.tm_mday = now.tm_mday;
+ if (alarm->time.tm_mon == -1)
+ alarm->time.tm_mon = now.tm_mon;
+ if (alarm->time.tm_year == -1)
+ alarm->time.tm_year = now.tm_year;
+ return 0;
+}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
@@ -210,6 +292,10 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
if (task == NULL || task->func == NULL)
return -EINVAL;
+ /* Cannot register while the char dev is in use */
+ if (!(mutex_trylock(&rtc->char_lock)))
+ return -EBUSY;
+
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == NULL) {
rtc->irq_task = task;
@@ -217,13 +303,14 @@ int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
}
spin_unlock_irq(&rtc->irq_task_lock);
+ mutex_unlock(&rtc->char_lock);
+
return retval;
}
EXPORT_SYMBOL_GPL(rtc_irq_register);
void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
{
-
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == task)
rtc->irq_task = NULL;
@@ -231,6 +318,16 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);
+/**
+ * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
+ * @rtc: the rtc device
+ * @task: currently registered with rtc_irq_register()
+ * @enabled: true to enable periodic IRQs
+ * Context: any
+ *
+ * Note that rtc_irq_set_freq() should previously have been used to
+ * specify the desired frequency of periodic IRQ task->func() callbacks.
+ */
int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)
{
int err = 0;
@@ -240,8 +337,10 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
return -ENXIO;
spin_lock_irqsave(&rtc->irq_task_lock, flags);
+ if (rtc->irq_task != NULL && task == NULL)
+ err = -EBUSY;
if (rtc->irq_task != task)
- err = -ENXIO;
+ err = -EACCES;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0)
@@ -251,6 +350,16 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);
+/**
+ * rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ
+ * @rtc: the rtc device
+ * @task: currently registered with rtc_irq_register()
+ * @freq: positive frequency with which task->func() will be called
+ * Context: any
+ *
+ * Note that rtc_irq_set_state() is used to enable or disable the
+ * periodic IRQs.
+ */
int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
{
int err = 0;
@@ -259,9 +368,14 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
if (rtc->ops->irq_set_freq == NULL)
return -ENXIO;
+ if (!is_power_of_2(freq))
+ return -EINVAL;
+
spin_lock_irqsave(&rtc->irq_task_lock, flags);
+ if (rtc->irq_task != NULL && task == NULL)
+ err = -EBUSY;
if (rtc->irq_task != task)
- err = -ENXIO;
+ err = -EACCES;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0) {
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 5d760bb6c2cd..e3fe83a23cf7 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -246,11 +246,9 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
/* 0 = no irqs; 1 = 2^15 Hz ... 15 = 2^0 Hz */
f = ffs(freq);
- if (f != 0) {
- if (f-- > 16 || freq != (1 << f))
- return -EINVAL;
- f = 16 - f;
- }
+ if (f-- > 16)
+ return -EINVAL;
+ f = 16 - f;
spin_lock_irqsave(&rtc_lock, flags);
CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
@@ -435,6 +433,19 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
if (!ports)
return -ENODEV;
+ /* Claim I/O ports ASAP, minimizing conflict with legacy driver.
+ *
+ * REVISIT non-x86 systems may instead use memory space resources
+ * (needing ioremap etc), not i/o space resources like this ...
+ */
+ ports = request_region(ports->start,
+ ports->end + 1 - ports->start,
+ driver_name);
+ if (!ports) {
+ dev_dbg(dev, "i/o registers already in use\n");
+ return -EBUSY;
+ }
+
cmos_rtc.irq = rtc_irq;
cmos_rtc.iomem = ports;
@@ -456,24 +467,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
cmos_rtc.rtc = rtc_device_register(driver_name, dev,
&cmos_rtc_ops, THIS_MODULE);
- if (IS_ERR(cmos_rtc.rtc))
- return PTR_ERR(cmos_rtc.rtc);
+ if (IS_ERR(cmos_rtc.rtc)) {
+ retval = PTR_ERR(cmos_rtc.rtc);
+ goto cleanup0;
+ }
cmos_rtc.dev = dev;
dev_set_drvdata(dev, &cmos_rtc);
-
- /* platform and pnp busses handle resources incompatibly.
- *
- * REVISIT for non-x86 systems we may need to handle io memory
- * resources: ioremap them, and request_mem_region().
- */
- if (is_pnp()) {
- retval = request_resource(&ioport_resource, ports);
- if (retval < 0) {
- dev_dbg(dev, "i/o registers already in use\n");
- goto cleanup0;
- }
- }
rename_region(ports, cmos_rtc.rtc->dev.bus_id);
spin_lock_irq(&rtc_lock);
@@ -536,9 +536,10 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
return 0;
cleanup1:
- rename_region(ports, NULL);
-cleanup0:
+ cmos_rtc.dev = NULL;
rtc_device_unregister(cmos_rtc.rtc);
+cleanup0:
+ release_region(ports->start, ports->end + 1 - ports->start);
return retval;
}
@@ -557,19 +558,21 @@ static void cmos_do_shutdown(void)
static void __exit cmos_do_remove(struct device *dev)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ struct resource *ports;
cmos_do_shutdown();
- if (is_pnp())
- release_resource(cmos->iomem);
- rename_region(cmos->iomem, NULL);
-
if (is_valid_irq(cmos->irq))
- free_irq(cmos->irq, cmos_rtc.rtc);
+ free_irq(cmos->irq, cmos->rtc);
- rtc_device_unregister(cmos_rtc.rtc);
+ rtc_device_unregister(cmos->rtc);
+ cmos->rtc = NULL;
- cmos_rtc.dev = NULL;
+ ports = cmos->iomem;
+ release_region(ports->start, ports->end + 1 - ports->start);
+ cmos->iomem = NULL;
+
+ cmos->dev = NULL;
dev_set_drvdata(dev, NULL);
}
@@ -656,7 +659,8 @@ static int cmos_resume(struct device *dev)
/*----------------------------------------------------------------*/
/* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems,
- * the device node will always be created as a PNPACPI device.
+ * the device node will always be created as a PNPACPI device. Plus
+ * pre-ACPI PCs probably list it in the PNPBIOS tables.
*/
#ifdef CONFIG_PNP
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 005fff3a3508..814583bd2fe7 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -142,7 +142,7 @@ static int set_uie(struct rtc_device *rtc)
static ssize_t
rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
- struct rtc_device *rtc = to_rtc_device(file->private_data);
+ struct rtc_device *rtc = file->private_data;
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
@@ -196,7 +196,7 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
{
- struct rtc_device *rtc = to_rtc_device(file->private_data);
+ struct rtc_device *rtc = file->private_data;
unsigned long data;
poll_wait(file, &rtc->irq_queue, wait);
@@ -233,22 +233,12 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
break;
case RTC_PIE_ON:
- if (!capable(CAP_SYS_RESOURCE))
+ if (rtc->irq_freq > rtc->max_user_freq &&
+ !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_irq(&rtc->irq_task_lock);
- if (rtc->irq_task)
- err = -EBUSY;
- spin_unlock_irq(&rtc->irq_task_lock);
-
- if (err < 0)
- return err;
- }
-
/* try the driver's ioctl interface */
if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg);
@@ -338,18 +328,20 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
err = rtc_set_time(rtc, &tm);
break;
- case RTC_IRQP_READ:
- if (ops->irq_set_freq)
- err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
- else
- err = -ENOTTY;
+ case RTC_PIE_ON:
+ err = rtc_irq_set_state(rtc, NULL, 1);
+ break;
+
+ case RTC_PIE_OFF:
+ err = rtc_irq_set_state(rtc, NULL, 0);
break;
case RTC_IRQP_SET:
- if (ops->irq_set_freq)
- err = rtc_irq_set_freq(rtc, rtc->irq_task, arg);
- else
- err = -ENOTTY;
+ err = rtc_irq_set_freq(rtc, NULL, arg);
+ break;
+
+ case RTC_IRQP_READ:
+ err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
break;
#if 0
@@ -405,7 +397,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
static int rtc_dev_release(struct inode *inode, struct file *file)
{
- struct rtc_device *rtc = to_rtc_device(file->private_data);
+ struct rtc_device *rtc = file->private_data;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
clear_uie(rtc);
@@ -419,7 +411,7 @@ static int rtc_dev_release(struct inode *inode, struct file *file)
static int rtc_dev_fasync(int fd, struct file *file, int on)
{
- struct rtc_device *rtc = to_rtc_device(file->private_data);
+ struct rtc_device *rtc = file->private_data;
return fasync_helper(fd, file, on, &rtc->async_queue);
}
@@ -449,8 +441,6 @@ void rtc_dev_prepare(struct rtc_device *rtc)
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
mutex_init(&rtc->char_lock);
- spin_lock_init(&rtc->irq_lock);
- init_waitqueue_head(&rtc->irq_queue);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
INIT_WORK(&rtc->uie_task, rtc_uie_task);
setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
new file mode 100644
index 000000000000..45bda186befc
--- /dev/null
+++ b/drivers/rtc/rtc-ds1374.c
@@ -0,0 +1,449 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
+ *
+ * Based on code by Randy Vinson <rvinson@mvista.com>,
+ * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
+ *
+ * Copyright (C) 2006-2007 Freescale Semiconductor
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+
+#define DS1374_REG_TOD0 0x00 /* Time of Day */
+#define DS1374_REG_TOD1 0x01
+#define DS1374_REG_TOD2 0x02
+#define DS1374_REG_TOD3 0x03
+#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
+#define DS1374_REG_WDALM1 0x05
+#define DS1374_REG_WDALM2 0x06
+#define DS1374_REG_CR 0x07 /* Control */
+#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
+#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
+#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
+#define DS1374_REG_SR 0x08 /* Status */
+#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
+#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
+#define DS1374_REG_TCR 0x09 /* Trickle Charge */
+
+struct ds1374 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds1374_driver;
+
+static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
+ int reg, int nbytes)
+{
+ u8 buf[4];
+ int ret;
+ int i;
+
+ if (nbytes > 4) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < nbytes)
+ return -EIO;
+
+ for (i = nbytes - 1, *time = 0; i >= 0; i--)
+ *time = (*time << 8) | buf[i];
+
+ return 0;
+}
+
+static int ds1374_write_rtc(struct i2c_client *client, u32 time,
+ int reg, int nbytes)
+{
+ u8 buf[4];
+ int i;
+
+ if (nbytes > 4) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ buf[i] = time & 0xff;
+ time >>= 8;
+ }
+
+ return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
+}
+
+static int ds1374_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS1374_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
+ return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+}
+
+static int ds1374_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 itime;
+ int ret;
+
+ ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
+ if (!ret)
+ rtc_time_to_tm(itime, time);
+
+ return ret;
+}
+
+static int ds1374_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long itime;
+
+ rtc_tm_to_time(time, &itime);
+ return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
+}
+
+/* The ds1374 has a decrementer for an alarm, rather than a comparator.
+ * If the time of day is changed, then the alarm will need to be
+ * reset.
+ */
+static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ u32 now, cur_alarm;
+ int cr, sr;
+ int ret = 0;
+
+ if (client->irq < 0)
+ return -EINVAL;
+
+ mutex_lock(&ds1374->mutex);
+
+ cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (ret < 0)
+ goto out;
+
+ ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
+ if (ret)
+ goto out;
+
+ ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
+ if (ret)
+ goto out;
+
+ rtc_time_to_tm(now + cur_alarm, &alarm->time);
+ alarm->enabled = !!(cr & DS1374_REG_CR_WACE);
+ alarm->pending = !!(sr & DS1374_REG_SR_AF);
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+
+static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct rtc_time now;
+ unsigned long new_alarm, itime;
+ int cr;
+ int ret = 0;
+
+ if (client->irq < 0)
+ return -EINVAL;
+
+ ret = ds1374_read_time(dev, &now);
+ if (ret < 0)
+ return ret;
+
+ rtc_tm_to_time(&alarm->time, &new_alarm);
+ rtc_tm_to_time(&now, &itime);
+
+ new_alarm -= itime;
+
+ /* This can happen due to races, in addition to dates that are
+ * truly in the past. To avoid requiring the caller to check for
+ * races, dates in the past are assumed to be in the recent past
+ * (i.e. not something that we'd rather the caller know about via
+ * an error), and the alarm is set to go off as soon as possible.
+ */
+ if (new_alarm <= 0)
+ new_alarm = 1;
+
+ mutex_lock(&ds1374->mutex);
+
+ ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ /* Disable any existing alarm before setting the new one
+ * (or lack thereof). */
+ cr &= ~DS1374_REG_CR_WACE;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+ if (ret < 0)
+ goto out;
+
+ ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
+ if (ret)
+ goto out;
+
+ if (alarm->enabled) {
+ cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
+ cr &= ~DS1374_REG_CR_WDALM;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+ }
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+
+static irqreturn_t ds1374_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds1374->work);
+ return IRQ_HANDLED;
+}
+
+static void ds1374_work(struct work_struct *work)
+{
+ struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
+ struct i2c_client *client = ds1374->client;
+ int stat, control;
+
+ mutex_lock(&ds1374->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (stat < 0)
+ return;
+
+ if (stat & DS1374_REG_SR_AF) {
+ stat &= ~DS1374_REG_SR_AF;
+ i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
+
+ control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (control < 0)
+ goto out;
+
+ control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
+ i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+
+ /* rtc_update_irq() assumes that it is called
+ * from IRQ-disabled context.
+ */
+ local_irq_disable();
+ rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
+ local_irq_enable();
+ }
+
+out:
+ if (!ds1374->exiting)
+ enable_irq(client->irq);
+
+ mutex_unlock(&ds1374->mutex);
+}
+
+static int ds1374_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ int ret = -ENOIOCTLCMD;
+
+ mutex_lock(&ds1374->mutex);
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ ret &= ~DS1374_REG_CR_WACE;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
+ if (ret < 0)
+ goto out;
+
+ break;
+
+ case RTC_AIE_ON:
+ ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
+ ret &= ~DS1374_REG_CR_WDALM;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
+ if (ret < 0)
+ goto out;
+
+ break;
+ }
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+
+static const struct rtc_class_ops ds1374_rtc_ops = {
+ .read_time = ds1374_read_time,
+ .set_time = ds1374_set_time,
+ .read_alarm = ds1374_read_alarm,
+ .set_alarm = ds1374_set_alarm,
+ .ioctl = ds1374_ioctl,
+};
+
+static int ds1374_probe(struct i2c_client *client)
+{
+ struct ds1374 *ds1374;
+ int ret;
+
+ ds1374 = kzalloc(sizeof(struct ds1374), GFP_KERNEL);
+ if (!ds1374)
+ return -ENOMEM;
+
+ ds1374->client = client;
+ i2c_set_clientdata(client, ds1374);
+
+ INIT_WORK(&ds1374->work, ds1374_work);
+ mutex_init(&ds1374->mutex);
+
+ ret = ds1374_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds1374_irq, 0,
+ "ds1374", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ ds1374->rtc = rtc_device_register(client->name, &client->dev,
+ &ds1374_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds1374->rtc)) {
+ ret = PTR_ERR(ds1374->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(ds1374);
+ return ret;
+}
+
+static int __devexit ds1374_remove(struct i2c_client *client)
+{
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds1374->mutex);
+ ds1374->exiting = 1;
+ mutex_unlock(&ds1374->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds1374->rtc);
+ i2c_set_clientdata(client, NULL);
+ kfree(ds1374);
+ return 0;
+}
+
+static struct i2c_driver ds1374_driver = {
+ .driver = {
+ .name = "rtc-ds1374",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds1374_probe,
+ .remove = __devexit_p(ds1374_remove),
+};
+
+static int __init ds1374_init(void)
+{
+ return i2c_add_driver(&ds1374_driver);
+}
+
+static void __exit ds1374_exit(void)
+{
+ i2c_del_driver(&ds1374_driver);
+}
+
+module_init(ds1374_init);
+module_exit(ds1374_exit);
+
+MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 5ab3492817d1..bb53c09bad16 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -395,7 +395,7 @@ static struct platform_driver ds1553_rtc_driver = {
.probe = ds1553_rtc_probe,
.remove = __devexit_p(ds1553_rtc_remove),
.driver = {
- .name = "ds1553",
+ .name = "rtc-ds1553",
.owner = THIS_MODULE,
},
};
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 67291b0f8283..c535b78698e2 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -251,7 +251,7 @@ static struct platform_driver ds1742_rtc_driver = {
.probe = ds1742_rtc_probe,
.remove = __devexit_p(ds1742_rtc_remove),
.driver = {
- .name = "ds1742",
+ .name = "rtc-ds1742",
.owner = THIS_MODULE,
},
};
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index d48b03374586..556d0e7da35b 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -332,6 +332,9 @@ static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind)
}
};
+ if (!i2c_check_functionality(adap, I2C_FUNC_I2C))
+ return 0;
+
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
if (!pcf)
return -ENOMEM;
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 93ee05eeaeba..78277a118b67 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -1,7 +1,7 @@
/*
* SuperH On-Chip RTC Support
*
- * Copyright (C) 2006 Paul Mundt
+ * Copyright (C) 2006, 2007 Paul Mundt
* Copyright (C) 2006 Jamie Lenehan
*
* Based on the old arch/sh/kernel/cpu/rtc.c by:
@@ -23,16 +23,19 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
+#include <asm/rtc.h>
#define DRV_NAME "sh-rtc"
-#define DRV_VERSION "0.1.2"
+#define DRV_VERSION "0.1.3"
#ifdef CONFIG_CPU_SH3
#define rtc_reg_size sizeof(u16)
#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
+#define RTC_DEF_CAPABILITIES 0UL
#elif defined(CONFIG_CPU_SH4)
#define rtc_reg_size sizeof(u32)
#define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */
+#define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR
#endif
#define RTC_REG(r) ((r) * rtc_reg_size)
@@ -80,6 +83,7 @@ struct sh_rtc {
struct rtc_device *rtc_dev;
spinlock_t lock;
int rearm_aie;
+ unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */
};
static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
@@ -319,14 +323,14 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT));
tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)) - 1;
-#if defined(CONFIG_CPU_SH4)
- yr = readw(rtc->regbase + RYRCNT);
- yr100 = BCD2BIN(yr >> 8);
- yr &= 0xff;
-#else
- yr = readb(rtc->regbase + RYRCNT);
- yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20);
-#endif
+ if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
+ yr = readw(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN(yr >> 8);
+ yr &= 0xff;
+ } else {
+ yr = readb(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20);
+ }
tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900;
@@ -375,14 +379,14 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT);
writeb(BIN2BCD(tm->tm_mon + 1), rtc->regbase + RMONCNT);
-#ifdef CONFIG_CPU_SH3
- year = tm->tm_year % 100;
- writeb(BIN2BCD(year), rtc->regbase + RYRCNT);
-#else
- year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) |
- BIN2BCD(tm->tm_year % 100);
- writew(year, rtc->regbase + RYRCNT);
-#endif
+ if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
+ year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) |
+ BIN2BCD(tm->tm_year % 100);
+ writew(year, rtc->regbase + RYRCNT);
+ } else {
+ year = tm->tm_year % 100;
+ writeb(BIN2BCD(year), rtc->regbase + RYRCNT);
+ }
/* Start RTC */
tmp = readb(rtc->regbase + RCR2);
@@ -589,6 +593,17 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
goto err_badmap;
}
+ rtc->capabilities = RTC_DEF_CAPABILITIES;
+ if (pdev->dev.platform_data) {
+ struct sh_rtc_platform_info *pinfo = pdev->dev.platform_data;
+
+ /*
+ * Some CPUs have special capabilities in addition to the
+ * default set. Add those in here.
+ */
+ rtc->capabilities |= pinfo->capabilities;
+ }
+
platform_set_drvdata(pdev, rtc);
return 0;
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 69df94b44841..6cad0841f3c4 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -73,11 +73,35 @@ rtc_sysfs_show_since_epoch(struct device *dev, struct device_attribute *attr,
return retval;
}
+static ssize_t
+rtc_sysfs_show_max_user_freq(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
+}
+
+static ssize_t
+rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ unsigned long val = simple_strtoul(buf, NULL, 0);
+
+ if (val >= 4096 || val == 0)
+ return -EINVAL;
+
+ rtc->max_user_freq = (int)val;
+
+ return n;
+}
+
static struct device_attribute rtc_attrs[] = {
__ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
__ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
__ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
__ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
+ __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,
+ rtc_sysfs_set_max_user_freq),
{ },
};