summaryrefslogtreecommitdiff
path: root/packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch
diff options
context:
space:
mode:
authorOyvind Repvik <nail@nslu2-linux.org>2006-01-22 12:00:56 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2006-01-22 12:00:56 +0000
commitb5a2a12f50424ad15585513e40da17c5b3e613c5 (patch)
tree96e867bdf6ed2165c1a5db445d147eff68bc3a7a /packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch
parentb3959eacd7022f8fa819a488ae624ae479d7d84b (diff)
parentf342d6c49ef16ade9e67c868d0c962cd455ca575 (diff)
merge of 897657fc95b12ea51abbdcb9bb8828bfa5a1caca
and bf04667bfe2d9cedd68fc1f220beacb16eccdbf9
Diffstat (limited to 'packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch')
-rw-r--r--packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch664
1 files changed, 538 insertions, 126 deletions
diff --git a/packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch b/packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch
index e10998f8d2..3b36813564 100644
--- a/packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch
+++ b/packages/linux/ixp4xx-kernel/2.6.15/40-rtc-class.patch
@@ -1,6 +1,6 @@
---- linux-nslu2.orig/include/linux/rtc.h 2006-01-08 18:54:58.000000000 +0100
-+++ linux-nslu2/include/linux/rtc.h 2006-01-08 18:55:19.000000000 +0100
-@@ -91,8 +91,81 @@ struct rtc_pll_info {
+--- linux-nslu2.orig/include/linux/rtc.h 2006-01-20 12:04:18.000000000 +0100
++++ linux-nslu2/include/linux/rtc.h 2006-01-20 20:23:24.000000000 +0100
+@@ -91,8 +91,91 @@ struct rtc_pll_info {
#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */
#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
@@ -64,7 +64,6 @@
+extern void rtc_device_unregister(struct rtc_device *rdev);
+extern int rtc_interface_register(struct class_interface *intf);
+
-+
+extern int rtc_month_days(unsigned int month, unsigned int year);
+extern int rtc_valid_tm(struct rtc_time *tm);
+extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time);
@@ -79,11 +78,22 @@
+extern void rtc_update_irq(struct class_device *class_dev,
+ unsigned long num, unsigned long events);
+
++extern struct class_device *rtc_open(char *name);
++extern void rtc_close(struct class_device *class_dev);
++extern int rtc_irq_register(struct class_device *class_dev,
++ struct rtc_task *task);
++extern void rtc_irq_unregister(struct class_device *class_dev,
++ struct rtc_task *task);
++extern int rtc_irq_set_state(struct class_device *class_dev,
++ struct rtc_task *task, int enabled);
++extern int rtc_irq_set_freq(struct class_device *class_dev,
++ struct rtc_task *task, int freq);
++
typedef struct rtc_task {
void (*func)(void *private_data);
void *private_data;
---- linux-nslu2.orig/drivers/Kconfig 2006-01-08 18:54:58.000000000 +0100
-+++ linux-nslu2/drivers/Kconfig 2006-01-08 18:55:19.000000000 +0100
+--- linux-nslu2.orig/drivers/Kconfig 2006-01-20 12:04:18.000000000 +0100
++++ linux-nslu2/drivers/Kconfig 2006-01-20 20:23:24.000000000 +0100
@@ -66,4 +66,6 @@ source "drivers/infiniband/Kconfig"
source "drivers/sn/Kconfig"
@@ -91,8 +101,8 @@
+source "drivers/rtc/Kconfig"
+
endmenu
---- linux-nslu2.orig/drivers/Makefile 2006-01-08 18:54:58.000000000 +0100
-+++ linux-nslu2/drivers/Makefile 2006-01-08 18:55:19.000000000 +0100
+--- linux-nslu2.orig/drivers/Makefile 2006-01-20 12:04:18.000000000 +0100
++++ linux-nslu2/drivers/Makefile 2006-01-20 20:23:24.000000000 +0100
@@ -54,6 +54,7 @@ obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_GAMEPORT) += input/gameport/
obj-$(CONFIG_INPUT) += input/
@@ -102,7 +112,7 @@
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_HWMON) += hwmon/
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/class.c 2006-01-08 18:55:19.000000000 +0100
++++ linux-nslu2/drivers/rtc/class.c 2006-01-20 20:23:24.000000000 +0100
@@ -0,0 +1,143 @@
+/*
+ * RTC subsystem, base class
@@ -248,8 +258,8 @@
+MODULE_DESCRIPTION("RTC class support");
+MODULE_LICENSE("GPL");
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/Kconfig 2006-01-09 04:17:02.000000000 +0100
-@@ -0,0 +1,93 @@
++++ linux-nslu2/drivers/rtc/Kconfig 2006-01-20 21:26:12.000000000 +0100
+@@ -0,0 +1,121 @@
+#
+# RTC class/drivers configuration
+#
@@ -268,6 +278,23 @@
+ This driver can also be built as a module. If so, the module
+ will be called rtc-class.
+
++config RTC_HCTOSYS
++ bool "Set system time from RTC on startup"
++ depends on RTC_CLASS = y
++ default y
++ help
++ If you say yes here, the system time will be set using
++ the value read from the specified RTC device. This is useful
++ in order to avoid unnecessary fschk runs.
++
++config RTC_HCTOSYS_DEVICE
++ string "The RTC to read the time from"
++ depends on RTC_HCTOSYS = y
++ default "rtc0"
++ help
++ The RTC device that will be used as the source for
++ the system time, usually rtc0.
++
+comment "RTC interfaces"
+ depends on RTC_CLASS
+
@@ -327,6 +354,17 @@
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1672.
+
++config RTC_DRV_PCF8563
++ tristate "Philips PCF8563/Epson RTC8564"
++ depends on RTC_CLASS && I2C
++ help
++ If you say yes here you get support for the
++ Philips PCF8563 RTC chip. The Epson RTC8564
++ should work as well.
++
++ This driver can also be built as a module. If so, the module
++ will be called rtc-pcf8563.
++
+config RTC_DRV_TEST
+ tristate "Test driver/device"
+ depends on RTC_CLASS
@@ -344,13 +382,14 @@
+
+endmenu
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/Makefile 2006-01-09 04:17:02.000000000 +0100
-@@ -0,0 +1,15 @@
++++ linux-nslu2/drivers/rtc/Makefile 2006-01-20 21:26:12.000000000 +0100
+@@ -0,0 +1,17 @@
+#
+# Makefile for RTC class/drivers.
+#
+
+obj-y += utils.o
++obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
+obj-$(CONFIG_RTC_CLASS) += rtc-core.o
+rtc-core-y := class.o interface.o
+obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
@@ -360,9 +399,10 @@
+obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
+obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
++obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
+
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/interface.c 2006-01-09 03:39:33.000000000 +0100
++++ linux-nslu2/drivers/rtc/interface.c 2006-01-20 21:26:14.000000000 +0100
@@ -0,0 +1,232 @@
+/*
+ * RTC subsystem, interface functions
@@ -597,7 +637,7 @@
+
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/utils.c 2006-01-08 18:55:19.000000000 +0100
++++ linux-nslu2/drivers/rtc/utils.c 2006-01-20 20:23:24.000000000 +0100
@@ -0,0 +1,97 @@
+/*
+ * RTC subsystem, utility functions
@@ -696,8 +736,72 @@
+ return 0;
+}
+EXPORT_SYMBOL(rtc_tm_to_time);
---- linux-nslu2.orig/arch/arm/Kconfig 2006-01-09 03:36:04.000000000 +0100
-+++ linux-nslu2/arch/arm/Kconfig 2006-01-09 03:39:50.000000000 +0100
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/hctosys.c 2006-01-20 20:23:24.000000000 +0100
+@@ -0,0 +1,61 @@
++/*
++ * RTC subsystem, initialize system time on startup
++ *
++ * Copyright (C) 2005 Tower Technologies
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ *
++ * 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.
++*/
++
++#include <linux/rtc.h>
++
++/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
++ * whether it stores the most close value or the value with partial
++ * seconds truncated. However, it is important that we use it to store
++ * the truncated value. This is because otherwise it is necessary,
++ * in an rtc sync function, to read both xtime.tv_sec and
++ * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
++ * of >32bits is not possible. So storing the most close value would
++ * slow down the sync API. So here we have the truncated value and
++ * the best guess is to add 0.5s.
++ */
++
++static int __init rtc_hctosys(void)
++{
++ int err;
++ struct class_device *class_dev = rtc_open(CONFIG_RTC_HCTOSYS_DEVICE);
++
++ if (class_dev) {
++ struct rtc_time tm;
++
++ if ((err = rtc_read_time(class_dev, &tm)) == 0) {
++ struct timespec tv;
++
++ tv.tv_nsec = NSEC_PER_SEC >> 1;
++
++ rtc_tm_to_time(&tm, &tv.tv_sec);
++
++ do_settimeofday(&tv);
++
++ dev_info(class_dev->dev,
++ "setting the system clock to "
++ "%d-%02d-%02d %02d:%02d:%02d (%d)\n",
++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++ tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_sec);
++ }
++ else
++ dev_err(class_dev->dev,
++ "unable to set the system clock\n");
++
++ rtc_close(class_dev);
++ }
++ else
++ printk("%s: unable to open rtc device (%s)\n",
++ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
++
++ return 0;
++}
++
++late_initcall(rtc_hctosys);
+--- linux-nslu2.orig/arch/arm/Kconfig 2006-01-20 21:25:46.000000000 +0100
++++ linux-nslu2/arch/arm/Kconfig 2006-01-20 21:26:12.000000000 +0100
@@ -748,6 +748,8 @@ source "drivers/usb/Kconfig"
source "drivers/mmc/Kconfig"
@@ -707,8 +811,8 @@
endmenu
source "fs/Kconfig"
---- linux-nslu2.orig/arch/arm/common/rtctime.c 2006-01-09 03:36:04.000000000 +0100
-+++ linux-nslu2/arch/arm/common/rtctime.c 2006-01-09 03:39:50.000000000 +0100
+--- linux-nslu2.orig/arch/arm/common/rtctime.c 2006-01-20 21:25:46.000000000 +0100
++++ linux-nslu2/arch/arm/common/rtctime.c 2006-01-20 21:26:12.000000000 +0100
@@ -40,89 +40,6 @@ static struct rtc_ops *rtc_ops;
#define rtc_epoch 1900UL
@@ -879,6 +983,24 @@
if (ret)
break;
ret = copy_to_user(uarg, &alrm, sizeof(alrm));
+@@ -351,7 +268,7 @@ static int rtc_ioctl(struct inode *inode
+ return ret;
+ }
+
+-static int rtc_open(struct inode *inode, struct file *file)
++static int rtc_arm_open(struct inode *inode, struct file *file)
+ {
+ int ret;
+
+@@ -406,7 +323,7 @@ static struct file_operations rtc_fops =
+ .read = rtc_read,
+ .poll = rtc_poll,
+ .ioctl = rtc_ioctl,
+- .open = rtc_open,
++ .open = rtc_arm_open,
+ .release = rtc_release,
+ .fasync = rtc_fasync,
+ };
@@ -425,7 +342,7 @@ static int rtc_read_proc(char *page, cha
struct rtc_time tm;
char *p = page;
@@ -897,8 +1019,8 @@
p += sprintf(p, "alrm_time\t: ");
if ((unsigned int)alrm.time.tm_hour <= 24)
p += sprintf(p, "%02d:", alrm.time.tm_hour);
---- linux-nslu2.orig/include/asm-arm/rtc.h 2006-01-09 03:36:04.000000000 +0100
-+++ linux-nslu2/include/asm-arm/rtc.h 2006-01-09 03:39:50.000000000 +0100
+--- linux-nslu2.orig/include/asm-arm/rtc.h 2006-01-20 21:25:46.000000000 +0100
++++ linux-nslu2/include/asm-arm/rtc.h 2006-01-20 21:26:12.000000000 +0100
@@ -25,9 +25,6 @@ struct rtc_ops {
int (*proc)(char *buf);
};
@@ -910,7 +1032,7 @@
void rtc_update(unsigned long, unsigned long);
int register_rtc(struct rtc_ops *);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-sysfs.c 2006-01-09 03:39:50.000000000 +0100
++++ linux-nslu2/drivers/rtc/rtc-sysfs.c 2006-01-20 21:26:12.000000000 +0100
@@ -0,0 +1,128 @@
+/*
+ * RTC subsystem, sysfs interface
@@ -1041,7 +1163,7 @@
+MODULE_DESCRIPTION("RTC class sysfs interface");
+MODULE_LICENSE("GPL");
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-proc.c 2006-01-09 03:39:50.000000000 +0100
++++ linux-nslu2/drivers/rtc/rtc-proc.c 2006-01-20 21:26:12.000000000 +0100
@@ -0,0 +1,158 @@
+/*
+ * RTC subsystem, proc interface
@@ -1202,7 +1324,7 @@
+MODULE_DESCRIPTION("RTC class proc interface");
+MODULE_LICENSE("GPL");
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-dev.c 2006-01-09 04:00:16.000000000 +0100
++++ linux-nslu2/drivers/rtc/rtc-dev.c 2006-01-20 21:26:12.000000000 +0100
@@ -0,0 +1,372 @@
+/*
+ * RTC subsystem, dev interface
@@ -1577,8 +1699,8 @@
+MODULE_DESCRIPTION("RTC class dev interface");
+MODULE_LICENSE("GPL");
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-x1205.c 2006-01-09 04:17:02.000000000 +0100
-@@ -0,0 +1,725 @@
++++ linux-nslu2/drivers/rtc/rtc-x1205.c 2006-01-20 21:26:12.000000000 +0100
+@@ -0,0 +1,672 @@
+/*
+ * An i2c driver for the Xicor/Intersil X1205 RTC
+ * Copyright 2004 Karen Spearel
@@ -1620,8 +1742,6 @@
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
-+I2C_CLIENT_MODULE_PARM(hctosys,
-+ "Set the system time from the hardware clock upon initialization");
+
+/* offsets into CCR area */
+
@@ -1918,50 +2038,6 @@
+ return 0;
+}
+
-+static int x1205_hctosys(struct i2c_client *client)
-+{
-+ int err;
-+
-+ struct rtc_time tm;
-+ struct timespec tv;
-+ unsigned char sr;
-+
-+ if ((err = x1205_get_status(client, &sr)) < 0)
-+ return err;
-+
-+ /* Don't set if we had a power failure */
-+ if (sr & X1205_SR_RTCF)
-+ return -EINVAL;
-+
-+ if ((err = x1205_get_datetime(client, &tm, X1205_CCR_BASE)) < 0)
-+ return err;
-+
-+ /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
-+ * whether it stores the most close value or the value with partial
-+ * seconds truncated. However, it is important that we use it to store
-+ * the truncated value. This is because otherwise it is necessary,
-+ * in an rtc sync function, to read both xtime.tv_sec and
-+ * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
-+ * of >32bits is not possible. So storing the most close value would
-+ * slow down the sync API. So here we have the truncated value and
-+ * the best guess is to add 0.5s.
-+ */
-+
-+ tv.tv_nsec = NSEC_PER_SEC >> 1;
-+
-+ rtc_tm_to_time(&tm, &tv.tv_sec);
-+
-+ do_settimeofday(&tv);
-+
-+ dev_info(&client->dev,
-+ "setting the system clock to %02d-%02d-%d %02d:%02d:%02d\n",
-+ tm.tm_year + 1900, tm.tm_mon + 1,
-+ tm.tm_mday, tm.tm_hour, tm.tm_min,
-+ tm.tm_sec);
-+
-+ return 0;
-+}
-+
+struct x1205_limit
+{
+ unsigned char reg;
@@ -2245,13 +2321,6 @@
+ else
+ dev_err(&client->dev, "couldn't read status\n");
+
-+ /* If requested, set the system time */
-+ if (hctosys) {
-+ if ((err = x1205_hctosys(client)) < 0)
-+ dev_err(&client->dev,
-+ "unable to set the system clock\n");
-+ }
-+
+ device_create_file(&client->dev, &dev_attr_atrim);
+ device_create_file(&client->dev, &dev_attr_dtrim);
+
@@ -2305,7 +2374,7 @@
+module_init(x1205_init);
+module_exit(x1205_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-test.c 2006-01-09 04:17:02.000000000 +0100
++++ linux-nslu2/drivers/rtc/rtc-test.c 2006-01-20 21:26:12.000000000 +0100
@@ -0,0 +1,206 @@
+/*
+ * An RTC test device/driver
@@ -2514,8 +2583,8 @@
+module_init(test_init);
+module_exit(test_exit);
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-nslu2/drivers/rtc/rtc-ds1672.c 2006-01-12 00:31:05.000000000 +0100
-@@ -0,0 +1,278 @@
++++ linux-nslu2/drivers/rtc/rtc-ds1672.c 2006-01-20 21:26:12.000000000 +0100
+@@ -0,0 +1,234 @@
+/*
+ * An rtc/i2c driver for the Dallas DS1672
+ * Copyright 2005 Alessandro Zummo
@@ -2537,8 +2606,6 @@
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
-+I2C_CLIENT_MODULE_PARM(hctosys,
-+ "Set the system time from the hardware clock upon initialization");
+
+/* Registers */
+
@@ -2626,41 +2693,6 @@
+ return ds1672_set_mmss(client, secs);
+}
+
-+static int ds1672_hctosys(struct i2c_client *client)
-+{
-+ int err;
-+ struct rtc_time tm;
-+ struct timespec tv;
-+
-+ if ((err = ds1672_get_datetime(client, &tm)) != 0)
-+ return err;
-+
-+ /* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
-+ * whether it stores the most close value or the value with partial
-+ * seconds truncated. However, it is important that we use it to store
-+ * the truncated value. This is because otherwise it is necessary,
-+ * in an rtc sync function, to read both xtime.tv_sec and
-+ * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
-+ * of >32bits is not possible. So storing the most close value would
-+ * slow down the sync API. So here we have the truncated value and
-+ * the best guess is to add 0.5s.
-+ */
-+
-+ tv.tv_nsec = NSEC_PER_SEC >> 1;
-+
-+ rtc_tm_to_time(&tm, &tv.tv_sec);
-+
-+ do_settimeofday(&tv);
-+
-+ dev_info(&client->dev,
-+ "setting the system clock to %d-%02d-%02d %02d:%02d:%02d\n",
-+ tm.tm_year + 1900, tm.tm_mon + 1,
-+ tm.tm_mday, tm.tm_hour, tm.tm_min,
-+ tm.tm_sec);
-+
-+ return 0;
-+}
-+
+static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return ds1672_get_datetime(to_i2c_client(dev), tm);
@@ -2758,13 +2790,6 @@
+
+ i2c_set_clientdata(client, rtc);
+
-+ /* If requested, set the system time */
-+ if (hctosys) {
-+ if ((err = ds1672_hctosys(client)) < 0)
-+ dev_err(&client->dev,
-+ "unable to set the system clock\n");
-+ }
-+
+ return 0;
+
+exit_detach:
@@ -2794,3 +2819,390 @@
+
+module_init(ds1672_init);
+module_exit(ds1672_exit);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-pcf8563.c 2006-01-20 21:27:41.000000000 +0100
+@@ -0,0 +1,384 @@
++/*
++ * An I2C driver for the Philips PCF8563 RTC
++ * Copyright 2005-06 Tower Technologies
++ *
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ * Maintainers: http://www.nslu2-linux.org/
++ *
++ * based on the other drivers in this same directory.
++ *
++ * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/bcd.h>
++#include <linux/rtc.h>
++
++#define DRV_VERSION "0.4.0"
++
++/* Addresses to scan */
++static unsigned short normal_i2c[] = { 0x51, I2C_CLIENT_END };
++
++/* Module parameters */
++I2C_CLIENT_INSMOD;
++
++#define PCF8563_REG_ST1 0x00 /* status */
++#define PCF8563_REG_ST2 0x01
++
++#define PCF8563_REG_SC 0x02 /* datetime */
++#define PCF8563_REG_MN 0x03
++#define PCF8563_REG_HR 0x04
++#define PCF8563_REG_DM 0x05
++#define PCF8563_REG_DW 0x06
++#define PCF8563_REG_MO 0x07
++#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 */
++#define PCF8563_REG_TMR 0x0F /* timer */
++
++#define PCF8563_SC_LV 0x80 /* low voltage */
++#define PCF8563_MO_C 0x80 /* century */
++
++/* Prototypes */
++static int pcf8563_attach(struct i2c_adapter *adapter);
++static int pcf8563_detach(struct i2c_client *client);
++static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind);
++
++static struct i2c_driver pcf8563_driver = {
++ .owner = THIS_MODULE,
++ .name = "pcf8563",
++ .flags = I2C_DF_NOTIFY,
++ .attach_adapter = &pcf8563_attach,
++ .detach_client = &pcf8563_detach,
++};
++
++/*
++ * 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)
++{
++ unsigned char buf[13];
++ unsigned char addr = PCF8563_REG_ST1;
++
++ struct i2c_msg msgs[] = {
++ { client->addr, 0, 1, &addr }, /* setup read ptr */
++ { client->addr, I2C_M_RD, 13, buf }, /* read status + date */
++ };
++
++ /* read registers */
++ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
++ dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
++ return -EIO;
++ }
++
++ if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
++ dev_info(&client->dev,
++ "low voltage detected, date/time is not reliable.\n");
++
++ dev_dbg(&client->dev,
++ "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
++ "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
++ __FUNCTION__,
++ buf[0], buf[1], buf[2], buf[3],
++ buf[4], buf[5], buf[6], buf[7],
++ buf[8]);
++
++
++ tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
++ tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
++ tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
++ tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
++ tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
++ tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
++ tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
++ + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 100 : 0);
++
++ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ /* 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 pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
++{
++ int i, err;
++ unsigned char buf[9];
++
++ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ /* hours, minutes and seconds */
++ buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
++ buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
++ buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
++
++ buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
++
++ /* month, 1 - 12 */
++ buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
++
++ /* year and century */
++ buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
++ if (tm->tm_year / 100)
++ buf[PCF8563_REG_MO] |= PCF8563_MO_C;
++
++ 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",
++ __FUNCTION__, err, data[0], data[1]);
++ return -EIO;
++ }
++ };
++
++ return 0;
++}
++
++struct pcf8563_limit
++{
++ unsigned char reg;
++ unsigned char mask;
++ unsigned char min;
++ unsigned char max;
++};
++
++static int pcf8563_validate_client(struct i2c_client *client)
++{
++ int i, xfer;
++
++ static const struct pcf8563_limit probe_limits_pattern[] = {
++ /* register, mask, min, max */
++ { PCF8563_REG_SC, 0x7F, 0, 59 },
++ { PCF8563_REG_MN, 0x7F, 0, 59 },
++ { PCF8563_REG_HR, 0x3F, 0, 23 },
++ { PCF8563_REG_DM, 0x3F, 0, 31 },
++ { PCF8563_REG_MO, 0x1F, 0, 12 },
++ { PCF8563_REG_YR, 0xFF, 0, 99 },
++ };
++
++ /* check limits (only registers with bcd values) */
++ for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
++ unsigned char addr, buf, value;
++
++ addr = probe_limits_pattern[i].reg;
++
++ struct i2c_msg msgs[2] = {
++ { client->addr, 0, 2, &addr },
++ { client->addr, I2C_M_RD, 1, &buf },
++ };
++
++ xfer = i2c_transfer(client->adapter, msgs, 2);
++
++ if (xfer != 2) {
++ dev_err(&client->adapter->dev,
++ "%s: could not read register %x\n",
++ __FUNCTION__, probe_limits_pattern[i].reg);
++
++ return -EIO;
++ }
++
++ value = BCD2BIN(buf & probe_limits_pattern[i].mask);
++
++ if (value > probe_limits_pattern[i].max ||
++ value < probe_limits_pattern[i].min) {
++ dev_dbg(&client->adapter->dev,
++ "%s: register=%x, lim pattern=%d, value=%d\n",
++ __FUNCTION__, probe_limits_pattern[i].reg, i, value);
++
++ return -ENODEV;
++ }
++ }
++
++ return 0;
++}
++
++static int pcf8563_rtc_read_time(struct device *dev,
++ struct rtc_time *tm)
++{
++ return pcf8563_get_datetime(to_i2c_client(dev), tm);
++}
++
++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_set_mmss(struct device *dev, unsigned long secs)
++{
++ int err;
++
++ struct rtc_time new_tm, old_tm;
++
++ if ((err = pcf8563_rtc_read_time(dev, &old_tm) == 0))
++ return err;
++
++ /* FIXME xtime.tv_nsec = old_tm.tm_sec * 10000000; */
++ new_tm.tm_sec = secs % 60;
++ secs /= 60;
++ new_tm.tm_min = secs % 60;
++ secs /= 60;
++ new_tm.tm_hour = secs % 24;
++
++ /*
++ * avoid writing when we're going to change the day
++ * of the month. We will retry in the next minute.
++ * This basically means that if the RTC must not drift
++ * by more than 1 minute in 11 minutes.
++ */
++ if ((old_tm.tm_hour == 23 && old_tm.tm_min == 59) ||
++ (new_tm.tm_hour == 23 && new_tm.tm_min == 59))
++ return 1;
++
++ return pcf8563_rtc_set_time(dev, &new_tm);
++}
++
++static int pcf8563_rtc_proc(struct device *dev, struct seq_file *seq)
++{
++ seq_printf(seq, "24hr\t\t: yes\n");
++ return 0;
++}
++
++static struct rtc_class_ops pcf8563_rtc_ops = {
++ .proc = pcf8563_rtc_proc,
++ .read_time = pcf8563_rtc_read_time,
++ .set_time = pcf8563_rtc_set_time,
++ .set_mmss = pcf8563_rtc_set_mmss,
++};
++
++static int pcf8563_attach(struct i2c_adapter *adapter)
++{
++ return i2c_probe(adapter, &addr_data, pcf8563_probe);
++}
++
++static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *client;
++ struct rtc_device *rtc;
++
++ int err = 0;
++
++ dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
++ err = -ENODEV;
++ goto exit;
++ }
++
++ if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto exit;
++ }
++
++ client->addr = address;
++ client->driver = &pcf8563_driver;
++ client->adapter = adapter;
++
++ strlcpy(client->name, pcf8563_driver.name, I2C_NAME_SIZE);
++
++ /* Verify the chip is really an PCF8563 */
++ if (kind < 0) {
++ if (pcf8563_validate_client(client) < 0) {
++ err = -ENODEV;
++ goto exit_kfree;
++ }
++ }
++
++ /* Inform the i2c layer */
++ if ((err = i2c_attach_client(client)))
++ goto exit_kfree;
++
++ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
++
++ rtc = rtc_device_register(pcf8563_driver.name, &client->dev,
++ &pcf8563_rtc_ops, THIS_MODULE);
++
++ if (IS_ERR(rtc)) {
++ err = PTR_ERR(rtc);
++ dev_err(&client->dev,
++ "unable to register the class device\n");
++ goto exit_detach;
++ }
++
++ i2c_set_clientdata(client, rtc);
++
++ return 0;
++
++exit_detach:
++ i2c_detach_client(client);
++
++exit_kfree:
++ kfree(client);
++
++exit:
++ return err;
++}
++
++static int pcf8563_detach(struct i2c_client *client)
++{
++ int err;
++ struct rtc_device *rtc = i2c_get_clientdata(client);
++
++ dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++
++ if (rtc)
++ rtc_device_unregister(rtc);
++
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ kfree(client);
++
++ return 0;
++}
++
++static int __init pcf8563_init(void)
++{
++ return i2c_add_driver(&pcf8563_driver);
++}
++
++static void __exit pcf8563_exit(void)
++{
++ i2c_del_driver(&pcf8563_driver);
++}
++
++MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
++MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(DRV_VERSION);
++
++module_init(pcf8563_init);
++module_exit(pcf8563_exit);
++