aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig52
-rw-r--r--drivers/rtc/Makefile5
-rw-r--r--drivers/rtc/rtc-cmos.c20
-rw-r--r--drivers/rtc/rtc-ds1307.c189
-rw-r--r--drivers/rtc/rtc-ds1374.c6
-rw-r--r--drivers/rtc/rtc-efi.c235
-rw-r--r--drivers/rtc/rtc-generic.c84
-rw-r--r--drivers/rtc/rtc-lib.c7
-rw-r--r--drivers/rtc/rtc-m41t80.c18
-rw-r--r--drivers/rtc/rtc-parisc.c112
-rw-r--r--drivers/rtc/rtc-ppc.c69
-rw-r--r--drivers/rtc/rtc-proc.c10
-rw-r--r--drivers/rtc/rtc-ps3.c104
-rw-r--r--drivers/rtc/rtc-sh.c101
-rw-r--r--drivers/rtc/rtc-v3020.c230
-rw-r--r--drivers/rtc/rtc-wm8350.c43
16 files changed, 958 insertions, 327 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 81450fbd8b12..4e9851fc1746 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -129,13 +129,14 @@ comment "I2C RTC drivers"
if I2C
config RTC_DRV_DS1307
- tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00"
+ tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
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).
+ EPSON RX-8025 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
@@ -224,11 +225,11 @@ config RTC_DRV_PCF8583
will be called rtc-pcf8583.
config RTC_DRV_M41T80
- tristate "ST M41T65/M41T80/81/82/83/84/85/87"
+ tristate "ST M41T62/65/M41T80/81/82/83/84/85/87"
help
If you say Y here you will get support for the ST M41T60
and M41T80 RTC chips series. Currently, the following chips are
- supported: M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84,
+ supported: M41T62, M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84,
M41ST85, and M41ST87.
This driver can also be built as a module. If so, the module
@@ -440,6 +441,16 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.
+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on IA64
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
config RTC_DRV_STK17TA8
tristate "Simtek STK17TA8"
depends on RTC_CLASS
@@ -562,7 +573,7 @@ config RTC_DRV_SA1100
config RTC_DRV_SH
tristate "SuperH On-Chip RTC"
- depends on RTC_CLASS && SUPERH
+ depends on RTC_CLASS && SUPERH && HAVE_CLK
help
Say Y here to enable support for the on-chip RTC found in
most SuperH processors.
@@ -677,22 +688,16 @@ config RTC_DRV_RS5C313
help
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
-config RTC_DRV_PARISC
- tristate "PA-RISC firmware RTC support"
- depends on PARISC
+config RTC_DRV_GENERIC
+ tristate "Generic RTC support"
+ # Please consider writing a new RTC driver instead of using the generic
+ # RTC abstraction
+ depends on PARISC || M68K || PPC
help
- Say Y or M here to enable RTC support on PA-RISC systems using
- firmware calls. If you do not know what you are doing, you should
+ Say Y or M here to enable RTC support on systems using the generic
+ RTC abstraction. If you do not know what you are doing, you should
just say Y.
-config RTC_DRV_PPC
- tristate "PowerPC machine dependent RTC support"
- depends on PPC
- help
- The PowerPC kernel has machine-specific functions for accessing
- the RTC. This exposes that functionality through the generic RTC
- class.
-
config RTC_DRV_PXA
tristate "PXA27x/PXA3xx"
depends on ARCH_PXA
@@ -736,4 +741,13 @@ config RTC_DRV_MV
This driver can also be built as a module. If so, the module
will be called rtc-mv.
+config RTC_DRV_PS3
+ tristate "PS3 RTC"
+ depends on PPC_PS3
+ help
+ If you say yes here you will get support for the RTC on PS3.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ps3.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 0e697aa51caa..6c0639a14f09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
@@ -55,8 +56,7 @@ obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
-obj-$(CONFIG_RTC_DRV_PARISC) += rtc-parisc.o
-obj-$(CONFIG_RTC_DRV_PPC) += rtc-ppc.o
+obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
@@ -76,3 +76,4 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
+obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index b6d35f50e404..23e10b6263d6 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -797,17 +797,15 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup2;
}
- pr_info("%s: alarms up to one %s%s, %zd bytes nvram%s\n",
- dev_name(&cmos_rtc.rtc->dev),
- is_valid_irq(rtc_irq)
- ? (cmos_rtc.mon_alrm
- ? "year"
- : (cmos_rtc.day_alrm
- ? "month" : "day"))
- : "no",
- cmos_rtc.century ? ", y3k" : "",
- nvram.size,
- is_hpet_enabled() ? ", hpet irqs" : "");
+ pr_info("%s: %s%s, %zd bytes nvram%s\n",
+ dev_name(&cmos_rtc.rtc->dev),
+ !is_valid_irq(rtc_irq) ? "no alarms" :
+ cmos_rtc.mon_alrm ? "alarms up to one year" :
+ cmos_rtc.day_alrm ? "alarms up to one month" :
+ "alarms up to one day",
+ cmos_rtc.century ? ", y3k" : "",
+ nvram.size,
+ is_hpet_enabled() ? ", hpet irqs" : "");
return 0;
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 7e5155e88ac7..2c4a65302a9d 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2005 James Chapman (ds1337 core)
* Copyright (C) 2006 David Brownell
+ * Copyright (C) 2009 Matthias Fuchs (rx8025 support)
*
* 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
@@ -31,6 +32,7 @@ enum ds_type {
ds_1339,
ds_1340,
m41t00,
+ rx_8025,
// rs5c372 too? different address...
};
@@ -83,6 +85,12 @@ enum ds_type {
#define DS1339_REG_ALARM1_SECS 0x07
#define DS1339_REG_TRICKLE 0x10
+#define RX8025_REG_CTRL1 0x0e
+# define RX8025_BIT_2412 0x20
+#define RX8025_REG_CTRL2 0x0f
+# define RX8025_BIT_PON 0x10
+# define RX8025_BIT_VDET 0x40
+# define RX8025_BIT_XST 0x20
struct ds1307 {
@@ -94,6 +102,10 @@ struct ds1307 {
struct i2c_client *client;
struct rtc_device *rtc;
struct work_struct work;
+ s32 (*read_block_data)(struct i2c_client *client, u8 command,
+ u8 length, u8 *values);
+ s32 (*write_block_data)(struct i2c_client *client, u8 command,
+ u8 length, const u8 *values);
};
struct chip_desc {
@@ -117,6 +129,8 @@ static const struct chip_desc chips[] = {
[ds_1340] = {
},
[m41t00] = {
+},
+[rx_8025] = {
}, };
static const struct i2c_device_id ds1307_id[] = {
@@ -126,12 +140,86 @@ static const struct i2c_device_id ds1307_id[] = {
{ "ds1339", ds_1339 },
{ "ds1340", ds_1340 },
{ "m41t00", m41t00 },
+ { "rx8025", rx_8025 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);
/*----------------------------------------------------------------------*/
+#define BLOCK_DATA_MAX_TRIES 10
+
+static s32 ds1307_read_block_data_once(struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ s32 i, data;
+
+ for (i = 0; i < length; i++) {
+ data = i2c_smbus_read_byte_data(client, command + i);
+ if (data < 0)
+ return data;
+ values[i] = data;
+ }
+ return i;
+}
+
+static s32 ds1307_read_block_data(struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ u8 oldvalues[I2C_SMBUS_BLOCK_MAX];
+ s32 ret;
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length);
+ ret = ds1307_read_block_data_once(client, command, length, values);
+ if (ret < 0)
+ return ret;
+ do {
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_read_block_data failed\n");
+ return -EIO;
+ }
+ memcpy(oldvalues, values, length);
+ ret = ds1307_read_block_data_once(client, command, length,
+ values);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(oldvalues, values, length));
+ return length;
+}
+
+static s32 ds1307_write_block_data(struct i2c_client *client, u8 command,
+ u8 length, const u8 *values)
+{
+ u8 currvalues[I2C_SMBUS_BLOCK_MAX];
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length);
+ do {
+ s32 i, ret;
+
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_write_block_data failed\n");
+ return -EIO;
+ }
+ for (i = 0; i < length; i++) {
+ ret = i2c_smbus_write_byte_data(client, command + i,
+ values[i]);
+ if (ret < 0)
+ return ret;
+ }
+ ret = ds1307_read_block_data_once(client, command, length,
+ currvalues);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(currvalues, values, length));
+ return length;
+}
+
+/*----------------------------------------------------------------------*/
+
/*
* The IRQ logic includes a "real" handler running in IRQ context just
* long enough to schedule this workqueue entry. We need a task context
@@ -202,7 +290,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
int tmp;
/* read the RTC date and time registers all at once */
- tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ tmp = ds1307->read_block_data(ds1307->client,
DS1307_REG_SECS, 7, ds1307->regs);
if (tmp != 7) {
dev_err(dev, "%s error %d\n", "read", tmp);
@@ -279,7 +367,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
"write", buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6]);
- result = i2c_smbus_write_i2c_block_data(ds1307->client, 0, 7, buf);
+ result = ds1307->write_block_data(ds1307->client, 0, 7, buf);
if (result < 0) {
dev_err(dev, "%s error %d\n", "write", result);
return result;
@@ -297,7 +385,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
return -EINVAL;
/* read all ALARM1, ALARM2, and status registers at once */
- ret = i2c_smbus_read_i2c_block_data(client,
+ ret = ds1307->read_block_data(client,
DS1339_REG_ALARM1_SECS, 9, ds1307->regs);
if (ret != 9) {
dev_err(dev, "%s error %d\n", "alarm read", ret);
@@ -356,7 +444,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
t->enabled, t->pending);
/* read current status of both alarms and the chip */
- ret = i2c_smbus_read_i2c_block_data(client,
+ ret = ds1307->read_block_data(client,
DS1339_REG_ALARM1_SECS, 9, buf);
if (ret != 9) {
dev_err(dev, "%s error %d\n", "alarm write", ret);
@@ -391,7 +479,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
}
buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
- ret = i2c_smbus_write_i2c_block_data(client,
+ ret = ds1307->write_block_data(client,
DS1339_REG_ALARM1_SECS, 9, buf);
if (ret < 0) {
dev_err(dev, "can't set alarm time\n");
@@ -479,7 +567,7 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
if (unlikely(!count))
return count;
- result = i2c_smbus_read_i2c_block_data(client, 8 + off, count, buf);
+ result = ds1307->read_block_data(client, 8 + off, count, buf);
if (result < 0)
dev_err(&client->dev, "%s error %d\n", "nvram read", result);
return result;
@@ -490,9 +578,11 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct i2c_client *client;
+ struct ds1307 *ds1307;
int result;
client = kobj_to_i2c_client(kobj);
+ ds1307 = i2c_get_clientdata(client);
if (unlikely(off >= NVRAM_SIZE))
return -EFBIG;
@@ -501,7 +591,7 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
if (unlikely(!count))
return count;
- result = i2c_smbus_write_i2c_block_data(client, 8 + off, count, buf);
+ result = ds1307->write_block_data(client, 8 + off, count, buf);
if (result < 0) {
dev_err(&client->dev, "%s error %d\n", "nvram write", result);
return result;
@@ -535,9 +625,8 @@ static int __devinit ds1307_probe(struct i2c_client *client,
int want_irq = false;
unsigned char *buf;
- if (!i2c_check_functionality(adapter,
- I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
- I2C_FUNC_SMBUS_I2C_BLOCK))
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
+ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return -EIO;
if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL)))
@@ -547,6 +636,13 @@ static int __devinit ds1307_probe(struct i2c_client *client,
i2c_set_clientdata(client, ds1307);
ds1307->type = id->driver_data;
buf = ds1307->regs;
+ if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ ds1307->read_block_data = i2c_smbus_read_i2c_block_data;
+ ds1307->write_block_data = i2c_smbus_write_i2c_block_data;
+ } else {
+ ds1307->read_block_data = ds1307_read_block_data;
+ ds1307->write_block_data = ds1307_write_block_data;
+ }
switch (ds1307->type) {
case ds_1337:
@@ -557,7 +653,7 @@ static int __devinit ds1307_probe(struct i2c_client *client,
want_irq = true;
}
/* get registers that the "rtc" read below won't read... */
- tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ tmp = ds1307->read_block_data(ds1307->client,
DS1337_REG_CONTROL, 2, buf);
if (tmp != 2) {
pr_debug("read error %d\n", tmp);
@@ -589,13 +685,79 @@ static int __devinit ds1307_probe(struct i2c_client *client,
dev_warn(&client->dev, "SET TIME!\n");
}
break;
+
+ case rx_8025:
+ tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
+ if (tmp != 2) {
+ pr_debug("read error %d\n", tmp);
+ err = -EIO;
+ goto exit_free;
+ }
+
+ /* oscillator off? turn it on, so clock can tick. */
+ if (!(ds1307->regs[1] & RX8025_BIT_XST)) {
+ ds1307->regs[1] |= RX8025_BIT_XST;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev,
+ "oscillator stop detected - SET TIME!\n");
+ }
+
+ if (ds1307->regs[1] & RX8025_BIT_PON) {
+ ds1307->regs[1] &= ~RX8025_BIT_PON;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev, "power-on detected\n");
+ }
+
+ if (ds1307->regs[1] & RX8025_BIT_VDET) {
+ ds1307->regs[1] &= ~RX8025_BIT_VDET;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev, "voltage drop detected\n");
+ }
+
+ /* make sure we are running in 24hour mode */
+ if (!(ds1307->regs[0] & RX8025_BIT_2412)) {
+ u8 hour;
+
+ /* switch to 24 hour mode */
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL1 << 4 | 0x08,
+ ds1307->regs[0] |
+ RX8025_BIT_2412);
+
+ tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
+ if (tmp != 2) {
+ pr_debug("read error %d\n", tmp);
+ err = -EIO;
+ goto exit_free;
+ }
+
+ /* correct hour */
+ hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]);
+ if (hour == 12)
+ hour = 0;
+ if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
+ hour += 12;
+
+ i2c_smbus_write_byte_data(client,
+ DS1307_REG_HOUR << 4 | 0x08,
+ hour);
+ }
+ break;
default:
break;
}
read_rtc:
/* read RTC registers */
- tmp = i2c_smbus_read_i2c_block_data(ds1307->client, 0, 8, buf);
+ tmp = ds1307->read_block_data(ds1307->client, 0, 8, buf);
if (tmp != 8) {
pr_debug("read error %d\n", tmp);
err = -EIO;
@@ -649,6 +811,7 @@ read_rtc:
dev_warn(&client->dev, "SET TIME!\n");
}
break;
+ case rx_8025:
case ds_1337:
case ds_1339:
break;
@@ -662,6 +825,8 @@ read_rtc:
* systems that will run through year 2100.
*/
break;
+ case rx_8025:
+ break;
default:
if (!(tmp & DS1307_BIT_12HR))
break;
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index a5b0fc09f0c6..4d32e328f6cd 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -222,16 +222,16 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
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)
+ if (time_before_eq(new_alarm, itime))
new_alarm = 1;
+ else
+ new_alarm -= itime;
mutex_lock(&ds1374->mutex);
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 000000000000..550292304b0f
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,235 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <dannf@hp.com>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/efi.h>
+
+#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
+/*
+ * EFI Epoch is 1/1/1998
+ */
+#define EFI_RTC_EPOCH 1998
+
+/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(efi_time_t *eft)
+{
+ /* efi_time_t.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(eft->day - 1, eft->month - 1, eft->year);
+}
+/*
+ * returns day of the week [0-6] 0=Sunday
+ *
+ * Don't try to provide a year that's before 1998, please !
+ */
+static int
+compute_wday(efi_time_t *eft)
+{
+ int y;
+ int ndays = 0;
+
+ if (eft->year < 1998) {
+ printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(eft);
+
+ /*
+ * 4=1/1/1998 was a Thursday
+ */
+ return (ndays + 4) % 7;
+}
+
+static void
+convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
+{
+ eft->year = wtime->tm_year + 1900;
+ eft->month = wtime->tm_mon + 1;
+ eft->day = wtime->tm_mday;
+ eft->hour = wtime->tm_hour;
+ eft->minute = wtime->tm_min;
+ eft->second = wtime->tm_sec;
+ eft->nanosecond = 0;
+ eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+ eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+}
+
+static void
+convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
+{
+ memset(wtime, 0, sizeof(*wtime));
+ wtime->tm_sec = eft->second;
+ wtime->tm_min = eft->minute;
+ wtime->tm_hour = eft->hour;
+ wtime->tm_mday = eft->day;
+ wtime->tm_mon = eft->month - 1;
+ wtime->tm_year = eft->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ wtime->tm_wday = compute_wday(eft);
+
+ /* day in the year [1-365]*/
+ wtime->tm_yday = compute_yday(eft);
+
+
+ switch (eft->daylight & EFI_ISDST) {
+ case EFI_ISDST:
+ wtime->tm_isdst = 1;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ wtime->tm_isdst = 0;
+ break;
+ default:
+ wtime->tm_isdst = -1;
+ }
+}
+
+static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ convert_from_efi_time(&eft, &wkalrm->time);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ printk(KERN_WARNING "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_read_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ printk(KERN_ERR "efitime: can't read time\n");
+ return -EINVAL;
+ }
+
+ convert_from_efi_time(&eft, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .read_time = efi_read_time,
+ .set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
+};
+
+static int __init efi_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __exit efi_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ .owner = THIS_MODULE,
+ },
+ .probe = efi_rtc_probe,
+ .remove = __exit_p(efi_rtc_remove),
+};
+
+static int __init efi_rtc_init(void)
+{
+ return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
+}
+
+static void __exit efi_rtc_exit(void)
+{
+ platform_driver_unregister(&efi_rtc_driver);
+}
+
+module_init(efi_rtc_init);
+module_exit(efi_rtc_exit);
+
+MODULE_AUTHOR("dann frazier <dannf@hp.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c
new file mode 100644
index 000000000000..98322004ad2e
--- /dev/null
+++ b/drivers/rtc/rtc-generic.c
@@ -0,0 +1,84 @@
+/* rtc-generic: RTC driver using the generic RTC abstraction
+ *
+ * Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <asm/rtc.h>
+
+static int generic_get_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned int ret = get_rtc_time(tm);
+
+ if (ret & RTC_BATT_BAD)
+ return -EOPNOTSUPP;
+
+ return rtc_valid_tm(tm);
+}
+
+static int generic_set_time(struct device *dev, struct rtc_time *tm)
+{
+ if (set_rtc_time(tm) < 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static const struct rtc_class_ops generic_rtc_ops = {
+ .read_time = generic_get_time,
+ .set_time = generic_set_time,
+};
+
+static int __init generic_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = rtc_device_register("rtc-generic", &dev->dev, &generic_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static int __exit generic_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct platform_driver generic_rtc_driver = {
+ .driver = {
+ .name = "rtc-generic",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(generic_rtc_remove),
+};
+
+static int __init generic_rtc_init(void)
+{
+ return platform_driver_probe(&generic_rtc_driver, generic_rtc_probe);
+}
+
+static void __exit generic_rtc_fini(void)
+{
+ platform_driver_unregister(&generic_rtc_driver);
+}
+
+module_init(generic_rtc_init);
+module_exit(generic_rtc_fini);
+
+MODULE_AUTHOR("Kyle McMartin <kyle@mcmartin.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic RTC driver");
+MODULE_ALIAS("platform:rtc-generic");
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index dd70bf73ce9d..773851f338b8 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -26,14 +26,13 @@ static const unsigned short rtc_ydays[2][13] = {
};
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
-#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
/*
* The number of days in the month.
*/
int rtc_month_days(unsigned int month, unsigned int year)
{
- return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+ return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
}
EXPORT_SYMBOL(rtc_month_days);
@@ -42,7 +41,7 @@ EXPORT_SYMBOL(rtc_month_days);
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
- return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
+ return rtc_ydays[is_leap_year(year)][month] + day-1;
}
EXPORT_SYMBOL(rtc_year_days);
@@ -66,7 +65,7 @@ void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
- days += 365 + LEAP_YEAR(year);
+ days += 365 + is_leap_year(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 893f7dece239..60fe266f0f49 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -64,10 +64,12 @@
#define M41T80_FEATURE_BL (1 << 1) /* Battery low indicator */
#define M41T80_FEATURE_SQ (1 << 2) /* Squarewave feature */
#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */
+#define M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */
#define DRV_VERSION "0.05"
static const struct i2c_device_id m41t80_id[] = {
+ { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
{ "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
{ "m41t80", M41T80_FEATURE_SQ },
{ "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
@@ -393,12 +395,15 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
- int val;
+ int val, reg_sqw;
if (!(clientdata->features & M41T80_FEATURE_SQ))
return -EINVAL;
- val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW);
+ reg_sqw = M41T80_REG_SQW;
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT)
+ reg_sqw = M41T80_REG_WDAY;
+ val = i2c_smbus_read_byte_data(client, reg_sqw);
if (val < 0)
return -EIO;
val = (val >> 4) & 0xf;
@@ -419,7 +424,7 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
- int almon, sqw;
+ int almon, sqw, reg_sqw;
int val = simple_strtoul(buf, NULL, 0);
if (!(clientdata->features & M41T80_FEATURE_SQ))
@@ -440,13 +445,16 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (almon < 0)
return -EIO;
- sqw = i2c_smbus_read_byte_data(client, M41T80_REG_SQW);
+ reg_sqw = M41T80_REG_SQW;
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT)
+ reg_sqw = M41T80_REG_WDAY;
+ sqw = i2c_smbus_read_byte_data(client, reg_sqw);
if (sqw < 0)
return -EIO;
sqw = (sqw & 0x0f) | (val << 4);
if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
almon & ~M41T80_ALMON_SQWE) < 0 ||
- i2c_smbus_write_byte_data(client, M41T80_REG_SQW, sqw) < 0)
+ i2c_smbus_write_byte_data(client, reg_sqw, sqw) < 0)
return -EIO;
if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
almon | M41T80_ALMON_SQWE) < 0)
diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c
deleted file mode 100644
index c6bfa6fe1a2a..000000000000
--- a/drivers/rtc/rtc-parisc.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/* rtc-parisc: RTC for HP PA-RISC firmware
- *
- * Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/time.h>
-#include <linux/platform_device.h>
-
-#include <asm/rtc.h>
-
-/* as simple as can be, and no simpler. */
-struct parisc_rtc {
- struct rtc_device *rtc;
- spinlock_t lock;
-};
-
-static int parisc_get_time(struct device *dev, struct rtc_time *tm)
-{
- struct parisc_rtc *p = dev_get_drvdata(dev);
- unsigned long flags, ret;
-
- spin_lock_irqsave(&p->lock, flags);
- ret = get_rtc_time(tm);
- spin_unlock_irqrestore(&p->lock, flags);
-
- if (ret & RTC_BATT_BAD)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static int parisc_set_time(struct device *dev, struct rtc_time *tm)
-{
- struct parisc_rtc *p = dev_get_drvdata(dev);
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&p->lock, flags);
- ret = set_rtc_time(tm);
- spin_unlock_irqrestore(&p->lock, flags);
-
- if (ret < 0)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static const struct rtc_class_ops parisc_rtc_ops = {
- .read_time = parisc_get_time,
- .set_time = parisc_set_time,
-};
-
-static int __devinit parisc_rtc_probe(struct platform_device *dev)
-{
- struct parisc_rtc *p;
-
- p = kzalloc(sizeof (*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- spin_lock_init(&p->lock);
-
- p->rtc = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(p->rtc)) {
- int err = PTR_ERR(p->rtc);
- kfree(p);
- return err;
- }
-
- platform_set_drvdata(dev, p);
-
- return 0;
-}
-
-static int __devexit parisc_rtc_remove(struct platform_device *dev)
-{
- struct parisc_rtc *p = platform_get_drvdata(dev);
-
- rtc_device_unregister(p->rtc);
- kfree(p);
-
- return 0;
-}
-
-static struct platform_driver parisc_rtc_driver = {
- .driver = {
- .name = "rtc-parisc",
- .owner = THIS_MODULE,
- },
- .probe = parisc_rtc_probe,
- .remove = __devexit_p(parisc_rtc_remove),
-};
-
-static int __init parisc_rtc_init(void)
-{
- return platform_driver_register(&parisc_rtc_driver);
-}
-
-static void __exit parisc_rtc_fini(void)
-{
- platform_driver_unregister(&parisc_rtc_driver);
-}
-
-module_init(parisc_rtc_init);
-module_exit(parisc_rtc_fini);
-
-MODULE_AUTHOR("Kyle McMartin <kyle@mcmartin.ca>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("HP PA-RISC RTC driver");
diff --git a/drivers/rtc/rtc-ppc.c b/drivers/rtc/rtc-ppc.c
deleted file mode 100644
index c8e97e25ef7e..000000000000
--- a/drivers/rtc/rtc-ppc.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * RTC driver for ppc_md RTC functions
- *
- * © 2007 Red Hat, Inc.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * 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/err.h>
-#include <linux/rtc.h>
-#include <linux/platform_device.h>
-#include <asm/machdep.h>
-
-static int ppc_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
- ppc_md.get_rtc_time(tm);
- return 0;
-}
-
-static int ppc_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
- return ppc_md.set_rtc_time(tm);
-}
-
-static const struct rtc_class_ops ppc_rtc_ops = {
- .set_time = ppc_rtc_set_time,
- .read_time = ppc_rtc_read_time,
-};
-
-static struct rtc_device *rtc;
-static struct platform_device *ppc_rtc_pdev;
-
-static int __init ppc_rtc_init(void)
-{
- if (!ppc_md.get_rtc_time || !ppc_md.set_rtc_time)
- return -ENODEV;
-
- ppc_rtc_pdev = platform_device_register_simple("ppc-rtc", 0, NULL, 0);
- if (IS_ERR(ppc_rtc_pdev))
- return PTR_ERR(ppc_rtc_pdev);
-
- rtc = rtc_device_register("ppc_md", &ppc_rtc_pdev->dev,
- &ppc_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- platform_device_unregister(ppc_rtc_pdev);
- return PTR_ERR(rtc);
- }
-
- return 0;
-}
-
-static void __exit ppc_rtc_exit(void)
-{
- rtc_device_unregister(rtc);
- platform_device_unregister(ppc_rtc_pdev);
-}
-
-module_init(ppc_rtc_init);
-module_exit(ppc_rtc_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("Generic RTC class driver for PowerPC");
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
index 0c6257a034ff..c086fc30a84c 100644
--- a/drivers/rtc/rtc-proc.c
+++ b/drivers/rtc/rtc-proc.c
@@ -105,14 +105,8 @@ static const struct file_operations rtc_proc_fops = {
void rtc_proc_add_device(struct rtc_device *rtc)
{
- if (rtc->id == 0) {
- struct proc_dir_entry *ent;
-
- ent = proc_create_data("driver/rtc", 0, NULL,
- &rtc_proc_fops, rtc);
- if (ent)
- ent->owner = rtc->owner;
- }
+ if (rtc->id == 0)
+ proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
}
void rtc_proc_del_device(struct rtc_device *rtc)
diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c
new file mode 100644
index 000000000000..968133ce1ee8
--- /dev/null
+++ b/drivers/rtc/rtc-ps3.c
@@ -0,0 +1,104 @@
+/*
+ * PS3 RTC Driver
+ *
+ * Copyright 2009 Sony Corporation
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3.h>
+
+
+static u64 read_rtc(void)
+{
+ int result;
+ u64 rtc_val;
+ u64 tb_val;
+
+ result = lv1_get_rtc(&rtc_val, &tb_val);
+ BUG_ON(result);
+
+ return rtc_val;
+}
+
+static int ps3_get_time(struct device *dev, struct rtc_time *tm)
+{
+ rtc_time_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm);
+ return rtc_valid_tm(tm);
+}
+
+static int ps3_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long now;
+
+ rtc_tm_to_time(tm, &now);
+ ps3_os_area_set_rtc_diff(now - read_rtc());
+ return 0;
+}
+
+static const struct rtc_class_ops ps3_rtc_ops = {
+ .read_time = ps3_get_time,
+ .set_time = ps3_set_time,
+};
+
+static int __init ps3_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = rtc_device_register("rtc-ps3", &dev->dev, &ps3_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+ return 0;
+}
+
+static int __exit ps3_rtc_remove(struct platform_device *dev)
+{
+ rtc_device_unregister(platform_get_drvdata(dev));
+ return 0;
+}
+
+static struct platform_driver ps3_rtc_driver = {
+ .driver = {
+ .name = "rtc-ps3",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ps3_rtc_remove),
+};
+
+static int __init ps3_rtc_init(void)
+{
+ return platform_driver_probe(&ps3_rtc_driver, ps3_rtc_probe);
+}
+
+static void __exit ps3_rtc_fini(void)
+{
+ platform_driver_unregister(&ps3_rtc_driver);
+}
+
+module_init(ps3_rtc_init);
+module_exit(ps3_rtc_fini);
+
+MODULE_AUTHOR("Sony Corporation");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ps3 RTC driver");
+MODULE_ALIAS("platform:rtc-ps3");
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 4898f7fe8518..d7310adb7152 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, 2007, 2008 Paul Mundt
+ * Copyright (C) 2006 - 2009 Paul Mundt
* Copyright (C) 2006 Jamie Lenehan
* Copyright (C) 2008 Angelo Castello
*
@@ -25,10 +25,11 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/log2.h>
+#include <linux/clk.h>
#include <asm/rtc.h>
#define DRV_NAME "sh-rtc"
-#define DRV_VERSION "0.2.1"
+#define DRV_VERSION "0.2.2"
#define RTC_REG(r) ((r) * rtc_reg_size)
@@ -87,16 +88,17 @@
#define RCR2_START 0x01 /* Start bit */
struct sh_rtc {
- void __iomem *regbase;
- unsigned long regsize;
- struct resource *res;
- int alarm_irq;
- int periodic_irq;
- int carry_irq;
- struct rtc_device *rtc_dev;
- spinlock_t lock;
- unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */
- unsigned short periodic_freq;
+ void __iomem *regbase;
+ unsigned long regsize;
+ struct resource *res;
+ int alarm_irq;
+ int periodic_irq;
+ int carry_irq;
+ struct clk *clk;
+ struct rtc_device *rtc_dev;
+ spinlock_t lock;
+ unsigned long capabilities; /* See asm/rtc.h for cap bits */
+ unsigned short periodic_freq;
};
static int __sh_rtc_interrupt(struct sh_rtc *rtc)
@@ -294,10 +296,10 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
tmp = readb(rtc->regbase + RCR1);
- if (!enable)
- tmp &= ~RCR1_AIE;
- else
+ if (enable)
tmp |= RCR1_AIE;
+ else
+ tmp &= ~RCR1_AIE;
writeb(tmp, rtc->regbase + RCR1);
@@ -618,6 +620,7 @@ static int sh_rtc_irq_set_freq(struct device *dev, int freq)
{
if (!is_power_of_2(freq))
return -EINVAL;
+
return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq);
}
@@ -637,7 +640,8 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
struct sh_rtc *rtc;
struct resource *res;
struct rtc_time r;
- int ret;
+ char clk_name[6];
+ int clk_id, ret;
rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
if (unlikely(!rtc))
@@ -652,6 +656,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No IRQ resource\n");
goto err_badres;
}
+
rtc->periodic_irq = ret;
rtc->carry_irq = platform_get_irq(pdev, 1);
rtc->alarm_irq = platform_get_irq(pdev, 2);
@@ -663,7 +668,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
goto err_badres;
}
- rtc->regsize = res->end - res->start + 1;
+ rtc->regsize = resource_size(res);
rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name);
if (unlikely(!rtc->res)) {
@@ -677,6 +682,26 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
goto err_badmap;
}
+ clk_id = pdev->id;
+ /* With a single device, the clock id is still "rtc0" */
+ if (clk_id < 0)
+ clk_id = 0;
+
+ snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id);
+
+ rtc->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(rtc->clk)) {
+ /*
+ * No error handling for rtc->clk intentionally, not all
+ * platforms will have a unique clock for the RTC, and
+ * the clk API can handle the struct clk pointer being
+ * NULL.
+ */
+ rtc->clk = NULL;
+ }
+
+ clk_enable(rtc->clk);
+
rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
&sh_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc_dev)) {
@@ -759,6 +784,8 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev)
return 0;
err_unmap:
+ clk_disable(rtc->clk);
+ clk_put(rtc->clk);
iounmap(rtc->regbase);
err_badmap:
release_resource(rtc->res);
@@ -780,6 +807,7 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev)
sh_rtc_setcie(&pdev->dev, 0);
free_irq(rtc->periodic_irq, rtc);
+
if (rtc->carry_irq > 0) {
free_irq(rtc->carry_irq, rtc);
free_irq(rtc->alarm_irq, rtc);
@@ -789,16 +817,55 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev)
iounmap(rtc->regbase);
+ clk_disable(rtc->clk);
+ clk_put(rtc->clk);
+
platform_set_drvdata(pdev, NULL);
kfree(rtc);
return 0;
}
+
+static void sh_rtc_set_irq_wake(struct device *dev, int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+
+ set_irq_wake(rtc->periodic_irq, enabled);
+
+ if (rtc->carry_irq > 0) {
+ set_irq_wake(rtc->carry_irq, enabled);
+ set_irq_wake(rtc->alarm_irq, enabled);
+ }
+}
+
+static int sh_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev))
+ sh_rtc_set_irq_wake(dev, 1);
+
+ return 0;
+}
+
+static int sh_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev))
+ sh_rtc_set_irq_wake(dev, 0);
+
+ return 0;
+}
+
+static struct dev_pm_ops sh_rtc_dev_pm_ops = {
+ .suspend = sh_rtc_suspend,
+ .resume = sh_rtc_resume,
+};
+
static struct platform_driver sh_rtc_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .pm = &sh_rtc_dev_pm_ops,
},
.probe = sh_rtc_probe,
.remove = __devexit_p(sh_rtc_remove),
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index 14d4f036a768..ad164056feb6 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -27,17 +27,162 @@
#include <linux/bcd.h>
#include <linux/rtc-v3020.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
-#include <asm/io.h>
+#include <linux/io.h>
#undef DEBUG
+struct v3020;
+
+struct v3020_chip_ops {
+ int (*map_io)(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata);
+ void (*unmap_io)(struct v3020 *chip);
+ unsigned char (*read_bit)(struct v3020 *chip);
+ void (*write_bit)(struct v3020 *chip, unsigned char bit);
+};
+
+#define V3020_CS 0
+#define V3020_WR 1
+#define V3020_RD 2
+#define V3020_IO 3
+
+struct v3020_gpio {
+ const char *name;
+ unsigned int gpio;
+};
+
struct v3020 {
+ /* MMIO access */
void __iomem *ioaddress;
int leftshift;
+
+ /* GPIO access */
+ struct v3020_gpio *gpio;
+
+ struct v3020_chip_ops *ops;
+
struct rtc_device *rtc;
};
+
+static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata)
+{
+ if (pdev->num_resources != 1)
+ return -EBUSY;
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM)
+ return -EBUSY;
+
+ chip->leftshift = pdata->leftshift;
+ chip->ioaddress = ioremap(pdev->resource[0].start, 1);
+ if (chip->ioaddress == NULL)
+ return -EBUSY;
+
+ return 0;
+}
+
+static void v3020_mmio_unmap(struct v3020 *chip)
+{
+ iounmap(chip->ioaddress);
+}
+
+static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit)
+{
+ writel(bit << chip->leftshift, chip->ioaddress);
+}
+
+static unsigned char v3020_mmio_read_bit(struct v3020 *chip)
+{
+ return readl(chip->ioaddress) & (1 << chip->leftshift);
+}
+
+static struct v3020_chip_ops v3020_mmio_ops = {
+ .map_io = v3020_mmio_map,
+ .unmap_io = v3020_mmio_unmap,
+ .read_bit = v3020_mmio_read_bit,
+ .write_bit = v3020_mmio_write_bit,
+};
+
+static struct v3020_gpio v3020_gpio[] = {
+ { "RTC CS", 0 },
+ { "RTC WR", 0 },
+ { "RTC RD", 0 },
+ { "RTC IO", 0 },
+};
+
+static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata)
+{
+ int i, err;
+
+ v3020_gpio[V3020_CS].gpio = pdata->gpio_cs;
+ v3020_gpio[V3020_WR].gpio = pdata->gpio_wr;
+ v3020_gpio[V3020_RD].gpio = pdata->gpio_rd;
+ v3020_gpio[V3020_IO].gpio = pdata->gpio_io;
+
+ for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) {
+ err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name);
+ if (err)
+ goto err_request;
+
+ gpio_direction_output(v3020_gpio[i].gpio, 1);
+ }
+
+ chip->gpio = v3020_gpio;
+
+ return 0;
+
+err_request:
+ while (--i >= 0)
+ gpio_free(v3020_gpio[i].gpio);
+
+ return err;
+}
+
+static void v3020_gpio_unmap(struct v3020 *chip)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++)
+ gpio_free(v3020_gpio[i].gpio);
+}
+
+static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit)
+{
+ gpio_direction_output(chip->gpio[V3020_IO].gpio, bit);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
+ gpio_set_value(chip->gpio[V3020_WR].gpio, 0);
+ udelay(1);
+ gpio_set_value(chip->gpio[V3020_WR].gpio, 1);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 1);
+}
+
+static unsigned char v3020_gpio_read_bit(struct v3020 *chip)
+{
+ int bit;
+
+ gpio_direction_input(chip->gpio[V3020_IO].gpio);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
+ gpio_set_value(chip->gpio[V3020_RD].gpio, 0);
+ udelay(1);
+ bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio);
+ udelay(1);
+ gpio_set_value(chip->gpio[V3020_RD].gpio, 1);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 1);
+
+ return bit;
+}
+
+static struct v3020_chip_ops v3020_gpio_ops = {
+ .map_io = v3020_gpio_map,
+ .unmap_io = v3020_gpio_unmap,
+ .read_bit = v3020_gpio_read_bit,
+ .write_bit = v3020_gpio_write_bit,
+};
+
static void v3020_set_reg(struct v3020 *chip, unsigned char address,
unsigned char data)
{
@@ -46,7 +191,7 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address,
tmp = address;
for (i = 0; i < 4; i++) {
- writel((tmp & 1) << chip->leftshift, chip->ioaddress);
+ chip->ops->write_bit(chip, (tmp & 1));
tmp >>= 1;
udelay(1);
}
@@ -54,7 +199,7 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address,
/* Commands dont have data */
if (!V3020_IS_COMMAND(address)) {
for (i = 0; i < 8; i++) {
- writel((data & 1) << chip->leftshift, chip->ioaddress);
+ chip->ops->write_bit(chip, (data & 1));
data >>= 1;
udelay(1);
}
@@ -63,18 +208,18 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address,
static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
{
- unsigned int data=0;
+ unsigned int data = 0;
int i;
for (i = 0; i < 4; i++) {
- writel((address & 1) << chip->leftshift, chip->ioaddress);
+ chip->ops->write_bit(chip, (address & 1));
address >>= 1;
udelay(1);
}
for (i = 0; i < 8; i++) {
data >>= 1;
- if (readl(chip->ioaddress) & (1 << chip->leftshift))
+ if (chip->ops->read_bit(chip))
data |= 0x80;
udelay(1);
}
@@ -106,16 +251,14 @@ static int v3020_read_time(struct device *dev, struct rtc_time *dt)
tmp = v3020_get_reg(chip, V3020_YEAR);
dt->tm_year = bcd2bin(tmp)+100;
-#ifdef DEBUG
- printk("\n%s : Read RTC values\n",__func__);
- 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
+ dev_dbg(dev, "\n%s : Read RTC values\n", __func__);
+ dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
+ dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
+ dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
+ dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
+ dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon);
+ dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
+ dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
return 0;
}
@@ -125,15 +268,13 @@ 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",__func__);
- 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
+ dev_dbg(dev, "\n%s : Setting RTC values\n", __func__);
+ dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
+ dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
+ dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
+ dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
+ dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
+ dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
/* Write all the values to ram... */
v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec));
@@ -168,30 +309,28 @@ static int rtc_probe(struct platform_device *pdev)
int i;
int temp;
- if (pdev->num_resources != 1)
- return -EBUSY;
-
- if (pdev->resource[0].flags != IORESOURCE_MEM)
- 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)
+ if (pdata->use_gpio)
+ chip->ops = &v3020_gpio_ops;
+ else
+ chip->ops = &v3020_mmio_ops;
+
+ retval = chip->ops->map_io(chip, pdev, pdata);
+ if (retval)
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);
+ temp = chip->ops->read_bit(chip);
/* 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) {
+ if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) {
retval = -ENODEV;
goto err_io;
}
@@ -200,10 +339,17 @@ static int rtc_probe(struct platform_device *pdev)
* are all disabled */
v3020_set_reg(chip, V3020_STATUS_0, 0x0);
- dev_info(&pdev->dev, "Chip available at physical address 0x%llx,"
- "data connected to D%d\n",
- (unsigned long long)pdev->resource[0].start,
- chip->leftshift);
+ if (pdata->use_gpio)
+ dev_info(&pdev->dev, "Chip available at GPIOs "
+ "%d, %d, %d, %d\n",
+ chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio,
+ chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio);
+ else
+ dev_info(&pdev->dev, "Chip available at "
+ "physical address 0x%llx,"
+ "data connected to D%d\n",
+ (unsigned long long)pdev->resource[0].start,
+ chip->leftshift);
platform_set_drvdata(pdev, chip);
@@ -218,7 +364,7 @@ static int rtc_probe(struct platform_device *pdev)
return 0;
err_io:
- iounmap(chip->ioaddress);
+ chip->ops->unmap_io(chip);
err_chip:
kfree(chip);
@@ -233,7 +379,7 @@ static int rtc_remove(struct platform_device *dev)
if (rtc)
rtc_device_unregister(rtc);
- iounmap(chip->ioaddress);
+ chip->ops->unmap_io(chip);
kfree(chip);
return 0;
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
index 5c5e3aa91385..c91edc572eb6 100644
--- a/drivers/rtc/rtc-wm8350.c
+++ b/drivers/rtc/rtc-wm8350.c
@@ -122,7 +122,7 @@ static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm)
do {
rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
schedule_timeout_uninterruptible(msecs_to_jiffies(1));
- } while (retries-- && !(rtc_ctrl & WM8350_RTC_STS));
+ } while (--retries && !(rtc_ctrl & WM8350_RTC_STS));
if (!retries) {
dev_err(dev, "timed out on set confirmation\n");
@@ -236,6 +236,17 @@ static int wm8350_rtc_start_alarm(struct wm8350 *wm8350)
return 0;
}
+static int wm8350_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+
+ if (enabled)
+ return wm8350_rtc_start_alarm(wm8350);
+ else
+ return wm8350_rtc_stop_alarm(wm8350);
+}
+
static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct wm8350 *wm8350 = dev_get_drvdata(dev);
@@ -291,30 +302,15 @@ static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
return ret;
}
-/*
- * Handle commands from user-space
- */
-static int wm8350_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int wm8350_rtc_update_irq_enable(struct device *dev,
+ unsigned int enabled)
{
struct wm8350 *wm8350 = dev_get_drvdata(dev);
- switch (cmd) {
- case RTC_AIE_OFF:
- return wm8350_rtc_stop_alarm(wm8350);
- case RTC_AIE_ON:
- return wm8350_rtc_start_alarm(wm8350);
-
- case RTC_UIE_OFF:
- wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
- break;
- case RTC_UIE_ON:
+ if (enabled)
wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC);
- break;
-
- default:
- return -ENOIOCTLCMD;
- }
+ else
+ wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
return 0;
}
@@ -345,11 +341,12 @@ static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq,
}
static const struct rtc_class_ops wm8350_rtc_ops = {
- .ioctl = wm8350_rtc_ioctl,
.read_time = wm8350_rtc_readtime,
.set_time = wm8350_rtc_settime,
.read_alarm = wm8350_rtc_readalarm,
.set_alarm = wm8350_rtc_setalarm,
+ .alarm_irq_enable = wm8350_rtc_alarm_irq_enable,
+ .update_irq_enable = wm8350_rtc_update_irq_enable,
};
#ifdef CONFIG_PM
@@ -440,7 +437,7 @@ static int wm8350_rtc_probe(struct platform_device *pdev)
do {
timectl = wm8350_reg_read(wm8350,
WM8350_RTC_TIME_CONTROL);
- } while (timectl & WM8350_RTC_STS && retries--);
+ } while (timectl & WM8350_RTC_STS && --retries);
if (retries == 0) {
dev_err(&pdev->dev, "failed to start: timeout\n");