linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/11] RTC subsystem
@ 2006-02-19 23:22 Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 01/11] RTC subsystem, class Alessandro Zummo
                   ` (11 more replies)
  0 siblings, 12 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel; +Cc: akpm, dtor_core


 Hello,

  this is me again with this shining new
 RTC subsystem :)

 quick changelog:

 - attribute groups
 - mutexes
 - fixed another bug in pcf8563 detection 

 Many thanks to all the people who contributed
 with comments both privately and on this ml.

--

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 01/11] RTC subsystem, class
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-22 22:15   ` Andrew Morton
  2006-02-19 23:22 ` [PATCH 02/11] RTC subsystem, ARM cleanup Alessandro Zummo
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-class.patch --]
[-- Type: text/plain, Size: 21529 bytes --]

This patch adds the basic RTC subsytem infrastructure
to the kernel.

rtc/class.c - registration facilities for RTC drivers
rtc/interface.c - kernel/rtc interface functions 
rtc/utils.c - misc rtc-related utility functions
rtc/hctosys.c - snippet of code that copies hw clock to sw clock
		at bootup, if configured to do so.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--
 drivers/Kconfig         |    2 
 drivers/Makefile        |    1 
 drivers/rtc/Kconfig     |   42 +++++++
 drivers/rtc/Makefile    |    9 +
 drivers/rtc/class.c     |  143 +++++++++++++++++++++++++
 drivers/rtc/hctosys.c   |   67 +++++++++++
 drivers/rtc/interface.c |  274 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/rtc/utils.c     |   97 ++++++++++++++++
 include/linux/rtc.h     |   89 +++++++++++++++
 9 files changed, 724 insertions(+)

--- linux-rtc.orig/include/linux/rtc.h	2006-02-15 03:24:36.000000000 +0100
+++ linux-rtc/include/linux/rtc.h	2006-02-15 04:13:58.000000000 +0100
@@ -93,8 +93,97 @@ 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 */
 
+/* interrupt flags */
+#define RTC_IRQF 0x80 /* any of the following is active */
+#define RTC_PF 0x40
+#define RTC_AF 0x20
+#define RTC_UF 0x10
+
 #ifdef __KERNEL__
 
+#include <linux/device.h>
+#include <linux/seq_file.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+
+struct rtc_class_ops {
+	int (*open)(struct device *);
+	void (*release)(struct device *);
+	int (*ioctl)(struct device *, unsigned int, unsigned long);
+	int (*read_time)(struct device *, struct rtc_time *);
+	int (*set_time)(struct device *, struct rtc_time *);
+	int (*read_alarm)(struct device *, struct rtc_wkalrm *);
+	int (*set_alarm)(struct device *, struct rtc_wkalrm *);
+	int (*proc)(struct device *, struct seq_file *);
+	int (*set_mmss)(struct device *, unsigned long secs);
+	int (*irq_set_state)(struct device *, int enabled);
+	int (*irq_set_freq)(struct device *, int freq);
+	int (*read_callback)(struct device *, int data);
+};
+
+#define RTC_DEVICE_NAME_SIZE 20
+struct rtc_task;
+
+struct rtc_device
+{
+	struct class_device class_dev;
+	struct module *owner;
+
+	int id;
+	char name[RTC_DEVICE_NAME_SIZE];
+
+	struct rtc_class_ops *ops;
+	struct mutex ops_lock;
+
+	struct cdev char_dev;
+	struct mutex char_lock;
+
+	unsigned long irq_data;
+	spinlock_t irq_lock;
+	wait_queue_head_t irq_queue;
+	struct fasync_struct *async_queue;
+
+	struct rtc_task *irq_task;
+	spinlock_t irq_task_lock;
+	int irq_freq;
+};
+#define to_rtc_device(d) container_of(d, struct rtc_device, class_dev)
+
+extern struct rtc_device *rtc_device_register(const char *name,
+					struct device *dev,
+					struct rtc_class_ops *ops,
+					struct module *owner);
+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);
+extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm);
+
+extern int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm);
+extern int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm);
+extern int rtc_set_mmss(struct class_device *class_dev, unsigned long secs);
+extern int rtc_read_alarm(struct class_device *class_dev,
+			struct rtc_wkalrm *alrm);
+extern int rtc_set_alarm(struct class_device *class_dev,
+				struct rtc_wkalrm *alrm);
+extern void rtc_update_irq(struct class_device *class_dev,
+			unsigned long num, unsigned long events);
+
+extern struct class_device *rtc_class_open(char *name);
+extern void rtc_class_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-rtc.orig/drivers/Kconfig	2006-02-15 03:24:36.000000000 +0100
+++ linux-rtc/drivers/Kconfig	2006-02-15 03:24:47.000000000 +0100
@@ -70,4 +70,6 @@ source "drivers/sn/Kconfig"
 
 source "drivers/edac/Kconfig"
 
+source "drivers/rtc/Kconfig"
+
 endmenu
--- linux-rtc.orig/drivers/Makefile	2006-02-15 03:24:36.000000000 +0100
+++ linux-rtc/drivers/Makefile	2006-02-15 03:24:47.000000000 +0100
@@ -56,6 +56,7 @@ obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
 obj-$(CONFIG_GAMEPORT)		+= input/gameport/
 obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
+obj-y				+= rtc/
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_HWMON)		+= hwmon/
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/class.c	2006-02-15 03:25:54.000000000 +0100
@@ -0,0 +1,143 @@
+/*
+ * RTC subsystem, base class
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * class skeleton from drivers/hwmon/hwmon.c
+ *
+ * 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/module.h>
+#include <linux/rtc.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+
+static DEFINE_IDR(rtc_idr);
+static DEFINE_MUTEX(idr_lock);
+struct class *rtc_class;
+
+static void rtc_device_release(struct class_device *class_dev)
+{
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+	mutex_lock(&idr_lock);
+	idr_remove(&rtc_idr, rtc->id);
+	mutex_unlock(&idr_lock);
+	kfree(rtc);
+}
+
+/**
+ * rtc_device_register - register w/ RTC class
+ * @dev: the device to register
+ *
+ * rtc_device_unregister() must be called when the class device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new struct class device.
+ */
+struct rtc_device *rtc_device_register(const char *name, struct device *dev,
+					struct rtc_class_ops *ops,
+					struct module *owner)
+{
+	struct rtc_device *rtc;
+	int id, err;
+
+	if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+
+	mutex_lock(&idr_lock);
+	err = idr_get_new(&rtc_idr, NULL, &id);
+	mutex_unlock(&idr_lock);
+
+	if (err < 0)
+		goto exit;
+
+	id = id & MAX_ID_MASK;
+
+	if ((rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL)) == NULL) {
+		err = -ENOMEM;
+		goto exit_idr;
+	}
+
+	rtc->id = id;
+	rtc->ops = ops;
+	rtc->owner = owner;
+	rtc->class_dev.dev = dev;
+	rtc->class_dev.class = rtc_class;
+	rtc->class_dev.release = rtc_device_release;
+
+	mutex_init(&rtc->ops_lock);
+	spin_lock_init(&rtc->irq_lock);
+	spin_lock_init(&rtc->irq_task_lock);
+
+	strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
+	snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id);
+
+	if ((err = class_device_register(&rtc->class_dev)))
+		goto exit_kfree;
+
+	dev_info(dev, "rtc core: registered %s as %s\n",
+			rtc->name, rtc->class_dev.class_id);
+
+	return rtc;
+
+exit_kfree:
+	kfree(rtc);
+
+exit_idr:
+	idr_remove(&rtc_idr, id);
+
+exit:
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(rtc_device_register);
+
+
+/**
+ * rtc_device_unregister - removes the previously registered RTC class device
+ *
+ * @rtc: the RTC class device to destroy
+ */
+void rtc_device_unregister(struct rtc_device *rtc)
+{
+	mutex_lock(&rtc->ops_lock);
+	rtc->ops = NULL;
+	mutex_unlock(&rtc->ops_lock);
+	class_device_unregister(&rtc->class_dev);
+}
+EXPORT_SYMBOL_GPL(rtc_device_unregister);
+
+int rtc_interface_register(struct class_interface *intf)
+{
+	intf->class = rtc_class;
+	return class_interface_register(intf);
+}
+EXPORT_SYMBOL_GPL(rtc_interface_register);
+
+static int __init rtc_init(void)
+{
+	rtc_class = class_create(THIS_MODULE, "rtc");
+	if (IS_ERR(rtc_class)) {
+		printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
+		return PTR_ERR(rtc_class);
+	}
+	return 0;
+}
+
+static void __exit rtc_exit(void)
+{
+	class_destroy(rtc_class);
+}
+
+module_init(rtc_init);
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towerteh.it>");
+MODULE_DESCRIPTION("RTC class support");
+MODULE_LICENSE("GPL");
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-19 23:33:06.000000000 +0100
@@ -0,0 +1,42 @@
+#
+# RTC class/drivers configuration
+#
+
+menu "Real Time Clock"
+
+config RTC_CLASS
+	tristate "RTC class"
+	depends on EXPERIMENTAL
+	default y
+	help
+	  Generic RTC class support. If you say yes here, you will
+ 	  be allowed to plug one or more RTCs to your system. You will
+	  probably want to enable one of more of the interfaces below.
+
+	  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
+
+comment "RTC drivers"
+	depends on RTC_CLASS
+
+endmenu
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/Makefile	2006-02-19 23:33:06.000000000 +0100
@@ -0,0 +1,9 @@
+#
+# 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
+
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/interface.c	2006-02-15 04:13:37.000000000 +0100
@@ -0,0 +1,274 @@
+/*
+ * RTC subsystem, interface functions
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * 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>
+
+extern struct class *rtc_class;
+
+int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if ((err = mutex_lock_interruptible(&rtc->ops_lock)))
+		return err;
+
+	if (!rtc->ops)
+		err = -ENODEV;
+	else if (!rtc->ops->read_time)
+		err = -EINVAL;
+	else {
+		memset(tm, 0, sizeof(struct rtc_time));
+		err = rtc->ops->read_time(class_dev->dev, tm);
+	}
+
+	mutex_unlock(&rtc->ops_lock);
+	return err;
+}
+EXPORT_SYMBOL(rtc_read_time);
+
+int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if ((err = rtc_valid_tm(tm)) != 0)
+		return err;
+
+	if ((err = mutex_lock_interruptible(&rtc->ops_lock)))
+		return err;
+
+	if (!rtc->ops)
+		err = -ENODEV;
+	else if (!rtc->ops->set_time)
+		err = -EINVAL;
+	else
+		err = rtc->ops->set_time(class_dev->dev, tm);
+
+	mutex_unlock(&rtc->ops_lock);
+	return err;
+}
+EXPORT_SYMBOL(rtc_set_time);
+
+int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if ((err = mutex_lock_interruptible(&rtc->ops_lock)))
+		return err;
+
+	if (!rtc->ops)
+		err = -ENODEV;
+	else if (!rtc->ops->set_mmss) {
+		if (rtc->ops->read_time && rtc->ops->set_time) {
+		        struct rtc_time new, old;
+
+		        new.tm_sec  = secs % 60;
+		        secs /= 60;
+		        new.tm_min  = secs % 60;
+		        secs /= 60;
+		        new.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_hour == 23 && old.tm_min == 59) ||
+		            (new.tm_hour == 23 && new.tm_min == 59)))
+				err = rtc->ops->set_time(class_dev->dev, &new);
+		}
+		else
+			err = -EINVAL;
+	}
+	else
+		err = rtc->ops->set_mmss(class_dev->dev, secs);
+
+	mutex_unlock(&rtc->ops_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(rtc_set_mmss);
+
+int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if ((err = mutex_lock_interruptible(&rtc->ops_lock)))
+		return err;
+
+	if (rtc->ops == NULL)
+		err = -ENODEV;
+	else if (!rtc->ops->read_alarm)
+		err = -EINVAL;
+	else {
+		memset(alarm, 0, sizeof(struct rtc_wkalrm));
+		err = rtc->ops->read_alarm(class_dev->dev, alarm);
+	}
+
+	mutex_unlock(&rtc->ops_lock);
+	return err;
+}
+EXPORT_SYMBOL(rtc_read_alarm);
+
+int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+{
+	int err;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if ((err = mutex_lock_interruptible(&rtc->ops_lock)))
+		return err;
+
+	if (!rtc->ops)
+		err = -ENODEV;
+	else if (!rtc->ops->set_alarm)
+		err = -EINVAL;
+	else
+		err = rtc->ops->set_alarm(class_dev->dev, alarm);
+
+	mutex_unlock(&rtc->ops_lock);
+	return err;
+}
+EXPORT_SYMBOL(rtc_set_alarm);
+
+void rtc_update_irq(struct class_device *class_dev,
+		unsigned long num, unsigned long events)
+{
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	spin_lock(&rtc->irq_lock);
+	rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
+	spin_unlock(&rtc->irq_lock);
+
+	spin_lock(&rtc->irq_task_lock);
+	if (rtc->irq_task)
+		rtc->irq_task->func(rtc->irq_task->private_data);
+	spin_unlock(&rtc->irq_task_lock);
+
+	wake_up_interruptible(&rtc->irq_queue);
+	kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
+}
+EXPORT_SYMBOL(rtc_update_irq);
+
+struct class_device *rtc_class_open(char *name)
+{
+	struct class_device *class_dev = NULL,
+				*class_dev_tmp;
+
+	down(&rtc_class->sem);
+	list_for_each_entry(class_dev_tmp, &rtc_class->children, node) {
+		if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) {
+			class_dev = class_dev_tmp;
+			break;
+		}
+	}
+
+	if (class_dev) {
+		if (!try_module_get(to_rtc_device(class_dev)->owner))
+			class_dev = NULL;
+	}
+	up(&rtc_class->sem);
+
+	return class_dev;
+}
+EXPORT_SYMBOL(rtc_class_open);
+
+void rtc_class_close(struct class_device *class_dev)
+{
+	module_put(to_rtc_device(class_dev)->owner);
+}
+EXPORT_SYMBOL(rtc_class_close);
+
+int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
+{
+	int retval = -EBUSY;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if (task == NULL || task->func == NULL)
+		return -EINVAL;
+
+	spin_lock(&rtc->irq_task_lock);
+	if (rtc->irq_task == NULL) {
+		rtc->irq_task = task;
+		retval = 0;
+	}
+	spin_unlock(&rtc->irq_task_lock);
+
+	return retval;
+}
+EXPORT_SYMBOL(rtc_irq_register);
+
+void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
+{
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	spin_lock(&rtc->irq_task_lock);
+	if (rtc->irq_task == task)
+		rtc->irq_task = NULL;
+	spin_unlock(&rtc->irq_task_lock);
+}
+EXPORT_SYMBOL(rtc_irq_unregister);
+
+int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled)
+{
+	int err = 0;
+	unsigned long flags;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	spin_lock_irqsave(&rtc->irq_task_lock, flags);
+	if (rtc->irq_task != task)
+		err = -ENXIO;
+	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+	if (err == 0)
+		err = rtc->ops->irq_set_state(class_dev->dev, enabled);
+
+	return err;
+}
+EXPORT_SYMBOL(rtc_irq_set_state);
+
+int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
+{
+	int err = 0, tmp = 0;
+	unsigned long flags;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	/* allowed range is 2-8192 */
+	if (freq < 2 || freq > 8192)
+		return -EINVAL;
+
+/*	if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE)))
+		return -EACCES;
+*/
+	/* check if freq is a power of 2 */
+	while (freq > (1 << tmp))
+		tmp++;
+
+	if (freq != (1 << tmp))
+		return -EINVAL;
+
+	spin_lock_irqsave(&rtc->irq_task_lock, flags);
+	if (rtc->irq_task != task)
+		err = -ENXIO;
+	spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+	if (err == 0) {
+		if ((err = rtc->ops->irq_set_freq(class_dev->dev, freq)) == 0)
+			rtc->irq_freq = freq;
+	}
+	return err;
+
+}
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/utils.c	2006-02-15 03:24:47.000000000 +0100
@@ -0,0 +1,97 @@
+/*
+ * RTC subsystem, utility functions
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * 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>
+
+static const unsigned char rtc_days_in_month[] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+int rtc_month_days(unsigned int month, unsigned int year)
+{
+	return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+}
+EXPORT_SYMBOL(rtc_month_days);
+
+/*
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+	int days, month, year;
+
+	days = time / 86400;
+	time -= days * 86400;
+
+	tm->tm_wday = (days + 4) % 7;
+
+	year = 1970 + days / 365;
+	days -= (year - 1970) * 365
+	        + LEAPS_THRU_END_OF(year - 1)
+	        - LEAPS_THRU_END_OF(1970 - 1);
+	if (days < 0) {
+		year -= 1;
+		days += 365 + LEAP_YEAR(year);
+	}
+	tm->tm_year = year - 1900;
+	tm->tm_yday = days + 1;
+
+	for (month = 0; month < 11; month++) {
+		int newdays;
+
+		newdays = days - rtc_month_days(month, year);
+		if (newdays < 0)
+			break;
+		days = newdays;
+	}
+	tm->tm_mon = month;
+	tm->tm_mday = days + 1;
+
+	tm->tm_hour = time / 3600;
+	time -= tm->tm_hour * 3600;
+	tm->tm_min = time / 60;
+	tm->tm_sec = time - tm->tm_min * 60;
+}
+EXPORT_SYMBOL(rtc_time_to_tm);
+
+/*
+ * Does the rtc_time represent a valid date/time?
+ */
+int rtc_valid_tm(struct rtc_time *tm)
+{
+	if (tm->tm_year < 70 ||
+	    tm->tm_mon >= 12 ||
+	    tm->tm_mday < 1 ||
+	    tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) ||
+	    tm->tm_hour >= 24 ||
+	    tm->tm_min >= 60 ||
+	    tm->tm_sec >= 60)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(rtc_valid_tm);
+
+/*
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+{
+	*time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+		       tm->tm_hour, tm->tm_min, tm->tm_sec);
+	return 0;
+}
+EXPORT_SYMBOL(rtc_tm_to_time);
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/hctosys.c	2006-02-15 03:24:47.000000000 +0100
@@ -0,0 +1,67 @@
+/*
+ * 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 rtc_time tm;
+	struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+
+	if (class_dev == NULL) {
+		printk("%s: unable to open rtc device (%s)\n",
+			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+		return -ENODEV;
+	}
+
+	if ((err = rtc_read_time(class_dev, &tm)) == 0) {
+		if (rtc_valid_tm(&tm)) {
+			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 (%u)\n",
+				tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+				tm.tm_hour, tm.tm_min, tm.tm_sec,
+				(unsigned int) tv.tv_sec);
+		}
+		else
+			dev_err(class_dev->dev,
+				"hctosys: invalid date/time\n");
+	}
+	else
+		dev_err(class_dev->dev,
+			"hctosys: unable to read the hardware clock\n");
+
+	rtc_class_close(class_dev);
+
+	return 0;
+}
+
+late_initcall(rtc_hctosys);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 02/11] RTC subsystem, ARM cleanup
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 01/11] RTC subsystem, class Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 03/11] RTC subsystem, I2C cleanup Alessandro Zummo
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-arm-cleanup.patch --]
[-- Type: text/plain, Size: 7006 bytes --]

This patch removes from the ARM subsytem some of the
rtc-related functions that have been included in
the RTC subsystem.

ARM Kconfig is modified to include the RTC subsystem.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 arch/arm/Kconfig          |    2 
 arch/arm/common/rtctime.c |  107 +++++-----------------------------------------
 drivers/char/Kconfig      |    2 
 include/asm-arm/rtc.h     |    3 -
 4 files changed, 15 insertions(+), 99 deletions(-)

--- linux-rtc.orig/arch/arm/Kconfig	2006-02-13 19:34:30.000000000 +0100
+++ linux-rtc/arch/arm/Kconfig	2006-02-13 19:35:30.000000000 +0100
@@ -817,6 +817,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/rtc/Kconfig"
+
 endmenu
 
 source "fs/Kconfig"
--- linux-rtc.orig/arch/arm/common/rtctime.c	2006-02-13 19:34:30.000000000 +0100
+++ linux-rtc/arch/arm/common/rtctime.c	2006-02-13 19:35:30.000000000 +0100
@@ -42,89 +42,6 @@ static struct rtc_ops *rtc_ops;
 
 #define rtc_epoch 1900UL
 
-static const unsigned char days_in_month[] = {
-	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
-#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
-
-static int month_days(unsigned int month, unsigned int year)
-{
-	return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
-}
-
-/*
- * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
- */
-void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
-{
-	int days, month, year;
-
-	days = time / 86400;
-	time -= days * 86400;
-
-	tm->tm_wday = (days + 4) % 7;
-
-	year = 1970 + days / 365;
-	days -= (year - 1970) * 365
-	        + LEAPS_THRU_END_OF(year - 1)
-	        - LEAPS_THRU_END_OF(1970 - 1);
-	if (days < 0) {
-		year -= 1;
-		days += 365 + LEAP_YEAR(year);
-	}
-	tm->tm_year = year - 1900;
-	tm->tm_yday = days + 1;
-
-	for (month = 0; month < 11; month++) {
-		int newdays;
-
-		newdays = days - month_days(month, year);
-		if (newdays < 0)
-			break;
-		days = newdays;
-	}
-	tm->tm_mon = month;
-	tm->tm_mday = days + 1;
-
-	tm->tm_hour = time / 3600;
-	time -= tm->tm_hour * 3600;
-	tm->tm_min = time / 60;
-	tm->tm_sec = time - tm->tm_min * 60;
-}
-EXPORT_SYMBOL(rtc_time_to_tm);
-
-/*
- * Does the rtc_time represent a valid date/time?
- */
-int rtc_valid_tm(struct rtc_time *tm)
-{
-	if (tm->tm_year < 70 ||
-	    tm->tm_mon >= 12 ||
-	    tm->tm_mday < 1 ||
-	    tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) ||
-	    tm->tm_hour >= 24 ||
-	    tm->tm_min >= 60 ||
-	    tm->tm_sec >= 60)
-		return -EINVAL;
-
-	return 0;
-}
-EXPORT_SYMBOL(rtc_valid_tm);
-
-/*
- * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
- */
-int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
-{
-	*time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
-		       tm->tm_hour, tm->tm_min, tm->tm_sec);
-
-	return 0;
-}
-EXPORT_SYMBOL(rtc_tm_to_time);
-
 /*
  * Calculate the next alarm time given the requested alarm time mask
  * and the current time.
@@ -143,13 +60,13 @@ void rtc_next_alarm_time(struct rtc_time
 	next->tm_sec = alrm->tm_sec;
 }
 
-static inline int rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
+static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm)
 {
 	memset(tm, 0, sizeof(struct rtc_time));
 	return ops->read_time(tm);
 }
 
-static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
+static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm)
 {
 	int ret;
 
@@ -160,7 +77,7 @@ static inline int rtc_set_time(struct rt
 	return ret;
 }
 
-static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
 {
 	int ret = -EINVAL;
 	if (ops->read_alarm) {
@@ -170,7 +87,7 @@ static inline int rtc_read_alarm(struct 
 	return ret;
 }
 
-static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
 {
 	int ret = -EINVAL;
 	if (ops->set_alarm)
@@ -258,7 +175,7 @@ static int rtc_ioctl(struct inode *inode
 
 	switch (cmd) {
 	case RTC_ALM_READ:
-		ret = rtc_read_alarm(ops, &alrm);
+		ret = rtc_arm_read_alarm(ops, &alrm);
 		if (ret)
 			break;
 		ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
@@ -280,11 +197,11 @@ static int rtc_ioctl(struct inode *inode
 		alrm.time.tm_wday = -1;
 		alrm.time.tm_yday = -1;
 		alrm.time.tm_isdst = -1;
-		ret = rtc_set_alarm(ops, &alrm);
+		ret = rtc_arm_set_alarm(ops, &alrm);
 		break;
 
 	case RTC_RD_TIME:
-		ret = rtc_read_time(ops, &tm);
+		ret = rtc_arm_read_time(ops, &tm);
 		if (ret)
 			break;
 		ret = copy_to_user(uarg, &tm, sizeof(tm));
@@ -302,7 +219,7 @@ static int rtc_ioctl(struct inode *inode
 			ret = -EFAULT;
 			break;
 		}
-		ret = rtc_set_time(ops, &tm);
+		ret = rtc_arm_set_time(ops, &tm);
 		break;
 
 	case RTC_EPOCH_SET:
@@ -333,11 +250,11 @@ static int rtc_ioctl(struct inode *inode
 			ret = -EFAULT;
 			break;
 		}
-		ret = rtc_set_alarm(ops, &alrm);
+		ret = rtc_arm_set_alarm(ops, &alrm);
 		break;
 
 	case RTC_WKALM_RD:
-		ret = rtc_read_alarm(ops, &alrm);
+		ret = rtc_arm_read_alarm(ops, &alrm);
 		if (ret)
 			break;
 		ret = copy_to_user(uarg, &alrm, sizeof(alrm));
@@ -427,7 +344,7 @@ static int rtc_read_proc(char *page, cha
 	struct rtc_time tm;
 	char *p = page;
 
-	if (rtc_read_time(ops, &tm) == 0) {
+	if (rtc_arm_read_time(ops, &tm) == 0) {
 		p += sprintf(p,
 			"rtc_time\t: %02d:%02d:%02d\n"
 			"rtc_date\t: %04d-%02d-%02d\n"
@@ -437,7 +354,7 @@ static int rtc_read_proc(char *page, cha
 			rtc_epoch);
 	}
 
-	if (rtc_read_alarm(ops, &alrm) == 0) {
+	if (rtc_arm_read_alarm(ops, &alrm) == 0) {
 		p += sprintf(p, "alrm_time\t: ");
 		if ((unsigned int)alrm.time.tm_hour <= 24)
 			p += sprintf(p, "%02d:", alrm.time.tm_hour);
--- linux-rtc.orig/include/asm-arm/rtc.h	2006-01-03 04:21:10.000000000 +0100
+++ linux-rtc/include/asm-arm/rtc.h	2006-02-13 19:35:30.000000000 +0100
@@ -25,9 +25,6 @@ struct rtc_ops {
 	int		(*proc)(char *buf);
 };
 
-void rtc_time_to_tm(unsigned long, struct rtc_time *);
-int rtc_tm_to_time(struct rtc_time *, unsigned long *);
-int rtc_valid_tm(struct rtc_time *);
 void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *);
 void rtc_update(unsigned long, unsigned long);
 int register_rtc(struct rtc_ops *);
--- linux-rtc.orig/drivers/char/Kconfig	2006-02-13 19:34:36.000000000 +0100
+++ linux-rtc/drivers/char/Kconfig	2006-02-13 19:35:30.000000000 +0100
@@ -695,7 +695,7 @@ config NVRAM
 
 config RTC
 	tristate "Enhanced Real Time Clock Support"
-	depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV
+	depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM
 	---help---
 	  If you say Y here and create a character special file /dev/rtc with
 	  major number 10 and minor number 135 using mknod ("man mknod"), you

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 03/11] RTC subsystem, I2C cleanup
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 01/11] RTC subsystem, class Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 02/11] RTC subsystem, ARM cleanup Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 04/11] RTC subsystem, sysfs interface Alessandro Zummo
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-i2c-purge.patch --]
[-- Type: text/plain, Size: 33125 bytes --]

This patch, completely optional, removes from
drivers/i2c/chips all the drivers that are implemented
in the new RTC subsystem.

It should be noted that none of the current
driver is actually integrated, i.e. usable
without further patches.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/i2c/chips/Kconfig   |   18 -
 drivers/i2c/chips/Makefile  |    2 
 drivers/i2c/chips/rtc8564.c |  385 ------------------------
 drivers/i2c/chips/rtc8564.h |   78 ----
 drivers/i2c/chips/x1205.c   |  698 --------------------------------------------
 include/linux/x1205.h       |   31 -
 6 files changed, 1212 deletions(-)

--- linux-rtc.orig/drivers/i2c/chips/x1205.c	2006-02-13 19:34:36.000000000 +0100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,698 +0,0 @@
-/*
- *  x1205.c - An i2c driver for the Xicor X1205 RTC
- *  Copyright 2004 Karen Spearel
- *  Copyright 2005 Alessandro Zummo
- *
- *  please send all reports to:
- *	kas11 at tampabay dot rr dot com
- *      a dot zummo at towertech dot it
- *
- *  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 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/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/string.h>
-#include <linux/bcd.h>
-#include <linux/rtc.h>
-#include <linux/list.h>
-
-#include <linux/x1205.h>
-
-#define DRV_VERSION "0.9.9"
-
-/* Addresses to scan: none. This chip is located at
- * 0x6f and uses a two bytes register addressing.
- * Two bytes need to be written to read a single register,
- * while most other chips just require one and take the second
- * one as the data to be written. To prevent corrupting
- * unknown chips, the user must explicitely set the probe parameter.
- */
-
-static unsigned short normal_i2c[] = { I2C_CLIENT_END };
-
-/* Insmod parameters */
-I2C_CLIENT_INSMOD;
-I2C_CLIENT_MODULE_PARM(hctosys,
-	"Set the system time from the hardware clock upon initialization");
-
-/* offsets into CCR area */
-
-#define CCR_SEC			0
-#define CCR_MIN			1
-#define CCR_HOUR		2
-#define CCR_MDAY		3
-#define CCR_MONTH		4
-#define CCR_YEAR		5
-#define CCR_WDAY		6
-#define CCR_Y2K			7
-
-#define X1205_REG_SR		0x3F	/* status register */
-#define X1205_REG_Y2K		0x37
-#define X1205_REG_DW		0x36
-#define X1205_REG_YR		0x35
-#define X1205_REG_MO		0x34
-#define X1205_REG_DT		0x33
-#define X1205_REG_HR		0x32
-#define X1205_REG_MN		0x31
-#define X1205_REG_SC		0x30
-#define X1205_REG_DTR		0x13
-#define X1205_REG_ATR		0x12
-#define X1205_REG_INT		0x11
-#define X1205_REG_0		0x10
-#define X1205_REG_Y2K1		0x0F
-#define X1205_REG_DWA1		0x0E
-#define X1205_REG_YRA1		0x0D
-#define X1205_REG_MOA1		0x0C
-#define X1205_REG_DTA1		0x0B
-#define X1205_REG_HRA1		0x0A
-#define X1205_REG_MNA1		0x09
-#define X1205_REG_SCA1		0x08
-#define X1205_REG_Y2K0		0x07
-#define X1205_REG_DWA0		0x06
-#define X1205_REG_YRA0		0x05
-#define X1205_REG_MOA0		0x04
-#define X1205_REG_DTA0		0x03
-#define X1205_REG_HRA0		0x02
-#define X1205_REG_MNA0		0x01
-#define X1205_REG_SCA0		0x00
-
-#define X1205_CCR_BASE		0x30	/* Base address of CCR */
-#define X1205_ALM0_BASE		0x00	/* Base address of ALARM0 */
-
-#define X1205_SR_RTCF		0x01	/* Clock failure */
-#define X1205_SR_WEL		0x02	/* Write Enable Latch */
-#define X1205_SR_RWEL		0x04	/* Register Write Enable */
-
-#define X1205_DTR_DTR0		0x01
-#define X1205_DTR_DTR1		0x02
-#define X1205_DTR_DTR2		0x04
-
-#define X1205_HR_MIL		0x80	/* Set in ccr.hour for 24 hr mode */
-
-/* Prototypes */
-static int x1205_attach(struct i2c_adapter *adapter);
-static int x1205_detach(struct i2c_client *client);
-static int x1205_probe(struct i2c_adapter *adapter, int address, int kind);
-static int x1205_command(struct i2c_client *client, unsigned int cmd,
-	void *arg);
-
-static struct i2c_driver x1205_driver = {
-	.driver = {
-		.name	= "x1205",
-	},
-	.attach_adapter = &x1205_attach,
-	.detach_client	= &x1205_detach,
-};
-
-struct x1205_data {
-	struct i2c_client client;
-	struct list_head list;
-	unsigned int epoch;
-};
-
-static const unsigned char days_in_mo[] =
-	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
-static LIST_HEAD(x1205_clients);
-
-/* Workaround until the I2C subsytem will allow to send
- * commands to a specific client. This function will send the command
- * to the first client.
- */
-int x1205_do_command(unsigned int cmd, void *arg)
-{
-	struct list_head *walk;
-	struct list_head *tmp;
-	struct x1205_data *data;
-
-	list_for_each_safe(walk, tmp, &x1205_clients) {
-		data = list_entry(walk, struct x1205_data, list);
-		return x1205_command(&data->client, cmd, arg);
-	}
-
-	return -ENODEV;
-}
-
-#define is_leap(year) \
-	((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
-/* make sure the rtc_time values are in bounds */
-static int x1205_validate_tm(struct rtc_time *tm)
-{
-	int year = tm->tm_year + 1900;
-
-	if ((tm->tm_year < 70) || (tm->tm_year > 255))
-		return -EINVAL;
-
-	if ((tm->tm_mon > 11) || (tm->tm_mday == 0))
-		return -EINVAL;
-
-	if (tm->tm_mday > days_in_mo[tm->tm_mon]
-		+ ((tm->tm_mon == 1) && is_leap(year)))
-		return -EINVAL;
-
-	if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60))
-		return -EINVAL;
-
-	return 0;
-}
-
-/*
- * In the routines that deal directly with the x1205 hardware, we use
- * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
- * Epoch is initialized as 2000. Time is set to UTC.
- */
-static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
-				u8 reg_base)
-{
-	unsigned char dt_addr[2] = { 0, reg_base };
-	static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
-
-	unsigned char buf[8], sr;
-
-	struct i2c_msg msgs[] = {
-		{ client->addr, 0, 2, sr_addr },	/* setup read ptr */
-		{ client->addr, I2C_M_RD, 1, &sr }, 	/* read status */
-		{ client->addr, 0, 2, dt_addr },	/* setup read ptr */
-		{ client->addr, I2C_M_RD, 8, buf },	/* read date */
-	};
-
-	struct x1205_data *data = i2c_get_clientdata(client);
-
-	/* read status register */
-	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
-		return -EIO;
-	}
-
-	/* check for battery failure */
-	if (sr & X1205_SR_RTCF) {
-		dev_warn(&client->dev,
-			"Clock had a power failure, you must set the date.\n");
-		return -EINVAL;
-	}
-
-	/* read date registers */
-	if ((i2c_transfer(client->adapter, &msgs[2], 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
-		return -EIO;
-	}
-
-	dev_dbg(&client->dev,
-		"%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
-		"mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
-		__FUNCTION__,
-		buf[0], buf[1], buf[2], buf[3],
-		buf[4], buf[5], buf[6], buf[7]);
-
-	tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
-	tm->tm_min = BCD2BIN(buf[CCR_MIN]);
-	tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
-	tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
-	tm->tm_mon = BCD2BIN(buf[CCR_MONTH]);
-	data->epoch = BCD2BIN(buf[CCR_Y2K]) * 100;
-	tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + data->epoch - 1900;
-	tm->tm_wday = buf[CCR_WDAY];
-
-	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);
-
-	return 0;
-}
-
-static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
-				int datetoo, u8 reg_base)
-{
-	int i, err, xfer;
-
-	unsigned char buf[8];
-
-	static const unsigned char wel[3] = { 0, X1205_REG_SR,
-						X1205_SR_WEL };
-
-	static const unsigned char rwel[3] = { 0, X1205_REG_SR,
-						X1205_SR_WEL | X1205_SR_RWEL };
-
-	static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
-
-	struct x1205_data *data = i2c_get_clientdata(client);
-
-	/* check if all values in the tm struct are correct */
-	if ((err = x1205_validate_tm(tm)) < 0)
-		return err;
-
-	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);
-
-	buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
-	buf[CCR_MIN] = BIN2BCD(tm->tm_min);
-
-	/* set hour and 24hr bit */
-	buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL;
-
-	/* should we also set the date? */
-	if (datetoo) {
-		buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
-
-		/* month, 0 - 11 */
-		buf[CCR_MONTH] = BIN2BCD(tm->tm_mon);
-
-		/* year, since 1900 */
-		buf[CCR_YEAR] = BIN2BCD(tm->tm_year + 1900 - data->epoch);
-		buf[CCR_WDAY] = tm->tm_wday & 0x07;
-		buf[CCR_Y2K] = BIN2BCD(data->epoch / 100);
-	}
-
-	/* this sequence is required to unlock the chip */
-	xfer = i2c_master_send(client, wel, 3);
-	if (xfer != 3) {
-		dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer);
-		return -EIO;
-	}
-
-	xfer = i2c_master_send(client, rwel, 3);
-	if (xfer != 3) {
-		dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer);
-		return -EIO;
-	}
-
-	/* write register's data */
-	for (i = 0; i < (datetoo ? 8 : 3); i++) {
-		unsigned char rdata[3] = { 0, reg_base + i, buf[i] };
-
-		xfer = i2c_master_send(client, rdata, 3);
-		if (xfer != 3) {
-			dev_err(&client->dev,
-				"%s: xfer=%d addr=%02x, data=%02x\n",
-				__FUNCTION__,
-				 xfer, rdata[1], rdata[2]);
-			return -EIO;
-		}
-	};
-
-	/* disable further writes */
-	xfer = i2c_master_send(client, diswe, 3);
-	if (xfer != 3) {
-		dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int x1205_get_dtrim(struct i2c_client *client, int *trim)
-{
-	unsigned char dtr;
-	static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
-
-	struct i2c_msg msgs[] = {
-		{ client->addr, 0, 2, dtr_addr },	/* setup read ptr */
-		{ client->addr, I2C_M_RD, 1, &dtr }, 	/* read dtr */
-	};
-
-	/* read dtr register */
-	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
-		return -EIO;
-	}
-
-	dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr);
-
-	*trim = 0;
-
-	if (dtr & X1205_DTR_DTR0)
-		*trim += 20;
-
-	if (dtr & X1205_DTR_DTR1)
-		*trim += 10;
-
-	if (dtr & X1205_DTR_DTR2)
-		*trim = -*trim;
-
-	return 0;
-}
-
-static int x1205_get_atrim(struct i2c_client *client, int *trim)
-{
-	s8 atr;
-	static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
-
-	struct i2c_msg msgs[] = {
-		{ client->addr, 0, 2, atr_addr },	/* setup read ptr */
-		{ client->addr, I2C_M_RD, 1, &atr }, 	/* read atr */
-	};
-
-	/* read atr register */
-	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
-		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
-		return -EIO;
-	}
-
-	dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr);
-
-	/* atr is a two's complement value on 6 bits,
-	 * perform sign extension. The formula is
-	 * Catr = (atr * 0.25pF) + 11.00pF.
-	 */
-	if (atr & 0x20)
-		atr |= 0xC0;
-
-	dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr);
-
-	*trim = (atr * 250) + 11000;
-
-	dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim);
-
-	return 0;
-}
-
-static int x1205_hctosys(struct i2c_client *client)
-{
-	int err;
-
-	struct rtc_time tm;
-	struct timespec tv;
-
-	err = x1205_command(client, X1205_CMD_GETDATETIME, &tm);
-
-	if (err) {
-		dev_err(&client->dev,
-			"Unable to set the system clock\n");
-		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;
-
-	/* WARNING: this is not the C library 'mktime' call, it is a built in
-	 * inline function from include/linux/time.h.  It expects (requires)
-	 * the month to be in the range 1-12
-	 */
-
-	tv.tv_sec  = mktime(tm.tm_year + 1900, tm.tm_mon + 1,
-				tm.tm_mday, tm.tm_hour,
-				tm.tm_min, tm.tm_sec);
-
-	do_settimeofday(&tv);
-
-	dev_info(&client->dev,
-		"setting the system clock to %d-%d-%d %d:%d:%d\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;
-	unsigned char mask;
-	unsigned char min;
-	unsigned char max;
-};
-
-static int x1205_validate_client(struct i2c_client *client)
-{
-	int i, xfer;
-
-	/* Probe array. We will read the register at the specified
-	 * address and check if the given bits are zero.
-	 */
-	static const unsigned char probe_zero_pattern[] = {
-		/* register, mask */
-		X1205_REG_SR,	0x18,
-		X1205_REG_DTR,	0xF8,
-		X1205_REG_ATR,	0xC0,
-		X1205_REG_INT,	0x18,
-		X1205_REG_0,	0xFF,
-	};
-
-	static const struct x1205_limit probe_limits_pattern[] = {
-		/* register, mask, min, max */
-		{ X1205_REG_Y2K,	0xFF,	19,	20	},
-		{ X1205_REG_DW,		0xFF,	0,	6	},
-		{ X1205_REG_YR,		0xFF,	0,	99	},
-		{ X1205_REG_MO,		0xFF,	0,	12	},
-		{ X1205_REG_DT,		0xFF,	0,	31	},
-		{ X1205_REG_HR,		0x7F,	0,	23	},
-		{ X1205_REG_MN,		0xFF,	0,	59	},
-		{ X1205_REG_SC,		0xFF,	0,	59	},
-		{ X1205_REG_Y2K1,	0xFF,	19,	20	},
-		{ X1205_REG_Y2K0,	0xFF,	19,	20	},
-	};
-
-	/* check that registers have bits a 0 where expected */
-	for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {
-		unsigned char buf;
-
-		unsigned char addr[2] = { 0, probe_zero_pattern[i] };
-
-		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__, addr[1]);
-
-			return -EIO;
-		}
-
-		if ((buf & probe_zero_pattern[i+1]) != 0) {
-			dev_err(&client->adapter->dev,
-				"%s: register=%02x, zero pattern=%d, value=%x\n",
-				__FUNCTION__, addr[1], i, buf);
-
-			return -ENODEV;
-		}
-	}
-
-	/* check limits (only registers with bcd values) */
-	for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
-		unsigned char reg, value;
-
-		unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
-
-		struct i2c_msg msgs[2] = {
-			{ client->addr, 0, 2, addr },
-			{ client->addr, I2C_M_RD, 1, &reg },
-		};
-
-		xfer = i2c_transfer(client->adapter, msgs, 2);
-
-		if (xfer != 2) {
-			dev_err(&client->adapter->dev,
-				"%s: could not read register %x\n",
-				__FUNCTION__, addr[1]);
-
-			return -EIO;
-		}
-
-		value = BCD2BIN(reg & 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__, addr[1], i, value);
-
-			return -ENODEV;
-		}
-	}
-
-	return 0;
-}
-
-static int x1205_attach(struct i2c_adapter *adapter)
-{
-	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
-
-	return i2c_probe(adapter, &addr_data, x1205_probe);
-}
-
-int x1205_direct_attach(int adapter_id,
-	struct i2c_client_address_data *address_data)
-{
-	int err;
-	struct i2c_adapter *adapter = i2c_get_adapter(adapter_id);
-
-	if (adapter) {
-		err = i2c_probe(adapter,
-			address_data, x1205_probe);
-
-		i2c_put_adapter(adapter);
-
-		return err;
-	}
-
-	return -ENODEV;
-}
-
-static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
-{
-	struct i2c_client *client;
-	struct x1205_data *data;
-
-	int err = 0;
-
-	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
-
-	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
-		err = -ENODEV;
-		goto exit;
-	}
-
-	if (!(data = kzalloc(sizeof(struct x1205_data), GFP_KERNEL))) {
-		err = -ENOMEM;
-		goto exit;
-	}
-
-	/* Initialize our structures */
-	data->epoch = 2000;
-
-	client = &data->client;
-	client->addr = address;
-	client->driver = &x1205_driver;
-	client->adapter	= adapter;
-
-	strlcpy(client->name, "x1205", I2C_NAME_SIZE);
-
-	i2c_set_clientdata(client, data);
-
-	/* Verify the chip is really an X1205 */
-	if (kind < 0) {
-		if (x1205_validate_client(client) < 0) {
-			err = -ENODEV;
-			goto exit_kfree;
-		}
-	}
-
-	/* Inform the i2c layer */
-	if ((err = i2c_attach_client(client)))
-		goto exit_kfree;
-
-	list_add(&data->list, &x1205_clients);
-
-	dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
-
-	/* If requested, set the system time */
-	if (hctosys)
-		x1205_hctosys(client);
-
-	return 0;
-
-exit_kfree:
-	kfree(data);
-
-exit:
-	return err;
-}
-
-static int x1205_detach(struct i2c_client *client)
-{
-	int err;
-	struct x1205_data *data = i2c_get_clientdata(client);
-
-	dev_dbg(&client->dev, "%s\n", __FUNCTION__);
-
-	if ((err = i2c_detach_client(client)))
-		return err;
-
-	list_del(&data->list);
-
-	kfree(data);
-
-	return 0;
-}
-
-static int x1205_command(struct i2c_client *client, unsigned int cmd,
-	void *param)
-{
-	if (param == NULL)
-		return -EINVAL;
-
-	if (!capable(CAP_SYS_TIME))
-		return -EACCES;
-
-	dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd);
-
-	switch (cmd) {
-	case X1205_CMD_GETDATETIME:
-		return x1205_get_datetime(client, param, X1205_CCR_BASE);
-
-	case X1205_CMD_SETTIME:
-		return x1205_set_datetime(client, param, 0,
-				X1205_CCR_BASE);
-
-	case X1205_CMD_SETDATETIME:
-		return x1205_set_datetime(client, param, 1,
-				X1205_CCR_BASE);
-
-	case X1205_CMD_GETALARM:
-		return x1205_get_datetime(client, param, X1205_ALM0_BASE);
-
-	case X1205_CMD_SETALARM:
-		return x1205_set_datetime(client, param, 1,
-				X1205_ALM0_BASE);
-
-	case X1205_CMD_GETDTRIM:
-		return x1205_get_dtrim(client, param);
-
-	case X1205_CMD_GETATRIM:
-		return x1205_get_atrim(client, param);
-
-	default:
-		return -EINVAL;
-	}
-}
-
-static int __init x1205_init(void)
-{
-	return i2c_add_driver(&x1205_driver);
-}
-
-static void __exit x1205_exit(void)
-{
-	i2c_del_driver(&x1205_driver);
-}
-
-MODULE_AUTHOR(
-	"Karen Spearel <kas11@tampabay.rr.com>, "
-	"Alessandro Zummo <a.zummo@towertech.it>");
-MODULE_DESCRIPTION("Xicor X1205 RTC driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-EXPORT_SYMBOL_GPL(x1205_do_command);
-EXPORT_SYMBOL_GPL(x1205_direct_attach);
-
-module_init(x1205_init);
-module_exit(x1205_exit);
--- linux-rtc.orig/drivers/i2c/chips/Makefile	2006-01-03 04:21:10.000000000 +0100
+++ linux-rtc/drivers/i2c/chips/Makefile	2006-02-13 19:35:30.000000000 +0100
@@ -10,10 +10,8 @@ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
-obj-$(CONFIG_SENSORS_RTC8564)	+= rtc8564.o
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
-obj-$(CONFIG_RTC_X1205_I2C)	+= x1205.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
--- linux-rtc.orig/drivers/i2c/chips/rtc8564.c	2006-02-13 19:34:36.000000000 +0100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,385 +0,0 @@
-/*
- *  linux/drivers/i2c/chips/rtc8564.c
- *
- *  Copyright (C) 2002-2004 Stefan Eletzhofer
- *
- *	based on linux/drivers/acron/char/pcf8583.c
- *  Copyright (C) 2000 Russell King
- *
- * 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.
- *
- * Driver for system3's EPSON RTC 8564 chip
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bcd.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/rtc.h>		/* get the user-level API */
-#include <linux/init.h>
-
-#include "rtc8564.h"
-
-#ifdef DEBUG
-# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0);
-#else
-# define _DBG(x, fmt, args...) do { } while(0);
-#endif
-
-#define _DBGRTCTM(x, rtctm) if (debug>=x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \
-			"mon=%d, year=%d, wday=%d VL=%d\n", __FUNCTION__, \
-			(rtctm).secs, (rtctm).mins, (rtctm).hours, (rtctm).mday, \
-			(rtctm).mon, (rtctm).year, (rtctm).wday, (rtctm).vl);
-
-struct rtc8564_data {
-	struct i2c_client client;
-	u16 ctrl;
-};
-
-static inline u8 _rtc8564_ctrl1(struct i2c_client *client)
-{
-	struct rtc8564_data *data = i2c_get_clientdata(client);
-	return data->ctrl & 0xff;
-}
-static inline u8 _rtc8564_ctrl2(struct i2c_client *client)
-{
-	struct rtc8564_data *data = i2c_get_clientdata(client);
-	return (data->ctrl & 0xff00) >> 8;
-}
-
-#define CTRL1(c) _rtc8564_ctrl1(c)
-#define CTRL2(c) _rtc8564_ctrl2(c)
-
-static int debug;;
-module_param(debug, int, S_IRUGO | S_IWUSR);
-
-static struct i2c_driver rtc8564_driver;
-
-static unsigned short ignore[] = { I2C_CLIENT_END };
-static unsigned short normal_addr[] = { 0x51, I2C_CLIENT_END };
-
-static struct i2c_client_address_data addr_data = {
-	.normal_i2c		= normal_addr,
-	.probe			= ignore,
-	.ignore			= ignore,
-};
-
-static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem);
-static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem);
-
-static int rtc8564_read(struct i2c_client *client, unsigned char adr,
-			unsigned char *buf, unsigned char len)
-{
-	int ret = -EIO;
-	unsigned char addr[1] = { adr };
-	struct i2c_msg msgs[2] = {
-		{client->addr, 0, 1, addr},
-		{client->addr, I2C_M_RD, len, buf}
-	};
-
-	_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len);
-
-	if (!buf) {
-		ret = -EINVAL;
-		goto done;
-	}
-
-	ret = i2c_transfer(client->adapter, msgs, 2);
-	if (ret == 2) {
-		ret = 0;
-	}
-
-done:
-	return ret;
-}
-
-static int rtc8564_write(struct i2c_client *client, unsigned char adr,
-			 unsigned char *data, unsigned char len)
-{
-	int ret = 0;
-	unsigned char _data[16];
-	struct i2c_msg wr;
-	int i;
-
-	if (!data || len > 15) {
-		ret = -EINVAL;
-		goto done;
-	}
-
-	_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len);
-
-	_data[0] = adr;
-	for (i = 0; i < len; i++) {
-		_data[i + 1] = data[i];
-		_DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]);
-	}
-
-	wr.addr = client->addr;
-	wr.flags = 0;
-	wr.len = len + 1;
-	wr.buf = _data;
-
-	ret = i2c_transfer(client->adapter, &wr, 1);
-	if (ret == 1) {
-		ret = 0;
-	}
-
-done:
-	return ret;
-}
-
-static int rtc8564_attach(struct i2c_adapter *adap, int addr, int kind)
-{
-	int ret;
-	struct i2c_client *new_client;
-	struct rtc8564_data *d;
-	unsigned char data[10];
-	unsigned char ad[1] = { 0 };
-	struct i2c_msg ctrl_wr[1] = {
-		{addr, 0, 2, data}
-	};
-	struct i2c_msg ctrl_rd[2] = {
-		{addr, 0, 1, ad},
-		{addr, I2C_M_RD, 2, data}
-	};
-
-	d = kzalloc(sizeof(struct rtc8564_data), GFP_KERNEL);
-	if (!d) {
-		ret = -ENOMEM;
-		goto done;
-	}
-	new_client = &d->client;
-
-	strlcpy(new_client->name, "RTC8564", I2C_NAME_SIZE);
-	i2c_set_clientdata(new_client, d);
-	new_client->addr = addr;
-	new_client->adapter = adap;
-	new_client->driver = &rtc8564_driver;
-
-	_DBG(1, "client=%p", new_client);
-
-	/* init ctrl1 reg */
-	data[0] = 0;
-	data[1] = 0;
-	ret = i2c_transfer(new_client->adapter, ctrl_wr, 1);
-	if (ret != 1) {
-		printk(KERN_INFO "rtc8564: cant init ctrl1\n");
-		ret = -ENODEV;
-		goto done;
-	}
-
-	/* read back ctrl1 and ctrl2 */
-	ret = i2c_transfer(new_client->adapter, ctrl_rd, 2);
-	if (ret != 2) {
-		printk(KERN_INFO "rtc8564: cant read ctrl\n");
-		ret = -ENODEV;
-		goto done;
-	}
-
-	d->ctrl = data[0] | (data[1] << 8);
-
-	_DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x",
-	     data[0], data[1]);
-
-	ret = i2c_attach_client(new_client);
-done:
-	if (ret) {
-		kfree(d);
-	}
-	return ret;
-}
-
-static int rtc8564_probe(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, rtc8564_attach);
-}
-
-static int rtc8564_detach(struct i2c_client *client)
-{
-	i2c_detach_client(client);
-	kfree(i2c_get_clientdata(client));
-	return 0;
-}
-
-static int rtc8564_get_datetime(struct i2c_client *client, struct rtc_tm *dt)
-{
-	int ret = -EIO;
-	unsigned char buf[15];
-
-	_DBG(1, "client=%p, dt=%p", client, dt);
-
-	if (!dt)
-		return -EINVAL;
-
-	memset(buf, 0, sizeof(buf));
-
-	ret = rtc8564_read(client, 0, buf, 15);
-	if (ret)
-		return ret;
-
-	/* century stored in minute alarm reg */
-	dt->year = BCD2BIN(buf[RTC8564_REG_YEAR]);
-	dt->year += 100 * BCD2BIN(buf[RTC8564_REG_AL_MIN] & 0x3f);
-	dt->mday = BCD2BIN(buf[RTC8564_REG_DAY] & 0x3f);
-	dt->wday = BCD2BIN(buf[RTC8564_REG_WDAY] & 7);
-	dt->mon = BCD2BIN(buf[RTC8564_REG_MON_CENT] & 0x1f);
-
-	dt->secs = BCD2BIN(buf[RTC8564_REG_SEC] & 0x7f);
-	dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80;
-	dt->mins = BCD2BIN(buf[RTC8564_REG_MIN] & 0x7f);
-	dt->hours = BCD2BIN(buf[RTC8564_REG_HR] & 0x3f);
-
-	_DBGRTCTM(2, *dt);
-
-	return 0;
-}
-
-static int
-rtc8564_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo)
-{
-	int ret, len = 5;
-	unsigned char buf[15];
-
-	_DBG(1, "client=%p, dt=%p", client, dt);
-
-	if (!dt)
-		return -EINVAL;
-
-	_DBGRTCTM(2, *dt);
-
-	buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP;
-	buf[RTC8564_REG_CTRL2] = CTRL2(client);
-	buf[RTC8564_REG_SEC] = BIN2BCD(dt->secs);
-	buf[RTC8564_REG_MIN] = BIN2BCD(dt->mins);
-	buf[RTC8564_REG_HR] = BIN2BCD(dt->hours);
-
-	if (datetoo) {
-		len += 5;
-		buf[RTC8564_REG_DAY] = BIN2BCD(dt->mday);
-		buf[RTC8564_REG_WDAY] = BIN2BCD(dt->wday);
-		buf[RTC8564_REG_MON_CENT] = BIN2BCD(dt->mon) & 0x1f;
-		/* century stored in minute alarm reg */
-		buf[RTC8564_REG_YEAR] = BIN2BCD(dt->year % 100);
-		buf[RTC8564_REG_AL_MIN] = BIN2BCD(dt->year / 100);
-	}
-
-	ret = rtc8564_write(client, 0, buf, len);
-	if (ret) {
-		_DBG(1, "error writing data! %d", ret);
-	}
-
-	buf[RTC8564_REG_CTRL1] = CTRL1(client);
-	ret = rtc8564_write(client, 0, buf, 1);
-	if (ret) {
-		_DBG(1, "error writing data! %d", ret);
-	}
-
-	return ret;
-}
-
-static int rtc8564_get_ctrl(struct i2c_client *client, unsigned int *ctrl)
-{
-	struct rtc8564_data *data = i2c_get_clientdata(client);
-
-	if (!ctrl)
-		return -1;
-
-	*ctrl = data->ctrl;
-	return 0;
-}
-
-static int rtc8564_set_ctrl(struct i2c_client *client, unsigned int *ctrl)
-{
-	struct rtc8564_data *data = i2c_get_clientdata(client);
-	unsigned char buf[2];
-
-	if (!ctrl)
-		return -1;
-
-	buf[0] = *ctrl & 0xff;
-	buf[1] = (*ctrl & 0xff00) >> 8;
-	data->ctrl = *ctrl;
-
-	return rtc8564_write(client, 0, buf, 2);
-}
-
-static int rtc8564_read_mem(struct i2c_client *client, struct mem *mem)
-{
-
-	if (!mem)
-		return -EINVAL;
-
-	return rtc8564_read(client, mem->loc, mem->data, mem->nr);
-}
-
-static int rtc8564_write_mem(struct i2c_client *client, struct mem *mem)
-{
-
-	if (!mem)
-		return -EINVAL;
-
-	return rtc8564_write(client, mem->loc, mem->data, mem->nr);
-}
-
-static int
-rtc8564_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
-
-	_DBG(1, "cmd=%d", cmd);
-
-	switch (cmd) {
-	case RTC_GETDATETIME:
-		return rtc8564_get_datetime(client, arg);
-
-	case RTC_SETTIME:
-		return rtc8564_set_datetime(client, arg, 0);
-
-	case RTC_SETDATETIME:
-		return rtc8564_set_datetime(client, arg, 1);
-
-	case RTC_GETCTRL:
-		return rtc8564_get_ctrl(client, arg);
-
-	case RTC_SETCTRL:
-		return rtc8564_set_ctrl(client, arg);
-
-	case MEM_READ:
-		return rtc8564_read_mem(client, arg);
-
-	case MEM_WRITE:
-		return rtc8564_write_mem(client, arg);
-
-	default:
-		return -EINVAL;
-	}
-}
-
-static struct i2c_driver rtc8564_driver = {
-	.driver = {
-		.name	= "RTC8564",
-	},
-	.id		= I2C_DRIVERID_RTC8564,
-	.attach_adapter = rtc8564_probe,
-	.detach_client	= rtc8564_detach,
-	.command	= rtc8564_command
-};
-
-static __init int rtc8564_init(void)
-{
-	return i2c_add_driver(&rtc8564_driver);
-}
-
-static __exit void rtc8564_exit(void)
-{
-	i2c_del_driver(&rtc8564_driver);
-}
-
-MODULE_AUTHOR("Stefan Eletzhofer <Stefan.Eletzhofer@eletztrick.de>");
-MODULE_DESCRIPTION("EPSON RTC8564 Driver");
-MODULE_LICENSE("GPL");
-
-module_init(rtc8564_init);
-module_exit(rtc8564_exit);
--- linux-rtc.orig/drivers/i2c/chips/rtc8564.h	2006-01-03 04:21:10.000000000 +0100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,78 +0,0 @@
-/*
- *  linux/drivers/i2c/chips/rtc8564.h
- *
- *  Copyright (C) 2002-2004 Stefan Eletzhofer
- *
- *	based on linux/drivers/acron/char/pcf8583.h
- *  Copyright (C) 2000 Russell King
- *
- * 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.
- */
-struct rtc_tm {
-	unsigned char	secs;
-	unsigned char	mins;
-	unsigned char	hours;
-	unsigned char	mday;
-	unsigned char	mon;
-	unsigned short	year; /* xxxx 4 digits :) */
-	unsigned char	wday;
-	unsigned char	vl;
-};
-
-struct mem {
-	unsigned int	loc;
-	unsigned int	nr;
-	unsigned char	*data;
-};
-
-#define RTC_GETDATETIME	0
-#define RTC_SETTIME	1
-#define RTC_SETDATETIME	2
-#define RTC_GETCTRL	3
-#define RTC_SETCTRL	4
-#define MEM_READ	5
-#define MEM_WRITE	6
-
-#define RTC8564_REG_CTRL1		0x0 /* T  0 S 0 | T 0 0 0 */
-#define RTC8564_REG_CTRL2		0x1 /* 0  0 0 TI/TP | AF TF AIE TIE */
-#define RTC8564_REG_SEC			0x2 /* VL 4 2 1 | 8 4 2 1 */
-#define RTC8564_REG_MIN			0x3 /* x  4 2 1 | 8 4 2 1 */
-#define RTC8564_REG_HR			0x4 /* x  x 2 1 | 8 4 2 1 */
-#define RTC8564_REG_DAY			0x5 /* x  x 2 1 | 8 4 2 1 */
-#define RTC8564_REG_WDAY		0x6 /* x  x x x | x 4 2 1 */
-#define RTC8564_REG_MON_CENT	0x7 /* C  x x 1 | 8 4 2 1 */
-#define RTC8564_REG_YEAR		0x8 /* 8  4 2 1 | 8 4 2 1 */
-#define RTC8564_REG_AL_MIN		0x9 /* AE 4 2 1 | 8 4 2 1 */
-#define RTC8564_REG_AL_HR		0xa /* AE 4 2 1 | 8 4 2 1 */
-#define RTC8564_REG_AL_DAY		0xb /* AE x 2 1 | 8 4 2 1 */
-#define RTC8564_REG_AL_WDAY		0xc /* AE x x x | x 4 2 1 */
-#define RTC8564_REG_CLKOUT		0xd /* FE x x x | x x FD1 FD0 */
-#define RTC8564_REG_TCTL		0xe /* TE x x x | x x FD1 FD0 */
-#define RTC8564_REG_TIMER		0xf /* 8 bit binary */
-
-/* Control reg */
-#define RTC8564_CTRL1_TEST1		(1<<3)
-#define RTC8564_CTRL1_STOP		(1<<5)
-#define RTC8564_CTRL1_TEST2		(1<<7)
-
-#define RTC8564_CTRL2_TIE		(1<<0)
-#define RTC8564_CTRL2_AIE		(1<<1)
-#define RTC8564_CTRL2_TF		(1<<2)
-#define RTC8564_CTRL2_AF		(1<<3)
-#define RTC8564_CTRL2_TI_TP		(1<<4)
-
-/* CLKOUT frequencies */
-#define RTC8564_FD_32768HZ		(0x0)
-#define RTC8564_FD_1024HZ		(0x1)
-#define RTC8564_FD_32			(0x2)
-#define RTC8564_FD_1HZ			(0x3)
-
-/* Timer CTRL */
-#define RTC8564_TD_4096HZ		(0x0)
-#define RTC8564_TD_64HZ			(0x1)
-#define RTC8564_TD_1HZ			(0x2)
-#define RTC8564_TD_1_60HZ		(0x3)
-
-#define I2C_DRIVERID_RTC8564 0xf000
--- linux-rtc.orig/include/linux/x1205.h	2006-01-03 04:21:10.000000000 +0100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,31 +0,0 @@
-/*
- *  x1205.h - defines for drivers/i2c/chips/x1205.c
- *  Copyright 2004 Karen Spearel
- *  Copyright 2005 Alessandro Zummo
- *
- *  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.
- */
-
-#ifndef __LINUX_X1205_H__
-#define __LINUX_X1205_H__
-
-/* commands */
-
-#define X1205_CMD_GETDATETIME	0
-#define X1205_CMD_SETTIME	1
-#define X1205_CMD_SETDATETIME	2
-#define X1205_CMD_GETALARM	3
-#define X1205_CMD_SETALARM	4
-#define X1205_CMD_GETDTRIM	5
-#define X1205_CMD_SETDTRIM	6
-#define X1205_CMD_GETATRIM	7
-#define X1205_CMD_SETATRIM	8
-
-extern int x1205_do_command(unsigned int cmd, void *arg);
-extern int x1205_direct_attach(int adapter_id,
-	struct i2c_client_address_data *address_data);
-
-#endif /* __LINUX_X1205_H__ */
--- linux-rtc.orig/drivers/i2c/chips/Kconfig	2006-01-03 04:21:10.000000000 +0100
+++ linux-rtc/drivers/i2c/chips/Kconfig	2006-02-13 19:35:30.000000000 +0100
@@ -65,15 +65,6 @@ config SENSORS_PCF8591
 	  This driver can also be built as a module.  If so, the module
 	  will be called pcf8591.
 
-config SENSORS_RTC8564
-	tristate "Epson 8564 RTC chip"
-	depends on I2C && EXPERIMENTAL
-	help
-	  If you say yes here you get support for the Epson 8564 RTC chip.
-
-	  This driver can also be built as a module.  If so, the module
-	  will be called i2c-rtc8564.
-
 config ISP1301_OMAP
 	tristate "Philips ISP1301 with OMAP OTG"
 	depends on I2C && ARCH_OMAP_OTG
@@ -126,13 +117,4 @@ config SENSORS_MAX6875
 	  This driver can also be built as a module.  If so, the module
 	  will be called max6875.
 
-config RTC_X1205_I2C
-	tristate "Xicor X1205 RTC chip"
-	depends on I2C && EXPERIMENTAL
-	help
-	  If you say yes here you get support for the Xicor X1205 RTC chip.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called x1205.
-
 endmenu

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 04/11] RTC subsystem, sysfs interface
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (2 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 03/11] RTC subsystem, I2C cleanup Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 05/11] RTC subsystem, proc interface Alessandro Zummo
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-intf-sysfs.patch --]
[-- Type: text/plain, Size: 4686 bytes --]

This patch adds the sysfs interface to the
RTC subsystem.

Each RTC client will have his own entry
under /sys/classs/rtc/rtcN .

Within this entry some attributes are
exported by the subsystem, like date and time.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/rtc/Kconfig     |   11 ++++
 drivers/rtc/Makefile    |    2 
 drivers/rtc/rtc-sysfs.c |  120 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+), 1 deletion(-)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-sysfs.c	2006-02-19 23:49:10.000000000 +0100
@@ -0,0 +1,120 @@
+/*
+ * RTC subsystem, sysfs interface
+ *
+ * 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/module.h>
+#include <linux/rtc.h>
+
+/* device attributes */
+
+static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf)
+{
+	return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
+}
+static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL);
+
+static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
+{
+	ssize_t retval;
+	struct rtc_time tm;
+
+	if ((retval = rtc_read_time(dev, &tm)) == 0) {
+		retval = sprintf(buf, "%04d-%02d-%02d\n",
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+	}
+
+	return retval;
+}
+static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL);
+
+static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
+{
+	ssize_t retval;
+	struct rtc_time tm;
+
+	if ((retval = rtc_read_time(dev, &tm)) == 0) {
+		retval = sprintf(buf, "%02d:%02d:%02d\n",
+			tm.tm_hour, tm.tm_min, tm.tm_sec);
+	}
+
+	return retval;
+}
+static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL);
+
+static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
+{
+	ssize_t retval;
+	struct rtc_time tm;
+
+	if ((retval = rtc_read_time(dev, &tm)) == 0) {
+		unsigned long time;
+		rtc_tm_to_time(&tm, &time);
+		retval = sprintf(buf, "%lu\n", time);
+	}
+
+	return retval;
+}
+static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
+
+static struct attribute *rtc_attrs[] = {
+        &class_device_attr_name.attr,
+        &class_device_attr_date.attr,
+        &class_device_attr_time.attr,
+        &class_device_attr_since_epoch.attr,
+        NULL,
+};
+
+static struct attribute_group rtc_attr_group = {
+        .attrs  = rtc_attrs,
+};
+
+static int __devinit rtc_sysfs_add_device(struct class_device *class_dev,
+					   struct class_interface *class_intf)
+{
+	int err;
+
+	dev_info(class_dev->dev, "rtc intf: sysfs\n");
+
+	if ((err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group)) != 0)
+		dev_err(class_dev->dev,
+			"failed to create sysfs attributes\n");
+
+	return err;
+}
+
+static void rtc_sysfs_remove_device(struct class_device *class_dev,
+				      struct class_interface *class_intf)
+{
+	sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
+}
+
+/* interface registration */
+
+struct class_interface rtc_sysfs_interface = {
+	.add = &rtc_sysfs_add_device,
+	.remove = &rtc_sysfs_remove_device,
+};
+
+static int __init rtc_sysfs_init(void)
+{
+	return rtc_interface_register(&rtc_sysfs_interface);
+}
+
+static void __exit rtc_sysfs_exit(void)
+{
+	class_interface_unregister(&rtc_sysfs_interface);
+}
+
+module_init(rtc_sysfs_init);
+module_exit(rtc_sysfs_exit);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC class sysfs interface");
+MODULE_LICENSE("GPL");
--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-19 23:48:02.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-19 23:48:06.000000000 +0100
@@ -36,6 +36,17 @@ config RTC_HCTOSYS_DEVICE
 comment "RTC interfaces"
 	depends on RTC_CLASS
 
+config RTC_INTF_SYSFS
+	tristate "sysfs"
+	depends on RTC_CLASS && SYSFS
+	default RTC_CLASS
+	help
+	  Say yes here if you want to use your RTC using the sysfs
+	  interface, /sys/class/rtc/rtcX .
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-sysfs.
+
 comment "RTC drivers"
 	depends on RTC_CLASS
 
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-19 23:48:02.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-19 23:48:06.000000000 +0100
@@ -6,4 +6,4 @@ 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

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 05/11] RTC subsystem, proc interface
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (3 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 04/11] RTC subsystem, sysfs interface Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 06/11] RTC subsystem, dev interface Alessandro Zummo
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-intf-proc.patch --]
[-- Type: text/plain, Size: 5636 bytes --]

This patch adds the proc interface to the
RTC subsystem.

The first RTC driver which registers with
the class will be accessible by /proc/driver/rtc .

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/rtc/Kconfig    |   11 +++
 drivers/rtc/Makefile   |    1 
 drivers/rtc/rtc-proc.c |  158 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-proc.c	2006-02-15 03:24:03.000000000 +0100
@@ -0,0 +1,158 @@
+/*
+ * RTC subsystem, proc interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * 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/module.h>
+#include <linux/rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static struct class_device *rtc_dev = NULL;
+static DEFINE_MUTEX(rtc_lock);
+
+static int rtc_proc_show(struct seq_file *seq, void *offset)
+{
+	struct class_device *class_dev = seq->private;
+	struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+	struct rtc_wkalrm alrm;
+	struct rtc_time tm;
+
+	if (rtc_read_time(class_dev, &tm) == 0) {
+		seq_printf(seq,
+			"rtc_time\t: %02d:%02d:%02d\n"
+			"rtc_date\t: %04d-%02d-%02d\n",
+			tm.tm_hour, tm.tm_min, tm.tm_sec,
+			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+	}
+
+	if (rtc_read_alarm(class_dev, &alrm) == 0) {
+		seq_printf(seq, "alrm_time\t: ");
+		if ((unsigned int)alrm.time.tm_hour <= 24)
+			seq_printf(seq, "%02d:", alrm.time.tm_hour);
+		else
+			seq_printf(seq, "**:");
+		if ((unsigned int)alrm.time.tm_min <= 59)
+			seq_printf(seq, "%02d:", alrm.time.tm_min);
+		else
+			seq_printf(seq, "**:");
+		if ((unsigned int)alrm.time.tm_sec <= 59)
+			seq_printf(seq, "%02d\n", alrm.time.tm_sec);
+		else
+			seq_printf(seq, "**\n");
+
+		seq_printf(seq, "alrm_date\t: ");
+		if ((unsigned int)alrm.time.tm_year <= 200)
+			seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
+		else
+			seq_printf(seq, "****-");
+		if ((unsigned int)alrm.time.tm_mon <= 11)
+			seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
+		else
+			seq_printf(seq, "**-");
+		if ((unsigned int)alrm.time.tm_mday <= 31)
+			seq_printf(seq, "%02d\n", alrm.time.tm_mday);
+		else
+			seq_printf(seq, "**\n");
+		seq_printf(seq, "alrm_wakeup\t: %s\n",
+			     alrm.enabled ? "yes" : "no");
+		seq_printf(seq, "alrm_pending\t: %s\n",
+			     alrm.pending ? "yes" : "no");
+	}
+
+	if (ops->proc)
+		ops->proc(class_dev->dev, seq);
+
+	return 0;
+}
+
+static int rtc_proc_open(struct inode *inode, struct file *file)
+{
+	struct class_device *class_dev = PDE(inode)->data;
+
+	if (!try_module_get(THIS_MODULE))
+		return -ENODEV;
+
+	return single_open(file, rtc_proc_show, class_dev);
+}
+
+static int rtc_proc_release(struct inode *inode, struct file *file)
+{
+	int res = single_release(inode, file);
+	module_put(THIS_MODULE);
+	return res;
+}
+
+static struct file_operations rtc_proc_fops = {
+	.open		= rtc_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= rtc_proc_release,
+};
+
+static int rtc_proc_add_device(struct class_device *class_dev,
+					   struct class_interface *class_intf)
+{
+	mutex_lock(&rtc_lock);
+	if (rtc_dev == NULL) {
+		struct proc_dir_entry *ent;
+
+		rtc_dev = class_dev;
+
+		if ((ent = create_proc_entry("driver/rtc", 0, NULL))) {
+			struct rtc_device *rtc = to_rtc_device(class_dev);
+
+			ent->proc_fops = &rtc_proc_fops;
+			ent->owner = rtc->owner;
+			ent->data = class_dev;
+
+			dev_info(class_dev->dev, "rtc intf: proc\n");
+		}
+		else
+			rtc_dev = NULL;
+	}
+	mutex_unlock(&rtc_lock);
+
+	return 0;
+}
+
+static void rtc_proc_remove_device(struct class_device *class_dev,
+					      struct class_interface *class_intf)
+{
+	mutex_lock(&rtc_lock);
+	if (rtc_dev == class_dev) {
+		remove_proc_entry("driver/rtc", NULL);
+		rtc_dev = NULL;
+	}
+	mutex_unlock(&rtc_lock);
+}
+
+struct class_interface rtc_proc_interface = {
+	.add = &rtc_proc_add_device,
+	.remove = &rtc_proc_remove_device,
+};
+
+static int __init rtc_proc_init(void)
+{
+	return rtc_interface_register(&rtc_proc_interface);
+}
+
+static void __exit rtc_proc_exit(void)
+{
+	class_interface_unregister(&rtc_proc_interface);
+}
+
+module_init(rtc_proc_init);
+module_exit(rtc_proc_exit);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC class proc interface");
+MODULE_LICENSE("GPL");
--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-15 01:26:06.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-15 03:20:53.000000000 +0100
@@ -47,6 +47,17 @@ config RTC_INTF_SYSFS
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-sysfs.
 
+config RTC_INTF_PROC
+	tristate "proc"
+	depends on RTC_CLASS && PROC_FS
+	default RTC_CLASS
+	help
+	  Say yes here if you want to use your RTC using the proc
+	  interface, /proc/driver/rtc .
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-proc.
+
 comment "RTC drivers"
 	depends on RTC_CLASS
 
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-15 01:26:06.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-15 03:20:53.000000000 +0100
@@ -7,3 +7,4 @@ 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
+obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 06/11] RTC subsystem, dev interface
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (4 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 05/11] RTC subsystem, proc interface Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 07/11] RTC subsystem, X1205 driver Alessandro Zummo
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-intf-dev.patch --]
[-- Type: text/plain, Size: 10281 bytes --]

This patch adds the dev interface to the RTC
subsystem.

Each RTC will be available under /dev/rtcX . A
symlink from /dev/rtc0 to /dev/rtc cab be obtained
with the following udev rule:

KERNEL=="rtc0", SYMLINK+="rtc"

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/rtc/Kconfig   |   11 +
 drivers/rtc/Makefile  |    1 
 drivers/rtc/rtc-dev.c |  370 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 382 insertions(+)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-dev.c	2006-02-14 11:37:15.000000000 +0100
@@ -0,0 +1,370 @@
+/*
+ * RTC subsystem, dev interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * 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/module.h>
+#include <linux/rtc.h>
+
+static dev_t rtc_devt;
+
+#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */
+
+static int rtc_dev_open(struct inode *inode, struct file *file)
+{
+	int err;
+	struct rtc_device *rtc = container_of(inode->i_cdev,
+					struct rtc_device, char_dev);
+	struct rtc_class_ops *ops = rtc->ops;
+
+	/* We keep the lock as long as the device is in use
+	 * and return immediately if busy
+	 */
+	if (!(mutex_trylock(&rtc->char_lock)))
+		return -EBUSY;
+
+	file->private_data = &rtc->class_dev;
+
+	err = ops->open ? ops->open(rtc->class_dev.dev) : 0;
+	if (err == 0) {
+
+		spin_lock_irq(&rtc->irq_lock);
+		rtc->irq_data = 0;
+		spin_unlock_irq(&rtc->irq_lock);
+
+		return 0;
+	}
+
+	/* something has gone wrong, release the lock */
+	mutex_unlock(&rtc->char_lock);
+	return err;
+}
+
+
+static ssize_t
+rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct rtc_device *rtc = to_rtc_device(file->private_data);
+
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t ret;
+
+	if (count < sizeof(unsigned long))
+		return -EINVAL;
+
+	add_wait_queue(&rtc->irq_queue, &wait);
+	do {
+		__set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irq(&rtc->irq_lock);
+		data = rtc->irq_data;
+		rtc->irq_data = 0;
+		spin_unlock_irq(&rtc->irq_lock);
+
+		if (data != 0) {
+			ret = 0;
+			break;
+		}
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	} while (1);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rtc->irq_queue, &wait);
+
+	if (ret == 0) {
+		/* Check for any data updates */
+		if (rtc->ops->read_callback)
+			data = rtc->ops->read_callback(rtc->class_dev.dev, data);
+
+		ret = put_user(data, (unsigned long __user *)buf);
+		if (ret == 0)
+			ret = sizeof(unsigned long);
+	}
+	return ret;
+}
+
+static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
+{
+	struct rtc_device *rtc = to_rtc_device(file->private_data);
+	unsigned long data;
+
+	poll_wait(file, &rtc->irq_queue, wait);
+
+	data = rtc->irq_data;
+
+	return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
+}
+
+static int rtc_dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		     unsigned long arg)
+{
+	int err = 0;
+	struct class_device *class_dev = file->private_data;
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+	struct rtc_class_ops *ops = rtc->ops;
+	struct rtc_time tm;
+	struct rtc_wkalrm alarm;
+	void __user *uarg = (void __user *) arg;
+
+	/* avoid conflicting IRQ users */
+	if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
+		spin_lock(&rtc->irq_task_lock);
+		if (rtc->irq_task)
+			err = -EBUSY;
+		spin_unlock(&rtc->irq_task_lock);
+
+		if (err < 0)
+			return err;
+	}
+
+	/* try the driver's ioctl interface */
+	if (ops->ioctl) {
+		err = ops->ioctl(class_dev->dev, cmd, arg);
+		if (err != -EINVAL)
+			return err;
+	}
+
+	/* if the driver does not provide the ioctl interface
+	 * or if that particular ioctl was not implemented
+	 * (-EINVAL), we will try to emulate here.
+	 */
+
+	switch (cmd) {
+	case RTC_ALM_READ:
+		if ((err = rtc_read_alarm(class_dev, &alarm)) < 0)
+			return err;
+
+		if ((err = copy_to_user(uarg, &alarm.time, sizeof(tm))))
+			return -EFAULT;
+		break;
+
+	case RTC_ALM_SET:
+		if ((err = copy_from_user(&alarm.time, uarg, sizeof(tm))))
+			return -EFAULT;
+
+		alarm.enabled = 0;
+		alarm.pending = 0;
+		alarm.time.tm_mday = -1;
+		alarm.time.tm_mon = -1;
+		alarm.time.tm_year = -1;
+		alarm.time.tm_wday = -1;
+		alarm.time.tm_yday = -1;
+		alarm.time.tm_isdst = -1;
+		err = rtc_set_alarm(class_dev, &alarm);
+		break;
+
+	case RTC_RD_TIME:
+		if ((err = rtc_read_time(class_dev, &tm)) < 0)
+			return err;
+
+		if ((err = copy_to_user(uarg, &tm, sizeof(tm))))
+			return -EFAULT;
+		break;
+
+	case RTC_SET_TIME:
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+
+		if ((err = copy_from_user(&tm, uarg, sizeof(tm))))
+			return -EFAULT;
+
+		err = rtc_set_time(class_dev, &tm);
+		break;
+#if 0
+	case RTC_EPOCH_SET:
+#ifndef rtc_epoch
+		/*
+		 * There were no RTC clocks before 1900.
+		 */
+		if (arg < 1900) {
+			err = -EINVAL;
+			break;
+		}
+		if (!capable(CAP_SYS_TIME)) {
+			err = -EACCES;
+			break;
+		}
+		rtc_epoch = arg;
+		err = 0;
+#endif
+		break;
+
+	case RTC_EPOCH_READ:
+		err = put_user(rtc_epoch, (unsigned long __user *)uarg);
+		break;
+#endif
+	case RTC_WKALM_SET:
+		if ((err = copy_from_user(&alarm, uarg, sizeof(alarm))))
+			return -EFAULT;
+
+		err = rtc_set_alarm(class_dev, &alarm);
+		break;
+
+	case RTC_WKALM_RD:
+		if ((err = rtc_read_alarm(class_dev, &alarm)) < 0)
+			return err;
+
+		if ((err = copy_to_user(uarg, &alarm, sizeof(alarm))))
+			return -EFAULT;
+		break;
+
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static int rtc_dev_release(struct inode *inode, struct file *file)
+{
+	struct rtc_device *rtc = to_rtc_device(file->private_data);
+
+	if (rtc->ops->release)
+		rtc->ops->release(rtc->class_dev.dev);
+
+	mutex_unlock(&rtc->char_lock);
+	return 0;
+}
+
+static int rtc_dev_fasync(int fd, struct file *file, int on)
+{
+	struct rtc_device *rtc = to_rtc_device(file->private_data);
+	return fasync_helper(fd, file, on, &rtc->async_queue);
+}
+
+static struct file_operations rtc_dev_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= rtc_dev_read,
+	.poll		= rtc_dev_poll,
+	.ioctl		= rtc_dev_ioctl,
+	.open		= rtc_dev_open,
+	.release	= rtc_dev_release,
+	.fasync		= rtc_dev_fasync,
+};
+
+static ssize_t rtc_dev_show_dev(struct class_device *class_dev, char *buf)
+{
+        return print_dev_t(buf, class_dev->devt);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, rtc_dev_show_dev, NULL);
+
+/* insertion/removal hooks */
+
+static int rtc_dev_add_device(struct class_device *class_dev,
+				struct class_interface *class_intf)
+{
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	if (rtc->id >= RTC_DEV_MAX) {
+		dev_err(class_dev->dev, "too many RTCs\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&rtc->char_lock);
+	spin_lock_init(&rtc->irq_lock);
+	init_waitqueue_head(&rtc->irq_queue);
+
+	cdev_init(&rtc->char_dev, &rtc_dev_fops);
+	rtc->char_dev.owner = rtc->owner;
+	class_dev->devt = MKDEV(MAJOR(rtc_devt), rtc->id);
+
+	if (cdev_add(&rtc->char_dev, class_dev->devt, 1)) {
+		cdev_del(&rtc->char_dev);
+
+		dev_err(class_dev->dev,
+			"failed to add char device %d:%d\n",
+			MAJOR(class_dev->devt),
+			MINOR(class_dev->devt));
+
+		class_dev->devt = MKDEV(0, 0);
+		return -ENODEV;
+	}
+
+	class_device_create_file(class_dev, &class_device_attr_dev);
+
+	dev_info(class_dev->dev, "rtc intf: dev (%d:%d)\n",
+		MAJOR(class_dev->devt),
+		MINOR(class_dev->devt));
+
+	kobject_uevent(&class_dev->kobj, KOBJ_ADD);
+
+	return 0;
+}
+
+static void rtc_dev_remove_device(struct class_device *class_dev,
+					struct class_interface *class_intf)
+{
+	struct rtc_device *rtc = to_rtc_device(class_dev);
+
+	class_device_remove_file(class_dev, &class_device_attr_dev);
+
+	if (MAJOR(class_dev->devt)) {
+		dev_dbg(class_dev->dev, "removing char %d:%d\n",
+			MAJOR(class_dev->devt),
+			MINOR(class_dev->devt));
+		cdev_del(&rtc->char_dev);
+
+		kobject_uevent(&class_dev->kobj, KOBJ_REMOVE);
+
+		class_dev->devt = MKDEV(0, 0);
+	}
+}
+
+/* interface registration */
+
+struct class_interface rtc_dev_interface = {
+	.add = &rtc_dev_add_device,
+	.remove = &rtc_dev_remove_device,
+};
+
+static int __init rtc_dev_init(void)
+{
+	int err;
+
+	if ((err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc")) < 0) {
+		printk(KERN_ERR "%s: failed to allocate char dev region\n",
+			__FILE__);
+		return err;
+	}
+
+	if ((err = rtc_interface_register(&rtc_dev_interface)) < 0) {
+		printk(KERN_ERR "%s: failed to register the interface\n",
+			__FILE__);
+		unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit rtc_dev_exit(void)
+{
+	class_interface_unregister(&rtc_dev_interface);
+
+	unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
+}
+
+module_init(rtc_dev_init);
+module_exit(rtc_dev_exit);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC class dev interface");
+MODULE_LICENSE("GPL");
--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-14 11:34:49.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-14 11:34:50.000000000 +0100
@@ -58,6 +58,17 @@ config RTC_INTF_PROC
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-proc.
 
+config RTC_INTF_DEV
+	tristate "dev"
+	depends on RTC_CLASS
+	default RTC_CLASS
+	help
+	  Say yes here if you want to use your RTC using the dev
+	  interface, /dev/rtc .
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-dev.
+
 comment "RTC drivers"
 	depends on RTC_CLASS
 
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-14 11:34:49.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-14 11:34:50.000000000 +0100
@@ -8,3 +8,4 @@ obj-$(CONFIG_RTC_CLASS)		+= rtc-core.o
 rtc-core-y			:= class.o interface.o
 obj-$(CONFIG_RTC_INTF_SYSFS)	+= rtc-sysfs.o
 obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o
+obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 07/11] RTC subsystem, X1205 driver
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (5 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 06/11] RTC subsystem, dev interface Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 08/11] RTC subsystem, test device/driver Alessandro Zummo
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-x1205.patch --]
[-- Type: text/plain, Size: 17815 bytes --]

This patch is a port of the existing x1205
driver under the new RTC subsystem.

It is actually under test within the NSLU2
project (http://www.nslu2-linux.org) and
it is working quite well.

It is the first driver under this new subsystem
and should be used as a guide to port other
drivers.

Ideally, at some point in the future, all
RTC drivers will reside under linux/rtc.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/rtc/Kconfig     |   10 
 drivers/rtc/Makefile    |    3 
 drivers/rtc/rtc-x1205.c |  619 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 632 insertions(+)

--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-19 23:33:00.000000000 +0100
@@ -72,4 +72,14 @@ config RTC_INTF_DEV
 comment "RTC drivers"
 	depends on RTC_CLASS
 
+config RTC_DRV_X1205
+	tristate "Xicor/Intersil X1205 RTC chip"
+	depends on RTC_CLASS && I2C
+	help
+	  If you say yes here you get support for the
+	  Xicor/Intersil X1205 RTC chip.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-x1205.
+
 endmenu
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-19 23:33:00.000000000 +0100
@@ -9,3 +9,6 @@ rtc-core-y			:= class.o interface.o
 obj-$(CONFIG_RTC_INTF_SYSFS)	+= rtc-sysfs.o
 obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.o
 obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
+
+obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
+
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-x1205.c	2006-02-15 04:26:21.000000000 +0100
@@ -0,0 +1,619 @@
+/*
+ * An i2c driver for the Xicor/Intersil X1205 RTC
+ * Copyright 2004 Karen Spearel
+ * Copyright 2005 Alessandro Zummo
+ *
+ * please send all reports to:
+ * 	Karen Spearel <kas111 at gmail dot com>
+ *	Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on a lot of other RTC drivers.
+ *
+ * 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/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#define DRV_VERSION "1.0.6"
+
+/* Addresses to scan: none. This chip is located at
+ * 0x6f and uses a two bytes register addressing.
+ * Two bytes need to be written to read a single register,
+ * while most other chips just require one and take the second
+ * one as the data to be written. To prevent corrupting
+ * unknown chips, the user must explicitely set the probe parameter.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+
+/* offsets into CCR area */
+
+#define CCR_SEC			0
+#define CCR_MIN			1
+#define CCR_HOUR		2
+#define CCR_MDAY		3
+#define CCR_MONTH		4
+#define CCR_YEAR		5
+#define CCR_WDAY		6
+#define CCR_Y2K			7
+
+#define X1205_REG_SR		0x3F	/* status register */
+#define X1205_REG_Y2K		0x37
+#define X1205_REG_DW		0x36
+#define X1205_REG_YR		0x35
+#define X1205_REG_MO		0x34
+#define X1205_REG_DT		0x33
+#define X1205_REG_HR		0x32
+#define X1205_REG_MN		0x31
+#define X1205_REG_SC		0x30
+#define X1205_REG_DTR		0x13
+#define X1205_REG_ATR		0x12
+#define X1205_REG_INT		0x11
+#define X1205_REG_0		0x10
+#define X1205_REG_Y2K1		0x0F
+#define X1205_REG_DWA1		0x0E
+#define X1205_REG_YRA1		0x0D
+#define X1205_REG_MOA1		0x0C
+#define X1205_REG_DTA1		0x0B
+#define X1205_REG_HRA1		0x0A
+#define X1205_REG_MNA1		0x09
+#define X1205_REG_SCA1		0x08
+#define X1205_REG_Y2K0		0x07
+#define X1205_REG_DWA0		0x06
+#define X1205_REG_YRA0		0x05
+#define X1205_REG_MOA0		0x04
+#define X1205_REG_DTA0		0x03
+#define X1205_REG_HRA0		0x02
+#define X1205_REG_MNA0		0x01
+#define X1205_REG_SCA0		0x00
+
+#define X1205_CCR_BASE		0x30	/* Base address of CCR */
+#define X1205_ALM0_BASE		0x00	/* Base address of ALARM0 */
+
+#define X1205_SR_RTCF		0x01	/* Clock failure */
+#define X1205_SR_WEL		0x02	/* Write Enable Latch */
+#define X1205_SR_RWEL		0x04	/* Register Write Enable */
+
+#define X1205_DTR_DTR0		0x01
+#define X1205_DTR_DTR1		0x02
+#define X1205_DTR_DTR2		0x04
+
+#define X1205_HR_MIL		0x80	/* Set in ccr.hour for 24 hr mode */
+
+/* Prototypes */
+static int x1205_attach(struct i2c_adapter *adapter);
+static int x1205_detach(struct i2c_client *client);
+static int x1205_probe(struct i2c_adapter *adapter, int address, int kind);
+
+static struct i2c_driver x1205_driver = {
+	.driver		= {
+		.name	= "x1205",
+	},
+	.attach_adapter = &x1205_attach,
+	.detach_client	= &x1205_detach,
+};
+
+/*
+ * In the routines that deal directly with the x1205 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
+				unsigned char reg_base)
+{
+	unsigned char dt_addr[2] = { 0, reg_base };
+
+	unsigned char buf[8];
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 2, dt_addr },	/* setup read ptr */
+		{ client->addr, I2C_M_RD, 8, buf },	/* read date */
+	};
+
+	/* read date registers */
+	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	dev_dbg(&client->dev,
+		"%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
+		"mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
+		__FUNCTION__,
+		buf[0], buf[1], buf[2], buf[3],
+		buf[4], buf[5], buf[6], buf[7]);
+
+	tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
+	tm->tm_min = BCD2BIN(buf[CCR_MIN]);
+	tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
+	tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
+	tm->tm_mon = BCD2BIN(buf[CCR_MONTH]) - 1; /* mon is 0-11 */
+	tm->tm_year = BCD2BIN(buf[CCR_YEAR])
+			+ (BCD2BIN(buf[CCR_Y2K]) * 100) - 1900;
+	tm->tm_wday = buf[CCR_WDAY];
+
+	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);
+
+	return 0;
+}
+
+static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
+{
+	static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 2, sr_addr },	/* setup read ptr */
+		{ client->addr, I2C_M_RD, 1, sr },	/* read status */
+	};
+
+	/* read status register */
+	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
+				int datetoo, u8 reg_base)
+{
+	int i, xfer;
+	unsigned char buf[8];
+
+	static const unsigned char wel[3] = { 0, X1205_REG_SR,
+						X1205_SR_WEL };
+
+	static const unsigned char rwel[3] = { 0, X1205_REG_SR,
+						X1205_SR_WEL | X1205_SR_RWEL };
+
+	static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
+
+	dev_dbg(&client->dev,
+		"%s: secs=%d, mins=%d, hours=%d\n",
+		__FUNCTION__,
+		tm->tm_sec, tm->tm_min, tm->tm_hour);
+
+	buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
+	buf[CCR_MIN] = BIN2BCD(tm->tm_min);
+
+	/* set hour and 24hr bit */
+	buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL;
+
+	/* should we also set the date? */
+	if (datetoo) {
+		dev_dbg(&client->dev,
+			"%s: mday=%d, mon=%d, year=%d, wday=%d\n",
+			__FUNCTION__,
+			tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+		buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
+
+		/* month, 1 - 12 */
+		buf[CCR_MONTH] = BIN2BCD(tm->tm_mon + 1);
+
+		/* year, since the rtc epoch*/
+		buf[CCR_YEAR] = BIN2BCD(tm->tm_year % 100);
+		buf[CCR_WDAY] = tm->tm_wday & 0x07;
+		buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100);
+	}
+
+	/* this sequence is required to unlock the chip */
+	if ((xfer = i2c_master_send(client, wel, 3)) != 3) {
+		dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer);
+		return -EIO;
+	}
+
+	if ((xfer = i2c_master_send(client, rwel, 3)) != 3) {
+		dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer);
+		return -EIO;
+	}
+
+	/* write register's data */
+	for (i = 0; i < (datetoo ? 8 : 3); i++) {
+		unsigned char rdata[3] = { 0, reg_base + i, buf[i] };
+
+		xfer = i2c_master_send(client, rdata, 3);
+		if (xfer != 3) {
+			dev_err(&client->dev,
+				"%s: xfer=%d addr=%02x, data=%02x\n",
+				__FUNCTION__,
+				 xfer, rdata[1], rdata[2]);
+			return -EIO;
+		}
+	};
+
+	/* disable further writes */
+	if ((xfer = i2c_master_send(client, diswe, 3)) != 3) {
+		dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int x1205_fix_osc(struct i2c_client *client)
+{
+	int err;
+	struct rtc_time tm;
+
+	tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+
+	if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0)
+		dev_err(&client->dev,
+			"unable to restart the oscillator\n");
+
+	return err;
+}
+
+static int x1205_get_dtrim(struct i2c_client *client, int *trim)
+{
+	unsigned char dtr;
+	static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 2, dtr_addr },	/* setup read ptr */
+		{ client->addr, I2C_M_RD, 1, &dtr }, 	/* read dtr */
+	};
+
+	/* read dtr register */
+	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr);
+
+	*trim = 0;
+
+	if (dtr & X1205_DTR_DTR0)
+		*trim += 20;
+
+	if (dtr & X1205_DTR_DTR1)
+		*trim += 10;
+
+	if (dtr & X1205_DTR_DTR2)
+		*trim = -*trim;
+
+	return 0;
+}
+
+static int x1205_get_atrim(struct i2c_client *client, int *trim)
+{
+	s8 atr;
+	static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 2, atr_addr },	/* setup read ptr */
+		{ client->addr, I2C_M_RD, 1, &atr }, 	/* read atr */
+	};
+
+	/* read atr register */
+	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr);
+
+	/* atr is a two's complement value on 6 bits,
+	 * perform sign extension. The formula is
+	 * Catr = (atr * 0.25pF) + 11.00pF.
+	 */
+	if (atr & 0x20)
+		atr |= 0xC0;
+
+	dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr);
+
+	*trim = (atr * 250) + 11000;
+
+	dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim);
+
+	return 0;
+}
+
+struct x1205_limit
+{
+	unsigned char reg, mask, min, max;
+};
+
+static int x1205_validate_client(struct i2c_client *client)
+{
+	int i, xfer;
+
+	/* Probe array. We will read the register at the specified
+	 * address and check if the given bits are zero.
+	 */
+	static const unsigned char probe_zero_pattern[] = {
+		/* register, mask */
+		X1205_REG_SR,	0x18,
+		X1205_REG_DTR,	0xF8,
+		X1205_REG_ATR,	0xC0,
+		X1205_REG_INT,	0x18,
+		X1205_REG_0,	0xFF,
+	};
+
+	static const struct x1205_limit probe_limits_pattern[] = {
+		/* register, mask, min, max */
+		{ X1205_REG_Y2K,	0xFF,	19,	20	},
+		{ X1205_REG_DW,		0xFF,	0,	6	},
+		{ X1205_REG_YR,		0xFF,	0,	99	},
+		{ X1205_REG_MO,		0xFF,	0,	12	},
+		{ X1205_REG_DT,		0xFF,	0,	31	},
+		{ X1205_REG_HR,		0x7F,	0,	23	},
+		{ X1205_REG_MN,		0xFF,	0,	59	},
+		{ X1205_REG_SC,		0xFF,	0,	59	},
+		{ X1205_REG_Y2K1,	0xFF,	19,	20	},
+		{ X1205_REG_Y2K0,	0xFF,	19,	20	},
+	};
+
+	/* check that registers have bits a 0 where expected */
+	for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {
+		unsigned char buf;
+
+		unsigned char addr[2] = { 0, probe_zero_pattern[i] };
+
+		struct i2c_msg msgs[2] = {
+			{ client->addr, 0, 2, addr },
+			{ client->addr, I2C_M_RD, 1, &buf },
+		};
+
+		if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
+			dev_err(&client->adapter->dev,
+				"%s: could not read register %x\n",
+				__FUNCTION__,  probe_zero_pattern[i]);
+
+			return -EIO;
+		}
+
+		if ((buf & probe_zero_pattern[i+1]) != 0) {
+			dev_err(&client->adapter->dev,
+				"%s: register=%02x, zero pattern=%d, value=%x\n",
+				__FUNCTION__, probe_zero_pattern[i], i, buf);
+
+			return -ENODEV;
+		}
+	}
+
+	/* check limits (only registers with bcd values) */
+	for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
+		unsigned char reg, value;
+
+		unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
+
+		struct i2c_msg msgs[2] = {
+			{ client->addr, 0, 2, addr },
+			{ client->addr, I2C_M_RD, 1, &reg },
+		};
+
+		if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
+			dev_err(&client->adapter->dev,
+				"%s: could not read register %x\n",
+				__FUNCTION__, probe_limits_pattern[i].reg);
+
+			return -EIO;
+		}
+
+		value = BCD2BIN(reg & 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 x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	return x1205_get_datetime(to_i2c_client(dev),
+		&alrm->time, X1205_ALM0_BASE);
+}
+
+static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+	return x1205_set_datetime(to_i2c_client(dev),
+		&alrm->time, 1, X1205_ALM0_BASE);
+}
+
+static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return x1205_get_datetime(to_i2c_client(dev),
+		tm, X1205_CCR_BASE);
+}
+
+static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return x1205_set_datetime(to_i2c_client(dev),
+		tm, 1, X1205_CCR_BASE);
+}
+
+static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	int err, dtrim, atrim;
+
+	seq_printf(seq, "24hr\t\t: yes\n");
+
+	if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0)
+		seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim);
+
+	if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0)
+		seq_printf(seq, "analog_trim\t: %d.%02d pF\n",
+			atrim / 1000, atrim % 1000);
+	return 0;
+}
+
+static struct rtc_class_ops x1205_rtc_ops = {
+	.proc		= x1205_rtc_proc,
+	.read_time	= x1205_rtc_read_time,
+	.set_time	= x1205_rtc_set_time,
+	.read_alarm	= x1205_rtc_read_alarm,
+	.set_alarm	= x1205_rtc_set_alarm,
+};
+
+static ssize_t x1205_sysfs_show_atrim(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int atrim;
+
+	if (x1205_get_atrim(to_i2c_client(dev), &atrim) == 0)
+		return sprintf(buf, "%d.%02d pF\n",
+			atrim / 1000, atrim % 1000);
+	return 0;
+}
+static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL);
+
+static ssize_t x1205_sysfs_show_dtrim(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int dtrim;
+
+	if (x1205_get_dtrim(to_i2c_client(dev), &dtrim) == 0)
+		return sprintf(buf, "%d ppm\n", dtrim);
+
+	return 0;
+}
+static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL);
+
+static int x1205_attach(struct i2c_adapter *adapter)
+{
+	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+	return i2c_probe(adapter, &addr_data, x1205_probe);
+}
+
+static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+	int err = 0;
+	unsigned char sr;
+	struct i2c_client *client;
+	struct rtc_device *rtc;
+
+	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;
+	}
+
+	/* I2C client */
+	client->addr = address;
+	client->driver = &x1205_driver;
+	client->adapter	= adapter;
+
+	strlcpy(client->name, x1205_driver.driver.name, I2C_NAME_SIZE);
+
+	/* Verify the chip is really an X1205 */
+	if (kind < 0) {
+		if (x1205_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(x1205_driver.driver.name, &client->dev,
+				&x1205_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);
+
+	/* Check for power failures and eventualy enable the osc */
+	if ((err = x1205_get_status(client, &sr)) == 0) {
+		if (sr & X1205_SR_RTCF) {
+			dev_err(&client->dev,
+				"power failure detected, "
+				"please set the clock\n");
+			udelay(50);
+			x1205_fix_osc(client);
+		}
+	}
+	else
+		dev_err(&client->dev, "couldn't read status\n");
+
+	device_create_file(&client->dev, &dev_attr_atrim);
+	device_create_file(&client->dev, &dev_attr_dtrim);
+
+	return 0;
+
+exit_detach:
+	i2c_detach_client(client);
+
+exit_kfree:
+	kfree(client);
+
+exit:
+	return err;
+}
+
+static int x1205_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 x1205_init(void)
+{
+	return i2c_add_driver(&x1205_driver);
+}
+
+static void __exit x1205_exit(void)
+{
+	i2c_del_driver(&x1205_driver);
+}
+
+MODULE_AUTHOR(
+	"Karen Spearel <kas111 at gmail dot com>, "
+	"Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(x1205_init);
+module_exit(x1205_exit);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 08/11] RTC subsystem, test device/driver
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (6 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 07/11] RTC subsystem, X1205 driver Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 09/11] RTC subsystem, DS1672 driver Alessandro Zummo
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-test.patch --]
[-- Type: text/plain, Size: 6391 bytes --]

Interrupts can be generated by
echo "alarm|tick|update" >/sys/class/rtc/rtcX/device/irq

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--

 drivers/rtc/Kconfig    |   15 +++
 drivers/rtc/Makefile   |    1 
 drivers/rtc/rtc-test.c |  206 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 222 insertions(+)

--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-13 19:35:30.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-13 19:39:51.000000000 +0100
@@ -82,4 +82,19 @@ config RTC_DRV_X1205
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-x1205.
 
+config RTC_DRV_TEST
+	tristate "Test driver/device"
+	depends on RTC_CLASS
+	help
+	  If you say yes here you get support for the
+	  RTC test driver. It's a software RTC which can be
+	  used to test the RTC subsystem APIs. It gets
+	  the time from the system clock.
+	  You want this driver only if you are doing development
+	  on the RTC subsystem. Please read the source code
+	  for further details.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-test.
+
 endmenu
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-13 19:35:30.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-13 19:39:51.000000000 +0100
@@ -11,4 +11,5 @@ obj-$(CONFIG_RTC_INTF_PROC)	+= rtc-proc.
 obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-test.c	2006-02-13 19:35:30.000000000 +0100
@@ -0,0 +1,206 @@
+/*
+ * An RTC test device/driver
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+struct platform_device *test0 = NULL, *test1 = NULL;
+
+
+static int test_rtc_read_alarm(struct device *dev,
+	struct rtc_wkalrm *alrm)
+{
+	return 0;
+}
+
+static int test_rtc_set_alarm(struct device *dev,
+	struct rtc_wkalrm *alrm)
+{
+	return 0;
+}
+
+static int test_rtc_read_time(struct device *dev,
+	struct rtc_time *tm)
+{
+	rtc_time_to_tm(get_seconds(), tm);
+	return 0;
+}
+
+static int test_rtc_set_time(struct device *dev,
+	struct rtc_time *tm)
+{
+	return 0;
+}
+
+static int test_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	return 0;
+}
+
+static int test_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	struct platform_device *plat_dev = to_platform_device(dev);
+
+	seq_printf(seq, "24hr\t\t: yes\n");
+	seq_printf(seq, "test\t\t: yes\n");
+	seq_printf(seq, "id\t\t: %d\n", plat_dev->id);
+
+	return 0;
+}
+
+static int test_rtc_ioctl(struct device *dev, unsigned int cmd,
+	unsigned long arg)
+{
+	/* We do support interrupts, they're generated
+	 * using the sysfs interface.
+	 */
+	switch (cmd) {
+	case RTC_PIE_ON:
+	case RTC_PIE_OFF:
+	case RTC_UIE_ON:
+	case RTC_UIE_OFF:
+	case RTC_AIE_ON:
+	case RTC_AIE_OFF:
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct rtc_class_ops test_rtc_ops = {
+	.proc = test_rtc_proc,
+	.read_time = test_rtc_read_time,
+	.set_time = test_rtc_set_time,
+	.read_alarm = test_rtc_read_alarm,
+	.set_alarm = test_rtc_set_alarm,
+	.set_mmss = test_rtc_set_mmss,
+	.ioctl = test_rtc_ioctl,
+};
+
+static ssize_t test_irq_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", 42);
+}
+static ssize_t test_irq_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int retval;
+	struct platform_device *plat_dev = to_platform_device(dev);
+	struct rtc_device *rtc = platform_get_drvdata(plat_dev);
+
+	retval = count;
+	if (strncmp(buf, "tick", 4) == 0)
+		rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
+	else if (strncmp(buf, "alarm", 5) == 0)
+		rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF);
+	else if (strncmp(buf, "update", 6) == 0)
+		rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
+	else
+		retval = -EINVAL;
+
+	return retval;
+}
+static DEVICE_ATTR(irq, S_IRUGO | S_IWUSR, test_irq_show, test_irq_store);
+
+static int test_probe(struct platform_device *plat_dev)
+{
+	int err;
+	struct rtc_device *rtc = rtc_device_register("test", &plat_dev->dev,
+						&test_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc)) {
+		err = PTR_ERR(rtc);
+		dev_err(&plat_dev->dev,
+			"unable to register the class device\n");
+		return err;
+	}
+	device_create_file(&plat_dev->dev, &dev_attr_irq);
+
+	platform_set_drvdata(plat_dev, rtc);
+
+	return 0;
+}
+
+static int __devexit test_remove(struct platform_device *plat_dev)
+{
+	struct rtc_device *rtc = platform_get_drvdata(plat_dev);
+
+	rtc_device_unregister(rtc);
+	device_remove_file(&plat_dev->dev, &dev_attr_irq);
+
+	return 0;
+}
+
+static struct platform_driver test_drv = {
+	.probe	= test_probe,
+	.remove = __devexit_p(test_remove),
+	.driver = {
+		.name = "rtc-test",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init test_init(void)
+{
+	int err;
+
+	if ((err = platform_driver_register(&test_drv)))
+		return err;
+
+	if ((test0 = platform_device_alloc("rtc-test", 0)) == NULL) {
+		err = -ENOMEM;
+		goto exit_driver_unregister;
+	}
+
+	if ((test1 = platform_device_alloc("rtc-test", 1)) == NULL) {
+		err = -ENOMEM;
+		goto exit_free_test0;
+	}
+
+	if ((err = platform_device_add(test0)))
+		goto exit_free_test1;
+
+	if ((err = platform_device_add(test1)))
+		goto exit_device_unregister;
+
+	return 0;
+
+exit_device_unregister:
+	platform_device_unregister(test0);
+
+exit_free_test1:
+	platform_device_put(test1);
+
+exit_free_test0:
+	platform_device_put(test0);
+
+exit_driver_unregister:
+	platform_driver_unregister(&test_drv);
+	return err;
+}
+
+static void __exit test_exit(void)
+{
+	platform_device_unregister(test0);
+	platform_device_unregister(test1);
+	platform_driver_unregister(&test_drv);
+}
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC test driver/device");
+MODULE_LICENSE("GPL");
+
+module_init(test_init);
+module_exit(test_exit);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 09/11] RTC subsystem, DS1672 driver
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (7 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 08/11] RTC subsystem, test device/driver Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 10/11] RTC subsystem, PCF8563 driver Alessandro Zummo
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-ds1672.patch --]
[-- Type: text/plain, Size: 7134 bytes --]

Driver for the Dallas/Maxim DS1672 chip, found
on the Loft (http://www.giantshoulderinc.com) .


Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--
 drivers/rtc/Kconfig      |   10 ++
 drivers/rtc/Makefile     |    1 
 drivers/rtc/rtc-ds1672.c |  233 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 244 insertions(+)

--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-15 04:14:38.000000000 +0100
@@ -82,6 +82,16 @@ config RTC_DRV_X1205
 	  This driver can also be built as a module. If so, the module
 	  will be called rtc-x1205.
 
+config RTC_DRV_DS1672
+	tristate "Dallas/Maxim DS1672"
+	depends on RTC_CLASS && I2C
+	help
+	  If you say yes here you get support for the
+	  Dallas/Maxim DS1672 timekeeping chip.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-ds1672.
+
 config RTC_DRV_TEST
 	tristate "Test driver/device"
 	depends on RTC_CLASS
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-15 04:14:38.000000000 +0100
@@ -12,4 +12,5 @@ obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
+obj-$(CONFIG_RTC_DRV_DS1672)	+= rtc-ds1672.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-ds1672.c	2006-02-15 04:15:08.000000000 +0100
@@ -0,0 +1,233 @@
+/*
+ * An rtc/i2c driver for the Dallas DS1672
+ * Copyright 2005 Alessandro Zummo
+ *
+ * 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/rtc.h>
+
+#define DRV_VERSION "0.2"
+
+/* Addresses to scan: none. This chip cannot be detected. */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+
+/* Registers */
+
+#define DS1672_REG_CNT_BASE	0
+#define DS1672_REG_CONTROL	4
+#define DS1672_REG_TRICKLE	5
+
+
+/* Prototypes */
+static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind);
+
+/*
+ * In the routines that deal directly with the ds1672 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	unsigned long time;
+	unsigned char addr = DS1672_REG_CNT_BASE;
+	unsigned char buf[4];
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 1, &addr },		/* setup read ptr */
+		{ client->addr, I2C_M_RD, 4, buf },	/* read date */
+	};
+
+        /* read date registers */
+        if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+                dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+                return -EIO;
+        }
+
+	dev_dbg(&client->dev,
+		"%s: raw read data - counters=%02x,%02x,%02x,%02x\n"
+		__FUNCTION__,
+		buf[0], buf[1], buf[2], buf[3]);
+
+	time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+	rtc_time_to_tm(time, tm);
+
+	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);
+
+	return 0;
+}
+
+static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
+{
+	int xfer;
+	unsigned char buf[5];
+
+	buf[0] = DS1672_REG_CNT_BASE;
+	buf[1] = secs & 0x000000FF;
+	buf[2] = (secs & 0x0000FF00) >> 8;
+	buf[3] = (secs & 0x00FF0000) >> 16;
+	buf[4] = (secs & 0xFF000000) >> 24;
+
+	xfer = i2c_master_send(client, buf, 5);
+	if (xfer != 5) {
+		dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	unsigned long secs;
+
+	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);
+
+	rtc_tm_to_time(tm, &secs);
+
+	return ds1672_set_mmss(client, secs);
+}
+
+static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return ds1672_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int ds1672_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return ds1672_set_datetime(to_i2c_client(dev), tm);
+}
+
+static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+	return ds1672_set_mmss(to_i2c_client(dev), secs);
+}
+
+static struct rtc_class_ops ds1672_rtc_ops = {
+	.read_time	= ds1672_rtc_read_time,
+	.set_time	= ds1672_rtc_set_time,
+	.set_mmss	= ds1672_rtc_set_mmss,
+};
+
+static int ds1672_attach(struct i2c_adapter *adapter)
+{
+	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+	return i2c_probe(adapter, &addr_data, ds1672_probe);
+}
+
+static int ds1672_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 struct i2c_driver ds1672_driver = {
+	.driver		= {
+		.name	= "ds1672",
+	},
+	.attach_adapter = &ds1672_attach,
+	.detach_client	= &ds1672_detach,
+};
+
+static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+	int err = 0;
+	struct i2c_client *client;
+	struct rtc_device *rtc;
+
+	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;
+	}
+
+	/* I2C client */
+	client->addr = address;
+	client->driver = &ds1672_driver;
+	client->adapter	= adapter;
+
+	strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE);
+
+	/* 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(ds1672_driver.driver.name, &client->dev,
+				&ds1672_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 __init ds1672_init(void)
+{
+	return i2c_add_driver(&ds1672_driver);
+}
+
+static void __exit ds1672_exit(void)
+{
+	i2c_del_driver(&ds1672_driver);
+}
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ds1672_init);
+module_exit(ds1672_exit);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 10/11] RTC subsystem, PCF8563 driver
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (8 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 09/11] RTC subsystem, DS1672 driver Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-19 23:22 ` [PATCH 11/11] RTC subsystem, RS5C372 driver Alessandro Zummo
  2006-02-20  0:58 ` [PATCH 00/11] RTC subsystem Andrew Morton
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-pcf8563.patch --]
[-- Type: text/plain, Size: 10848 bytes --]

An RTC class aware driver for the Philips
PCF8563 RTC and Epson RTC8564  chips.

This chip is used on the Iomega NAS100D.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--
 drivers/rtc/Kconfig       |   11 +
 drivers/rtc/Makefile      |    1 
 drivers/rtc/rtc-pcf8563.c |  355 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 367 insertions(+)

--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-15 04:14:20.000000000 +0100
@@ -92,6 +92,17 @@ config RTC_DRV_DS1672
 	  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
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-15 04:14:03.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-15 04:14:20.000000000 +0100
@@ -13,4 +13,5 @@ obj-$(CONFIG_RTC_INTF_DEV)	+= rtc-dev.o
 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-rtc/drivers/rtc/rtc-pcf8563.c	2006-02-15 04:14:30.000000000 +0100
@@ -0,0 +1,355 @@
+/*
+ * 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/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+#define DRV_VERSION "0.4.2"
+
+/* Addresses to scan: none
+ * This chip cannot be reliably autodetected. An empty eeprom
+ * located at 0x51 will pass the validation routine due to
+ * the way the registers are implemented.
+ */
+static unsigned short normal_i2c[] = { 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 = {
+	.driver		= {
+		.name	= "pcf8563",
+	},
+	.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] = { PCF8563_REG_ST1 };
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 1, buf },	/* 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;
+
+	static const struct pcf8563_limit 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	},
+	};
+
+	/* check limits (only registers with bcd values) */
+	for (i = 0; i < ARRAY_SIZE(pattern); i++) {
+		int xfer;
+		unsigned char value;
+		unsigned char buf = pattern[i].reg;
+
+		struct i2c_msg msgs[] = {
+			{ client->addr, 0, 1, &buf },
+			{ client->addr, I2C_M_RD, 1, &buf },
+		};
+
+		xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+
+		if (xfer != ARRAY_SIZE(msgs)) {
+			dev_err(&client->adapter->dev,
+				"%s: could not read register 0x%02X\n",
+				__FUNCTION__, pattern[i].reg);
+
+			return -EIO;
+		}
+
+		value = BCD2BIN(buf & pattern[i].mask);
+
+		if (value > pattern[i].max ||
+			value < pattern[i].min) {
+			dev_dbg(&client->adapter->dev,
+				"%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, "
+				"max=%d, value=%d, raw=0x%02X\n",
+				__FUNCTION__, i, pattern[i].reg, pattern[i].mask,
+				pattern[i].min, pattern[i].max,
+				value, buf);
+
+			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_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,
+};
+
+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.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.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);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 11/11] RTC subsystem, RS5C372 driver
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (9 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 10/11] RTC subsystem, PCF8563 driver Alessandro Zummo
@ 2006-02-19 23:22 ` Alessandro Zummo
  2006-02-20  0:58 ` [PATCH 00/11] RTC subsystem Andrew Morton
  11 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-19 23:22 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-rs5c372.patch --]
[-- Type: text/plain, Size: 8960 bytes --]

RTC class aware driver for the Ricoh RS5C372
chip used, among others, on the Synology DS101.

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--
 drivers/rtc/Kconfig       |   10 +
 drivers/rtc/Makefile      |    1 
 drivers/rtc/rtc-rs5c372.c |  295 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 306 insertions(+)

--- linux-rtc.orig/drivers/rtc/Kconfig	2006-02-15 03:57:31.000000000 +0100
+++ linux-rtc/drivers/rtc/Kconfig	2006-02-15 04:01:14.000000000 +0100
@@ -103,6 +103,16 @@ 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_RS5C372
+	tristate "Ricoh RS5C372A/B"
+	depends on RTC_CLASS && I2C
+	help
+	  If you say yes here you get support for the
+	  Ricoh RS5C372A and RS5C372B RTC chips.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-rs5c372.
+
 config RTC_DRV_TEST
 	tristate "Test driver/device"
 	depends on RTC_CLASS
--- linux-rtc.orig/drivers/rtc/Makefile	2006-02-15 03:57:31.000000000 +0100
+++ linux-rtc/drivers/rtc/Makefile	2006-02-15 04:01:14.000000000 +0100
@@ -14,4 +14,5 @@ obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205
 obj-$(CONFIG_RTC_DRV_TEST)	+= rtc-test.o
 obj-$(CONFIG_RTC_DRV_DS1672)	+= rtc-ds1672.o
 obj-$(CONFIG_RTC_DRV_PCF8563)	+= rtc-pcf8563.o
+obj-$(CONFIG_RTC_DRV_RS5C372)	+= rtc-rs5c372.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-rtc/drivers/rtc/rtc-rs5c372.c	2006-02-15 04:01:26.000000000 +0100
@@ -0,0 +1,295 @@
+/*
+ * An I2C driver for the Ricoh RS5C372 RTC
+ *
+ * Copyright (C) 2005 Pavel Mironchik pmironchik@optifacio.net
+ * Copyright (C) 2006 Tower Technologies
+ *
+ * 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/rtc.h>
+#include <linux/bcd.h>
+
+#define DRV_VERSION "0.2"
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD;
+
+#define RS5C372_REG_SECS	0
+#define RS5C372_REG_MINS	1
+#define RS5C372_REG_HOURS	2
+#define RS5C372_REG_WDAY	3
+#define RS5C372_REG_DAY		4
+#define RS5C372_REG_MONTH	5
+#define RS5C372_REG_YEAR	6
+#define RS5C372_REG_TRIM	7
+
+#define RS5C372_TRIM_XSL	0x80
+#define RS5C372_TRIM_MASK	0x7F
+
+#define RS5C372_REG_BASE	0
+
+static int rs5c372_attach(struct i2c_adapter *adapter);
+static int rs5c372_detach(struct i2c_client *client);
+static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind);
+
+static struct i2c_driver rs5c372_driver = {
+	.driver		= {
+		.name	= "rs5c372",
+	},
+	.attach_adapter	= &rs5c372_attach,
+	.detach_client	= &rs5c372_detach,
+};
+
+static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	unsigned char buf[7] = { RS5C372_REG_BASE };
+
+	/* this implements the 1st reading method, according
+	 * to the datasheet. buf[0] is initialized with
+	 * address ptr and transmission format register.
+	 */
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 1, buf },
+		{ client->addr, I2C_M_RD, 7, buf },
+	};
+
+	if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	tm->tm_sec = BCD2BIN(buf[RS5C372_REG_SECS] & 0x7f);
+	tm->tm_min = BCD2BIN(buf[RS5C372_REG_MINS] & 0x7f);
+	tm->tm_hour = BCD2BIN(buf[RS5C372_REG_HOURS] & 0x3f);
+	tm->tm_wday = BCD2BIN(buf[RS5C372_REG_WDAY] & 0x07);
+	tm->tm_mday = BCD2BIN(buf[RS5C372_REG_DAY] & 0x3f);
+
+	/* tm->tm_mon is zero-based */
+	tm->tm_mon = BCD2BIN(buf[RS5C372_REG_MONTH] & 0x1f) - 1;
+
+	/* year is 1900 + tm->tm_year */
+	tm->tm_year = BCD2BIN(buf[RS5C372_REG_YEAR]) + 100;
+
+	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);
+
+	return 0;
+}
+
+static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+	unsigned char buf[8] = { RS5C372_REG_BASE };
+
+	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);
+
+	buf[1] = BIN2BCD(tm->tm_sec);
+	buf[2] = BIN2BCD(tm->tm_min);
+	buf[3] = BIN2BCD(tm->tm_hour);
+	buf[4] = BIN2BCD(tm->tm_wday);
+	buf[5] = BIN2BCD(tm->tm_mday);
+	buf[6] = BIN2BCD(tm->tm_mon + 1);
+	buf[7] = BIN2BCD(tm->tm_year - 100);
+
+	if ((i2c_master_send(client, buf, 8)) != 8) {
+		dev_err(&client->dev, "%s: write error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
+{
+	unsigned char buf = RS5C372_REG_TRIM;
+
+	struct i2c_msg msgs[] = {
+		{ client->addr, 0, 1, &buf },
+		{ client->addr, I2C_M_RD, 1, &buf },
+	};
+
+	if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+		dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, trim);
+
+	if (osc)
+		*osc = (buf & RS5C372_TRIM_XSL) ? 32000 : 32768;
+
+	if (trim)
+		*trim = buf & RS5C372_TRIM_MASK;
+
+	return 0;
+}
+
+static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return rs5c372_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	return rs5c372_set_datetime(to_i2c_client(dev), tm);
+}
+
+static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	int err, osc, trim;
+
+	seq_printf(seq, "24hr\t\t: yes\n");
+
+	if ((err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim)) == 0) {
+		seq_printf(seq, "%d.%03d KHz\n", osc / 1000, osc % 1000);
+		seq_printf(seq, "trim\t: %d\n", trim);
+	}
+
+	return 0;
+}
+
+static struct rtc_class_ops rs5c372_rtc_ops = {
+	.proc		= rs5c372_rtc_proc,
+	.read_time	= rs5c372_rtc_read_time,
+	.set_time	= rs5c372_rtc_set_time,
+};
+
+static ssize_t rs5c372_sysfs_show_trim(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int trim;
+
+	if (rs5c372_get_trim(to_i2c_client(dev), NULL, &trim) == 0)
+		return sprintf(buf, "0x%2x\n", trim);
+
+	return 0;
+}
+static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL);
+
+static ssize_t rs5c372_sysfs_show_osc(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	int osc;
+
+	if (rs5c372_get_trim(to_i2c_client(dev), &osc, NULL) == 0)
+		return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
+
+	return 0;
+}
+static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL);
+
+static int rs5c372_attach(struct i2c_adapter *adapter)
+{
+	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
+	return i2c_probe(adapter, &addr_data, rs5c372_probe);
+}
+
+static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
+{
+	int err = 0;
+	struct i2c_client *client;
+	struct rtc_device *rtc;
+
+	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;
+	}
+
+	/* I2C client */
+	client->addr = address;
+	client->driver = &rs5c372_driver;
+	client->adapter = adapter;
+
+	strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE);
+
+	/* Inform the i2c layer */
+	if ((err = i2c_attach_client(client)))
+		goto exit_kfree;
+
+	dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+	rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev,
+				&rs5c372_rtc_ops, THIS_MODULE);
+
+	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);
+
+	device_create_file(&client->dev, &dev_attr_trim);
+	device_create_file(&client->dev, &dev_attr_osc);
+
+	return 0;
+
+exit_detach:
+	i2c_detach_client(client);
+
+exit_kfree:
+	kfree(client);
+
+exit:
+	return err;
+}
+
+static int rs5c372_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 __init int rs5c372_init(void)
+{
+	return i2c_add_driver(&rs5c372_driver);
+}
+
+static __exit void rs5c372_exit(void)
+{
+	i2c_del_driver(&rs5c372_driver);
+}
+
+module_init(rs5c372_init);
+module_exit(rs5c372_exit);
+
+MODULE_AUTHOR(
+		"Pavel Mironchik <pmironchik@optifacio.net>, "
+		"Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] RTC subsystem
  2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
                   ` (10 preceding siblings ...)
  2006-02-19 23:22 ` [PATCH 11/11] RTC subsystem, RS5C372 driver Alessandro Zummo
@ 2006-02-20  0:58 ` Andrew Morton
  2006-02-20  1:10   ` Alessandro Zummo
  11 siblings, 1 reply; 20+ messages in thread
From: Andrew Morton @ 2006-02-20  0:58 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel, dtor_core

Alessandro Zummo <a.zummo@towertech.it> wrote:
>
> 
>  Hello,
> 
>   this is me again with this shining new
>  RTC subsystem :)
> 
>  quick changelog:
> 
>  - attribute groups
>  - mutexes
>  - fixed another bug in pcf8563 detection 
> 
>  Many thanks to all the people who contributed
>  with comments both privately and on this ml.
> 

This is nowhere near a sufficient explanation for such a patchset.

What does it all do, how does it do it and, importantly, _why_ does it do
it?


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] RTC subsystem
  2006-02-20  0:58 ` [PATCH 00/11] RTC subsystem Andrew Morton
