summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig29
-rw-r--r--drivers/rtc/Makefile5
-rw-r--r--drivers/rtc/class.c16
-rw-r--r--drivers/rtc/interface.c2
-rw-r--r--drivers/rtc/rtc-ds1343.c75
-rw-r--r--drivers/rtc/rtc-ds1742.c2
-rw-r--r--drivers/rtc/rtc-efi-platform.c31
-rw-r--r--drivers/rtc/rtc-efi.c32
-rw-r--r--drivers/rtc/rtc-isl12022.c12
-rw-r--r--drivers/rtc/rtc-pcf85063.c204
-rw-r--r--drivers/rtc/rtc-pcf8563.c231
-rw-r--r--drivers/rtc/rtc-tps65910.c4
12 files changed, 591 insertions, 52 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 0754f5c..a168e96 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -373,6 +373,14 @@ config RTC_DRV_PCF8563
This driver can also be built as a module. If so, the module
will be called rtc-pcf8563.
+config RTC_DRV_PCF85063
+ tristate "nxp PCF85063"
+ help
+ If you say yes here you get support for the PCF85063 RTC chip
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf85063.
+
config RTC_DRV_PCF8583
tristate "Philips PCF8583"
help
@@ -760,6 +768,15 @@ 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_DS2404
+ tristate "Maxim/Dallas DS2404"
+ help
+ If you say yes here you get support for the
+ Dallas DS2404 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds2404.
+
config RTC_DRV_DA9052
tristate "Dialog DA9052/DA9053 RTC"
depends on PMIC_DA9052
@@ -789,7 +806,7 @@ config RTC_DRV_DA9063
config RTC_DRV_EFI
tristate "EFI RTC"
- depends on IA64
+ depends on EFI
help
If you say yes here you will get support for the EFI
Real Time Clock.
@@ -873,15 +890,6 @@ config RTC_DRV_V3020
This driver can also be built as a module. If so, the module
will be called rtc-v3020.
-config RTC_DRV_DS2404
- tristate "Dallas DS2404"
- help
- If you say yes here you get support for the
- Dallas DS2404 RTC chip.
-
- This driver can also be built as a module. If so, the module
- will be called rtc-ds2404.
-
config RTC_DRV_WM831X
tristate "Wolfson Microelectronics WM831x RTC"
depends on MFD_WM831X
@@ -1349,6 +1357,7 @@ config RTC_DRV_SIRFSOC
config RTC_DRV_MOXART
tristate "MOXA ART RTC"
+ depends on ARCH_MOXART || COMPILE_TEST
help
If you say yes here you get support for the MOXA ART
RTC module.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 70347d0..56f061c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -10,6 +10,10 @@ obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o
+ifdef CONFIG_RTC_DRV_EFI
+rtc-core-y += rtc-efi-platform.o
+endif
+
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
@@ -93,6 +97,7 @@ obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
+obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 589351e..38e26be 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -53,6 +53,7 @@ static int rtc_suspend(struct device *dev)
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
struct timespec delta, delta_delta;
+ int err;
if (has_persistent_clock())
return 0;
@@ -61,7 +62,12 @@ static int rtc_suspend(struct device *dev)
return 0;
/* snapshot the current RTC and system time at suspend*/
- rtc_read_time(rtc, &tm);
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
getnstimeofday(&old_system);
rtc_tm_to_time(&tm, &old_rtc.tv_sec);
@@ -94,6 +100,7 @@ static int rtc_resume(struct device *dev)
struct rtc_time tm;
struct timespec new_system, new_rtc;
struct timespec sleep_time;
+ int err;
if (has_persistent_clock())
return 0;
@@ -104,7 +111,12 @@ static int rtc_resume(struct device *dev)
/* snapshot the current rtc and system time at resume */
getnstimeofday(&new_system);
- rtc_read_time(rtc, &tm);
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
if (rtc_valid_tm(&tm) != 0) {
pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
return 0;
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5813fa5..5b2717f 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -348,6 +348,8 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
/* Make sure we're not setting alarms in the past */
err = __rtc_read_time(rtc, &tm);
+ if (err)
+ return err;
rtc_tm_to_time(&tm, &now);
if (scheduled <= now)
return -ETIME;
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c
index c371918..ae9f997 100644
--- a/drivers/rtc/rtc-ds1343.c
+++ b/drivers/rtc/rtc-ds1343.c
@@ -4,6 +4,7 @@
* Real Time Clock
*
* Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
+ * Ankur Srivastava <sankurece@gmail.com> : DS1343 Nvram 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
@@ -45,6 +46,9 @@
#define DS1343_CONTROL_REG 0x0F
#define DS1343_STATUS_REG 0x10
#define DS1343_TRICKLE_REG 0x11
+#define DS1343_NVRAM 0x20
+
+#define DS1343_NVRAM_LEN 96
/* DS1343 Control Registers bits */
#define DS1343_EOSC 0x80
@@ -149,6 +153,64 @@ static ssize_t ds1343_store_glitchfilter(struct device *dev,
static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter,
ds1343_store_glitchfilter);
+static ssize_t ds1343_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_write(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram write %d", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static ssize_t ds1343_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_read(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram read %d\n", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static struct bin_attribute nvram_attr = {
+ .attr.name = "nvram",
+ .attr.mode = S_IRUGO | S_IWUSR,
+ .read = ds1343_nvram_read,
+ .write = ds1343_nvram_write,
+ .size = DS1343_NVRAM_LEN,
+};
+
static ssize_t ds1343_show_alarmstatus(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -274,12 +336,16 @@ static int ds1343_sysfs_register(struct device *dev)
if (err)
goto error1;
+ err = device_create_bin_file(dev, &nvram_attr);
+ if (err)
+ goto error2;
+
if (priv->irq <= 0)
return err;
err = device_create_file(dev, &dev_attr_alarm_mode);
if (err)
- goto error2;
+ goto error3;
err = device_create_file(dev, &dev_attr_alarm_status);
if (!err)
@@ -287,6 +353,9 @@ static int ds1343_sysfs_register(struct device *dev)
device_remove_file(dev, &dev_attr_alarm_mode);
+error3:
+ device_remove_bin_file(dev, &nvram_attr);
+
error2:
device_remove_file(dev, &dev_attr_trickle_charger);
@@ -302,6 +371,7 @@ static void ds1343_sysfs_unregister(struct device *dev)
device_remove_file(dev, &dev_attr_glitch_filter);
device_remove_file(dev, &dev_attr_trickle_charger);
+ device_remove_bin_file(dev, &nvram_attr);
if (priv->irq <= 0)
return;
@@ -684,6 +754,7 @@ static struct spi_driver ds1343_driver = {
module_spi_driver(ds1343_driver);
MODULE_DESCRIPTION("DS1343 RTC SPI Driver");
-MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>");
+MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>,"
+ "Ankur Srivastava <sankurece@gmail.com>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DS1343_DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index c6b2191..9822715 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -231,7 +231,7 @@ static struct platform_driver ds1742_rtc_driver = {
.driver = {
.name = "rtc-ds1742",
.owner = THIS_MODULE,
- .of_match_table = ds1742_rtc_of_match,
+ .of_match_table = of_match_ptr(ds1742_rtc_of_match),
},
};
diff --git a/drivers/rtc/rtc-efi-platform.c b/drivers/rtc/rtc-efi-platform.c
new file mode 100644
index 0000000..b40fbe3
--- /dev/null
+++ b/drivers/rtc/rtc-efi-platform.c
@@ -0,0 +1,31 @@
+/*
+ * Moved from arch/ia64/kernel/time.c
+ *
+ * Copyright (C) 1998-2003 Hewlett-Packard Co
+ * Stephane Eranian <eranian@hpl.hp.com>
+ * David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
+ * Copyright (C) 1999-2000 VA Linux Systems
+ * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/efi.h>
+#include <linux/platform_device.h>
+
+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ pr_err("unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index c4c3843..8225b89 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/stringify.h>
#include <linux/time.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
@@ -48,8 +49,8 @@ compute_wday(efi_time_t *eft)
int y;
int ndays = 0;
- if (eft->year < 1998) {
- pr_err("EFI year < 1998, invalid date\n");
+ if (eft->year < EFI_RTC_EPOCH) {
+ pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n");
return -1;
}
@@ -78,19 +79,36 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
}
-static void
+static bool
convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
{
memset(wtime, 0, sizeof(*wtime));
+
+ if (eft->second >= 60)
+ return false;
wtime->tm_sec = eft->second;
+
+ if (eft->minute >= 60)
+ return false;
wtime->tm_min = eft->minute;
+
+ if (eft->hour >= 24)
+ return false;
wtime->tm_hour = eft->hour;
+
+ if (!eft->day || eft->day > 31)
+ return false;
wtime->tm_mday = eft->day;
+
+ if (!eft->month || eft->month > 12)
+ return false;
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);
+ if (wtime->tm_wday < 0)
+ return false;
/* day in the year [1-365]*/
wtime->tm_yday = compute_yday(eft);
@@ -106,6 +124,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
default:
wtime->tm_isdst = -1;
}
+
+ return true;
}
static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
@@ -122,7 +142,8 @@ static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
if (status != EFI_SUCCESS)
return -EINVAL;
- convert_from_efi_time(&eft, &wkalrm->time);
+ if (!convert_from_efi_time(&eft, &wkalrm->time))
+ return -EIO;
return rtc_valid_tm(&wkalrm->time);
}
@@ -163,7 +184,8 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm)
return -EINVAL;
}
- convert_from_efi_time(&eft, tm);
+ if (!convert_from_efi_time(&eft, tm))
+ return -EIO;
return rtc_valid_tm(tm);
}
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
index 03b8911..aa55f08 100644
--- a/drivers/rtc/rtc-isl12022.c
+++ b/drivers/rtc/rtc-isl12022.c
@@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#define DRV_VERSION "0.1"
@@ -271,6 +273,13 @@ static int isl12022_probe(struct i2c_client *client,
return PTR_ERR_OR_ZERO(isl12022->rtc);
}
+#ifdef CONFIG_OF
+static struct of_device_id isl12022_dt_match[] = {
+ { .compatible = "isl,isl12022" },
+ { },
+};
+#endif
+
static const struct i2c_device_id isl12022_id[] = {
{ "isl12022", 0 },
{ }
@@ -280,6 +289,9 @@ MODULE_DEVICE_TABLE(i2c, isl12022_id);
static struct i2c_driver isl12022_driver = {
.driver = {
.name = "rtc-isl12022",
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(isl12022_dt_match),
+#endif
},
.probe = isl12022_probe,
.id_table = isl12022_id,
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
new file mode 100644
index 0000000..6a12bf6
--- /dev/null
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -0,0 +1,204 @@
+/*
+ * An I2C driver for the PCF85063 RTC
+ * Copyright 2014 Rose Technology
+ *
+ * Author: Søren Andersen <san@rosetechnology.dk>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * based on the other drivers in this same directory.
+ *
+ * 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/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.0.1"
+
+#define PCF85063_REG_CTRL1 0x00 /* status */
+#define PCF85063_REG_CTRL2 0x01
+
+#define PCF85063_REG_SC 0x04 /* datetime */
+#define PCF85063_REG_MN 0x05
+#define PCF85063_REG_HR 0x06
+#define PCF85063_REG_DM 0x07
+#define PCF85063_REG_DW 0x08
+#define PCF85063_REG_MO 0x09
+#define PCF85063_REG_YR 0x0A
+
+#define PCF85063_MO_C 0x80 /* century */
+
+static struct i2c_driver pcf85063_driver;
+
+struct pcf85063 {
+ struct rtc_device *rtc;
+ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
+ int voltage_low; /* indicates if a low_voltage was detected */
+};
+
+/*
+ * In the routines that deal directly with the pcf85063 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
+ unsigned char buf[13] = { PCF85063_REG_CTRL1 };
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = buf
+ },
+ {/* read status + date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 13,
+ .buf = buf
+ },
+ };
+
+ /* read registers */
+ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+ /* detect the polarity heuristically. see note above. */
+ pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
+ (tm->tm_year >= 100) : (tm->tm_year < 100);
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ int i = 0, err = 0;
+ unsigned char buf[11];
+
+ /* Control & status */
+ buf[PCF85063_REG_CTRL1] = 0;
+ buf[PCF85063_REG_CTRL2] = 5;
+
+ /* hours, minutes and seconds */
+ buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
+
+ buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
+ buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
+
+ /* Day of month, 1 - 31 */
+ buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
+
+ /* Day, 0 - 6 */
+ buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
+
+ /* month, 1 - 12 */
+ buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ /* write register's data */
+ for (i = 0; i < sizeof(buf); i++) {
+ unsigned char data[2] = { i, buf[i] };
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops pcf85063_rtc_ops = {
+ .read_time = pcf85063_rtc_read_time,
+ .set_time = pcf85063_rtc_set_time
+};
+
+static int pcf85063_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf85063 *pcf85063;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
+ GFP_KERNEL);
+ if (!pcf85063)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, pcf85063);
+
+ pcf85063->rtc = devm_rtc_device_register(&client->dev,
+ pcf85063_driver.driver.name,
+ &pcf85063_rtc_ops, THIS_MODULE);
+
+ return PTR_ERR_OR_ZERO(pcf85063->rtc);
+}
+
+static const struct i2c_device_id pcf85063_id[] = {
+ { "pcf85063", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf85063_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf85063_of_match[] = {
+ { .compatible = "nxp,pcf85063" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf85063_of_match);
+#endif
+
+static struct i2c_driver pcf85063_driver = {
+ .driver = {
+ .name = "rtc-pcf85063",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf85063_of_match),
+ },
+ .probe = pcf85063_probe,
+ .id_table = pcf85063_id,
+};
+
+module_i2c_driver(pcf85063_driver);
+
+MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
+MODULE_DESCRIPTION("PCF85063 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 63b558c..5a197d9 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -26,6 +26,8 @@
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
+#define PCF8563_BIT_AIE (1 << 1)
+#define PCF8563_BIT_AF (1 << 3)
#define PCF8563_REG_SC 0x02 /* datetime */
#define PCF8563_REG_MN 0x03
@@ -36,9 +38,6 @@
#define PCF8563_REG_YR 0x08
#define PCF8563_REG_AMN 0x09 /* alarm */
-#define PCF8563_REG_AHR 0x0A
-#define PCF8563_REG_ADM 0x0B
-#define PCF8563_REG_ADW 0x0C
#define PCF8563_REG_CLKO 0x0D /* clock out */
#define PCF8563_REG_TMRC 0x0E /* timer control */
@@ -67,37 +66,133 @@ struct pcf8563 {
*/
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
int voltage_low; /* incicates if a low_voltage was detected */
+
+ struct i2c_client *client;
};
-/*
- * In the routines that deal directly with the pcf8563 hardware, we use
- * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
- */
-static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
+ unsigned char length, unsigned char *buf)
{
- struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
- unsigned char buf[13] = { PCF8563_REG_ST1 };
-
struct i2c_msg msgs[] = {
{/* setup read ptr */
.addr = client->addr,
.len = 1,
- .buf = buf
+ .buf = &reg,
},
- {/* read status + date */
+ {
.addr = client->addr,
.flags = I2C_M_RD,
- .len = 13,
+ .len = length,
.buf = buf
},
};
- /* read registers */
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __func__);
return -EIO;
}
+ return 0;
+}
+
+static int pcf8563_write_block_data(struct i2c_client *client,
+ unsigned char reg, unsigned char length,
+ unsigned char *buf)
+{
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+ unsigned char data[2] = { reg + i, buf[i] };
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
+{
+ unsigned char buf[2];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, buf + 1);
+ if (err < 0)
+ return err;
+
+ if (on)
+ buf[1] |= PCF8563_BIT_AIE;
+ else
+ buf[1] &= ~PCF8563_BIT_AIE;
+
+ buf[1] &= ~PCF8563_BIT_AF;
+ buf[0] = PCF8563_REG_ST2;
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, buf + 1);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
+ unsigned char *pen)
+{
+ unsigned char buf;
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err)
+ return err;
+
+ if (en)
+ *en = !!(buf & PCF8563_BIT_AIE);
+ if (pen)
+ *pen = !!(buf & PCF8563_BIT_AF);
+
+ return 0;
+}
+
+static irqreturn_t pcf8563_irq(int irq, void *dev_id)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
+ int err;
+ char pending;
+
+ err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
+ if (err < 0)
+ return err;
+
+ if (pending) {
+ rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
+ pcf8563_set_alarm_mode(pcf8563->client, 1);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/*
+ * In the routines that deal directly with the pcf8563 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
+ unsigned char buf[9];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf);
+ if (err)
+ return err;
+
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
pcf8563->voltage_low = 1;
dev_info(&client->dev,
@@ -144,7 +239,7 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
- int i, err;
+ int err;
unsigned char buf[9];
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
@@ -170,19 +265,10 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
- /* write register's data */
- for (i = 0; i < 7; i++) {
- unsigned char data[2] = { PCF8563_REG_SC + i,
- buf[PCF8563_REG_SC + i] };
-
- err = i2c_master_send(client, data, sizeof(data));
- if (err != sizeof(data)) {
- dev_err(&client->dev,
- "%s: err=%d addr=%02x, data=%02x\n",
- __func__, err, data[0], data[1]);
- return -EIO;
- }
- }
+ err = pcf8563_write_block_data(client, PCF8563_REG_SC,
+ 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
+ if (err)
+ return err;
return 0;
}
@@ -235,16 +321,83 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
+static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ dev_dbg(&client->dev,
+ "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+
+ tm->time.tm_min = bcd2bin(buf[0] & 0x7F);
+ tm->time.tm_hour = bcd2bin(buf[1] & 0x7F);
+ tm->time.tm_mday = bcd2bin(buf[2] & 0x1F);
+ tm->time.tm_wday = bcd2bin(buf[3] & 0x7);
+ tm->time.tm_mon = -1;
+ tm->time.tm_year = -1;
+ tm->time.tm_yday = -1;
+ tm->time.tm_isdst = -1;
+
+ err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending);
+ if (err < 0)
+ return err;
+
+ dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
+ " enabled=%d, pending=%d\n", __func__, tm->time.tm_min,
+ tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday,
+ tm->enabled, tm->pending);
+
+ return 0;
+}
+
+static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+
+ dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
+ "enabled=%d pending=%d\n", __func__,
+ tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
+ tm->time.tm_mday, tm->enabled, tm->pending);
+
+ buf[0] = bin2bcd(tm->time.tm_min);
+ buf[1] = bin2bcd(tm->time.tm_hour);
+ buf[2] = bin2bcd(tm->time.tm_mday);
+ buf[3] = tm->time.tm_wday & 0x07;
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ return pcf8563_set_alarm_mode(client, 1);
+}
+
+static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
+{
+ return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
+}
+
static const struct rtc_class_ops pcf8563_rtc_ops = {
.ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
+ .read_alarm = pcf8563_rtc_read_alarm,
+ .set_alarm = pcf8563_rtc_set_alarm,
+ .alarm_irq_enable = pcf8563_irq_enable,
};
static int pcf8563_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf8563 *pcf8563;
+ int err;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -259,12 +412,30 @@ static int pcf8563_probe(struct i2c_client *client,
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
i2c_set_clientdata(client, pcf8563);
+ pcf8563->client = client;
+ device_set_wakeup_capable(&client->dev, 1);
pcf8563->rtc = devm_rtc_device_register(&client->dev,
pcf8563_driver.driver.name,
&pcf8563_rtc_ops, THIS_MODULE);
- return PTR_ERR_OR_ZERO(pcf8563->rtc);
+ if (IS_ERR(pcf8563->rtc))
+ return PTR_ERR(pcf8563->rtc);
+
+ if (client->irq > 0) {
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pcf8563_irq,
+ IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
+ pcf8563->rtc->name, client);
+ if (err) {
+ dev_err(&client->dev, "unable to request IRQ %d\n",
+ client->irq);
+ return err;
+ }
+
+ }
+
+ return 0;
}
static const struct i2c_device_id pcf8563_id[] = {
diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c
index 7af0020..2583349 100644
--- a/drivers/rtc/rtc-tps65910.c
+++ b/drivers/rtc/rtc-tps65910.c
@@ -258,6 +258,8 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ platform_set_drvdata(pdev, tps_rtc);
+
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
@@ -283,8 +285,6 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
return ret;
}
- platform_set_drvdata(pdev, tps_rtc);
-
return 0;
}