@ 2006-02-20  1:10   ` Alessandro Zummo
  0 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-20  1:10 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, dtor_core

On Sun, 19 Feb 2006 16:58:45 -0800
Andrew Morton <akpm@osdl.org> wrote:

> > 
> 
> This is nowhere near a sufficient explanation for such a patchset.
> 
> What does it all do, how does it do it and, importantly, _why_ does it do
> it?

 Sorry Andrew, it was meant as an update to my original email
 with full description. I've reported here as it is still valid.


 Hello,

  this is a proposal for the implementation of a kernel-wide
 RTC subsystem.

  The current state of RTCs under linux is that each one
 of the current drivers is actually self-contained and
 has a lot of redundant functions [1].
 
  The lack of a kernel-wide subsystem is particulary important
 on embedded devices, where the RTC is usually implemented
 on an I2C chip.

  Of the current I2C RTC drivers, no-one actually interfaces
 with the kernel [2]: the driver is actually useless
 without further patches that are probably provided as part
 of an external project.

  When new driver are to be implemented [3], I've noticed
 authors are often confused on how to do it, resulting
 in drivers that will not work on different architectures
 and that will probably never be merged in the kernel.

  They also happen to use ioctls over (struct i2c_client *)->command,
 which has recently been deprecated [4].

  The architecture is quite simple. Each RTC device should
 register to the RTC class, providing a set of pointers
 to functions. The class will provide access to the RTC
 to the whole kernel and userspace.

  For this purpose, the class supports multiple interfaces,
 like sysfs, proc and dev.

  The user has complete control over which interfaces
 gets added using the standard Kconfig mechanism.

  proc and dev, due to their nature, will only expose
 the first RTC that registers to the subsystem.

  The RTC code is derived from the one of the ARM subsystem.

  This patchset has been verify to properly work under Linux/ARM
 on several NSLU2s (http://www.linux-nslu2.org) and applies
 successfully on the 2.6.15-rc6 kernel . If this is the right
 way to go, I will port the x86 rtc driver in order to get
 broader testing.

  I'd appreciate receiving feedback on this proposal.

  Thanks in advance.

--

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


[1]
	http://lkml.org/lkml/2005/11/17/180

[2]
	drivers/i2c/chips/m41t00.c
	drivers/i2c/chips/rtc8564.c
	drivers/i2c/chips/ds1337.c
	drivers/i2c/chips/ds1374.c

[3]
	http://lists.lm-sensors.org/pipermail/lm-sensors/2005-November/014428.html
	http://lists.lm-sensors.org/pipermail/lm-sensors/2005-November/014386.html

[4]
	http://lists.lm-sensors.org/pipermail/lm-sensors/2005-December/014688.html
	http://lists.lm-sensors.org/pipermail/lm-sensors/2005-November/014369.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 01/11] RTC subsystem, class
  2006-02-19 23:22 ` [PATCH 01/11] RTC subsystem, class Alessandro Zummo
@ 2006-02-22 22:15   ` Andrew Morton
  2006-02-23  1:09     ` Alessandro Zummo
  0 siblings, 1 reply; 20+ messages in thread
From: Andrew Morton @ 2006-02-22 22:15 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel

Alessandro Zummo <a.zummo@towertech.it> wrote:
>
> This patch adds the basic RTC subsytem infrastructure
> to the kernel.
> 
> rtc/class.c - registration facilities for RTC drivers
> rtc/interface.c - kernel/rtc interface functions 
> rtc/utils.c - misc rtc-related utility functions
> rtc/hctosys.c - snippet of code that copies hw clock to sw clock
> 		at bootup, if configured to do so.

Couple of questions.

a) Is all this code 100% compatible with existing kernel interfaces?  No
   userspace-visible breakage?  Right down the all the same -EFOO return
   codes for all the same errors?

b) Will the kernel compile and run at each stage of your patch series? 
   I hit a nasty no-compile half an hour through a git-bisect session
   yesterday and it's still smarting.

Code looks nice and clean.

> +	if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
> +		err = -ENOMEM;
> +		goto exit;
> +	}
> +
> +
> +	mutex_lock(&idr_lock);
> +	err = idr_get_new(&rtc_idr, NULL, &id);
> +	mutex_unlock(&idr_lock);

Oh the IDR API does suck.  Not your fault though.

> +	if ((rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL)) == NULL) {

You do a lot of

	if ((lhs = rhs) == something)

But preferred kernel style is

	lhs = rhs;
	if (lhs == something)

Generally, kernel style is to keep things as utterly simple as they can be.

+config RTC_CLASS
> +	tristate "RTC class"
> +	depends on EXPERIMENTAL
> +	default y
> +	help
> +	  Generic RTC class support. If you say yes here, you will
> + 	  be allowed to plug one or more RTCs to your system. You will
> +	  probably want to enable one of more of the interfaces below.
> +
> +	  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.

hm.  Doesn't the above disable RTC_HCTOSYS and RTC_HCTOSYS_DEVICE if
RTC_CLASS=m?

> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-rtc/drivers/rtc/interface.c	2006-02-15 04:13:37.000000000 +0100
> @@ -0,0 +1,274 @@
> +/*
> + * RTC subsystem, interface functions
> + *
> + * Copyright (C) 2005 Tower Technologies
> + * Author: Alessandro Zummo <a.zummo@towertech.it>
> + *
> + * based on arch/arm/common/rtctime.c
> + *
> + * 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>
> +
> +extern struct class *rtc_class;

Please always put extern declarations in a header file.

> +}
> +EXPORT_SYMBOL(rtc_set_alarm);
> +
> +void rtc_update_irq(struct class_device *class_dev,
> +		unsigned long num, unsigned long events)
> +{
> +	struct rtc_device *rtc = to_rtc_device(class_dev);
> +
> +	spin_lock(&rtc->irq_lock);
> +	rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
> +	spin_unlock(&rtc->irq_lock);
> +
> +	spin_lock(&rtc->irq_task_lock);
> +	if (rtc->irq_task)
> +		rtc->irq_task->func(rtc->irq_task->private_data);
> +	spin_unlock(&rtc->irq_task_lock);
> +
> +	wake_up_interruptible(&rtc->irq_queue);
> +	kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
> +}
> +EXPORT_SYMBOL(rtc_update_irq);

I don't know what this does.

Please document all non-static functions.  Preferably with kernel-doc
format.  Feel free to document static functions too..

> +int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
> +{
> +	int err = 0, tmp = 0;
> +	unsigned long flags;
> +	struct rtc_device *rtc = to_rtc_device(class_dev);
> +
> +	/* allowed range is 2-8192 */
> +	if (freq < 2 || freq > 8192)
> +		return -EINVAL;
> +
> +/*	if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE)))
> +		return -EACCES;
> +*/

What happened to rtc_max_user_freq?


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 01/11] RTC subsystem, class
  2006-02-22 22:15   ` Andrew Morton
@ 2006-02-23  1:09     ` Alessandro Zummo
  2006-02-23  1:31       ` Andrew Morton
  0 siblings, 1 reply; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-23  1:09 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Alessandro Zummo, linux-kernel

On Wed, 22 Feb 2006 14:15:54 -0800
Andrew Morton <akpm@osdl.org> wrote:

> Couple of questions.
> 
> a) Is all this code 100% compatible with existing kernel interfaces?  No
>    userspace-visible breakage?  Right down the all the same -EFOO return
>    codes for all the same errors?

 this new class works only in places of the kernel were there were no
 RTCs before.. basically, I haven't touched the x86 or any other platform clock
 and focused only on i2c clocks which were actually not used.

 From the userspace point of view, the interface is the same. I noticed
 that hwclock does not like things like time that is not changing or
 an -EINVAL on a read, which can instead happen when you use
 i2c clocks. However, that is not an issue yet because hwclock has
 not been used on those i2c clocks until now. 

> 
> b) Will the kernel compile and run at each stage of your patch series? 
>    I hit a nasty no-compile half an hour through a git-bisect session
>    yesterday and it's still smarting.

 I' haven't tested it.. I think there may be problems because at some
 points some functions in the kernel needs to be renamed and/or
 changed.. i would say the first two patches should be applied
 together. 

> Code looks nice and clean.

 thanks.

> > +	if ((rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL)) == NULL) {
> 
> You do a lot of
> 
> 	if ((lhs = rhs) == something)
> 
> But preferred kernel style is
> 
> 	lhs = rhs;
> 	if (lhs == something)

 good to know, that is also my preferred style. I will happily change
 this, I just thought the kernel style was the other one :)


> Generally, kernel style is to keep things as utterly simple as they can be.

[..]

> > +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.
> 
> hm.  Doesn't the above disable RTC_HCTOSYS and RTC_HCTOSYS_DEVICE if
> RTC_CLASS=m?

 yes. I can't remember if it is intended, but the point of having
 hctosys was to copy the time early in the bootup process.

> > +
> > +extern struct class *rtc_class;
> 
> Please always put extern declarations in a header file.

 ack.

> > +EXPORT_SYMBOL(rtc_update_irq);
> 
> I don't know what this does.
> 
> Please document all non-static functions.  Preferably with kernel-doc
> format.  Feel free to document static functions too..

 will do.

> > +int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
> > +{
> > +	int err = 0, tmp = 0;
> > +	unsigned long flags;
> > +	struct rtc_device *rtc = to_rtc_device(class_dev);
> > +
> > +	/* allowed range is 2-8192 */
> > +	if (freq < 2 || freq > 8192)
> > +		return -EINVAL;
> > +
> > +/*	if ((freq > rtc_max_user_freq) && (!capable(CAP_SYS_RESOURCE)))
> > +		return -EACCES;
> > +*/
> 
> What happened to rtc_max_user_freq?

 not implemented yet, I need to handle it in a different way. rtc_irq_set_freq
 is the kernel interface, I must move this check in the /dev/rtc code.

 How can I handle further updates, just repost the whole patchset
 to lkml ?

 thanks for you review!

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 01/11] RTC subsystem, class
  2006-02-23  1:09     ` Alessandro Zummo
@ 2006-02-23  1:31       ` Andrew Morton
  0 siblings, 0 replies; 20+ messages in thread
From: Andrew Morton @ 2006-02-23  1:31 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: a.zummo, linux-kernel

Alessandro Zummo <alessandro.zummo@towertech.it> wrote:
>
>   How can I handle further updates, just repost the whole patchset
>   to lkml ?

Yes, that would be best.  Once the patches are settled down I prefer
incremental updates so we can see what changed, but at this stage in
proceedings a wholesale replacement is OK.

It would be good if you can arrange it so there are no build-breaks
anywhere in the series.



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] RTC subsystem
  2006-02-14 10:30 ` Ben Dooks
@ 2006-02-14 11:00   ` Alessandro Zummo
  0 siblings, 0 replies; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-14 11:00 UTC (permalink / raw)
  To: Ben Dooks; +Cc: linux-kernel

On Tue, 14 Feb 2006 10:30:03 +0000
Ben Dooks <ben-linux@fluff.org> wrote:

> >  As usual, your feedback is highly appreciated.
> 
> are you planning on doing the updates for the
> s3c2410-rtc.c driver?

 Hi Ben,

  I examined that driver some days ago and was going
 to ask you if you can port it to the new subsystem.

 The driver is well structured and and should be easy
 to port. I will have no means to test it, but I'd
 be happy to provide any support you'll need for
 the conversion.

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/11] RTC subsystem
  2006-02-13 22:54 Alessandro Zummo
@ 2006-02-14 10:30 ` Ben Dooks
  2006-02-14 11:00   ` Alessandro Zummo
  0 siblings, 1 reply; 20+ messages in thread
From: Ben Dooks @ 2006-02-14 10:30 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel

On Mon, Feb 13, 2006 at 11:54:16PM +0100, Alessandro Zummo wrote:
> 
>  Hello,
> 
>   this release of the new RTC subsystem has got
>  a few locking issues fixed as well as new
>  drivers for the Ricoh RS5C372A/B and
>  Philips PCF8563/Epson RTC8564.
> 
>   Detection routines have been improved
>  in some drivers, while some others now require
>  a specific probe parameter in order to
>  avoid spurious writes on I2C eeprom chips.
> 
>  As usual, your feedback is highly appreciated.

are you planning on doing the updates for the
s3c2410-rtc.c driver?

-- 
Ben (ben@fluff.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 00/11] RTC subsystem
@ 2006-02-13 22:54 Alessandro Zummo
  2006-02-14 10:30 ` Ben Dooks
  0 siblings, 1 reply; 20+ messages in thread
From: Alessandro Zummo @ 2006-02-13 22:54 UTC (permalink / raw)
  To: linux-kernel


 Hello,

  this release of the new RTC subsystem has got
 a few locking issues fixed as well as new
 drivers for the Ricoh RS5C372A/B and
 Philips PCF8563/Epson RTC8564.

  Detection routines have been improved
 in some drivers, while some others now require
 a specific probe parameter in order to
 avoid spurious writes on I2C eeprom chips.

 As usual, your feedback is highly appreciated.

--

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it

--

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2006-02-23  1:31 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-02-19 23:22 [PATCH 00/11] RTC subsystem Alessandro Zummo
2006-02-19 23:22 ` [PATCH 01/11] RTC subsystem, class Alessandro Zummo
2006-02-22 22:15   ` Andrew Morton
2006-02-23  1:09     ` Alessandro Zummo
2006-02-23  1:31       ` Andrew Morton
2006-02-19 23:22 ` [PATCH 02/11] RTC subsystem, ARM cleanup Alessandro Zummo
2006-02-19 23:22 ` [PATCH 03/11] RTC subsystem, I2C cleanup Alessandro Zummo
2006-02-19 23:22 ` [PATCH 04/11] RTC subsystem, sysfs interface Alessandro Zummo
2006-02-19 23:22 ` [PATCH 05/11] RTC subsystem, proc interface Alessandro Zummo
2006-02-19 23:22 ` [PATCH 06/11] RTC subsystem, dev interface Alessandro Zummo
2006-02-19 23:22 ` [PATCH 07/11] RTC subsystem, X1205 driver Alessandro Zummo
2006-02-19 23:22 ` [PATCH 08/11] RTC subsystem, test device/driver Alessandro Zummo
2006-02-19 23:22 ` [PATCH 09/11] RTC subsystem, DS1672 driver Alessandro Zummo
2006-02-19 23:22 ` [PATCH 10/11] RTC subsystem, PCF8563 driver Alessandro Zummo
2006-02-19 23:22 ` [PATCH 11/11] RTC subsystem, RS5C372 driver Alessandro Zummo
2006-02-20  0:58 ` [PATCH 00/11] RTC subsystem Andrew Morton
2006-02-20  1:10   ` Alessandro Zummo
  -- strict thread matches above, loose matches on Subject: below --
2006-02-13 22:54 Alessandro Zummo
2006-02-14 10:30 ` Ben Dooks
2006-02-14 11:00   ` Alessandro Zummo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).