linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] RTC subsystem
@ 2006-01-08 23:12 Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 1/8] RTC subsystem, class Alessandro Zummo
                   ` (7 more replies)
  0 siblings, 8 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

 Hello,

  updated version of my proposal for an RTC Subsystem. 

 I've fixed some locking issues, thanks to Dmitry Torokhov
 and added a driver for the Dallas/Maxim DS1672 Timekeeping
 chip.

 The subsystem should now be capable to handle all
 of the existing standalone drivers. 

 As usual, your feedback is highly appreciated.

--

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it

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

* [PATCH 1/8] RTC subsystem, class
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 2/8] RTC subsystem, ARM cleanup Alessandro Zummo
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-class.patch --]
[-- Type: text/plain, Size: 16074 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

Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
--
 drivers/Kconfig         |    2 
 drivers/Makefile        |    1 
 drivers/rtc/Kconfig     |   25 ++++++
 drivers/rtc/Makefile    |    8 ++
 drivers/rtc/class.c     |  143 ++++++++++++++++++++++++++++++++++++
 drivers/rtc/interface.c |  189 ++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/rtc/utils.c     |   97 ++++++++++++++++++++++++
 include/linux/rtc.h     |   73 ++++++++++++++++++
 8 files changed, 538 insertions(+)

--- linux-nslu2.orig/include/linux/rtc.h	2006-01-04 02:36:34.000000000 +0100
+++ linux-nslu2/include/linux/rtc.h	2006-01-08 16:59:26.000000000 +0100
@@ -91,8 +91,81 @@ 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>
+
+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);
+};
+
+#define RTC_DEVICE_NAME_SIZE 20
+struct rtc_task;
+
+struct rtc_device
+{
+	int id;
+	struct module *owner;
+	struct class_device class_dev;
+	struct semaphore ops_lock;
+	struct rtc_class_ops *ops;
+	char name[RTC_DEVICE_NAME_SIZE];
+
+	struct cdev char_dev;
+	struct semaphore char_sem;
+
+	unsigned long irq_data;
+	spinlock_t irq_lock;
+	wait_queue_head_t irq_queue;
+	struct fasync_struct *async_queue;
+
+	spinlock_t irq_task_lock;
+	struct rtc_task *irq_task;
+	int irq_freq;
+};
+#define to_rtc_device(d) container_of(d, struct rtc_device, class_dev)
+
+extern struct rtc_device *rtc_device_register(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_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);
+
 typedef struct rtc_task {
 	void (*func)(void *private_data);
 	void *private_data;
--- linux-nslu2.orig/drivers/Kconfig	2006-01-04 02:36:34.000000000 +0100
+++ linux-nslu2/drivers/Kconfig	2006-01-08 15:49:13.000000000 +0100
@@ -66,4 +66,6 @@ source "drivers/infiniband/Kconfig"
 
 source "drivers/sn/Kconfig"
 
+source "drivers/rtc/Kconfig"
+
 endmenu
--- linux-nslu2.orig/drivers/Makefile	2006-01-04 02:36:34.000000000 +0100
+++ linux-nslu2/drivers/Makefile	2006-01-08 15:49:13.000000000 +0100
@@ -54,6 +54,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-nslu2/drivers/rtc/class.c	2006-01-08 17:07:35.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 DECLARE_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);
+	down(&idr_lock);
+	idr_remove(&rtc_idr, rtc->id);
+	up(&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(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;
+	}
+
+
+	down(&idr_lock);
+	err = idr_get_new(&rtc_idr, NULL, &id);
+	up(&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;
+
+	init_MUTEX(&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)
+{
+	down(&rtc->ops_lock);
+	rtc->ops = NULL;
+	up(&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-nslu2/drivers/rtc/Kconfig	2006-01-08 18:54:54.000000000 +0100
@@ -0,0 +1,25 @@
+#
+# 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.
+
+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-nslu2/drivers/rtc/Makefile	2006-01-08 18:54:54.000000000 +0100
@@ -0,0 +1,8 @@
+#
+# Makefile for RTC class/drivers.
+#
+
+obj-y				+= utils.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-nslu2/drivers/rtc/interface.c	2006-01-04 03:56:39.000000000 +0100
@@ -0,0 +1,189 @@
+/*
+ * 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 = -EINVAL;
+	struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+
+	if (ops->read_time) {
+		memset(tm, 0, sizeof(struct rtc_time));
+		err = ops->read_time(class_dev->dev, tm);
+	}
+	return err;
+}
+EXPORT_SYMBOL(rtc_read_time);
+
+int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
+{
+	int err;
+	struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+
+	err = rtc_valid_tm(tm);
+	if (err == 0 && ops->set_time)
+		err = ops->set_time(class_dev->dev, tm);
+
+	return err;
+}
+EXPORT_SYMBOL(rtc_set_time);
+
+int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+{
+	struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+	int err = -EINVAL;
+
+	if (ops->read_alarm) {
+		memset(alarm, 0, sizeof(struct rtc_wkalrm));
+		err = ops->read_alarm(class_dev->dev, alarm);
+	}
+	return err;
+}
+EXPORT_SYMBOL(rtc_read_alarm);
+
+int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
+{
+	int err = -EINVAL;
+	struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+
+	if (ops->set_alarm)
+		err = ops->set_alarm(class_dev->dev, alarm);
+	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_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;
+		}
+	}
+	up(&rtc_class->sem);
+
+	return class_dev;
+}
+EXPORT_SYMBOL(rtc_open);
+
+void rtc_close(struct class_device *class_dev)
+{
+}
+EXPORT_SYMBOL(rtc_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-nslu2/drivers/rtc/utils.c	2006-01-04 03:56:39.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);

--

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

* [PATCH 2/8] RTC subsystem, ARM cleanup
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 1/8] RTC subsystem, class Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 3/8] RTC subsystem, sysfs interface Alessandro Zummo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-arm-cleanup.patch --]
[-- Type: text/plain, Size: 6426 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 +++++-----------------------------------------
 include/asm-arm/rtc.h     |    3 -
 3 files changed, 14 insertions(+), 98 deletions(-)

--- linux-nslu2.orig/arch/arm/Kconfig	2006-01-04 01:27:04.000000000 +0100
+++ linux-nslu2/arch/arm/Kconfig	2006-01-04 01:27:09.000000000 +0100
@@ -750,6 +750,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/rtc/Kconfig"
+
 endmenu
 
 source "fs/Kconfig"
--- linux-nslu2.orig/arch/arm/common/rtctime.c	2006-01-04 01:27:04.000000000 +0100
+++ linux-nslu2/arch/arm/common/rtctime.c	2006-01-04 01:27:09.000000000 +0100
@@ -40,89 +40,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.
@@ -141,13 +58,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;
 
@@ -158,7 +75,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) {
@@ -168,7 +85,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)
@@ -256,7 +173,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));
@@ -278,11 +195,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));
@@ -300,7 +217,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:
@@ -331,11 +248,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));
@@ -425,7 +342,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"
@@ -435,7 +352,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-nslu2.orig/include/asm-arm/rtc.h	2006-01-04 01:27:04.000000000 +0100
+++ linux-nslu2/include/asm-arm/rtc.h	2006-01-04 01:27:09.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 *);

--

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

* [PATCH 3/8] RTC subsystem, sysfs interface
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 1/8] RTC subsystem, class Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 2/8] RTC subsystem, ARM cleanup Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
       [not found]   ` <200601082102.40992.dtor_core@ameritech.net>
  2006-01-08 23:12 ` [PATCH 4/8] RTC subsystem, proc interface Alessandro Zummo
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-intf-sysfs.patch --]
[-- Type: text/plain, Size: 5180 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 |  129 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 141 insertions(+), 1 deletion(-)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-nslu2/drivers/rtc/rtc-sysfs.c	2006-01-08 17:07:57.000000000 +0100
@@ -0,0 +1,129 @@
+/*
+ * 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 = -ENODEV;
+	struct rtc_device *rtc = to_rtc_device(dev);
+	struct rtc_time tm;
+
+	if ((retval = down_interruptible(&rtc->ops_lock)))
+		return retval;
+
+	if (rtc->ops && ((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);
+	}
+
+	up(&rtc->ops_lock);
+
+	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 = -ENODEV;
+	struct rtc_device *rtc = to_rtc_device(dev);
+	struct rtc_time tm;
+
+	if ((retval = down_interruptible(&rtc->ops_lock)))
+		return retval;
+
+
+	if (rtc->ops && ((retval = rtc_read_time(dev, &tm)) == 0)) {
+		retval = sprintf(buf, "%02d:%02d:%02d\n",
+			tm.tm_hour, tm.tm_min, tm.tm_sec);
+	}
+
+	up(&rtc->ops_lock);
+
+	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 = -ENODEV;
+	struct rtc_device *rtc = to_rtc_device(dev);
+	struct rtc_time tm;
+
+	if ((retval = down_interruptible(&rtc->ops_lock)))
+		return retval;
+
+	if (rtc->ops && ((retval = rtc_read_time(dev, &tm)) == 0)) {
+		unsigned long time;
+		rtc_tm_to_time(&tm, &time);
+		retval = sprintf(buf, "%lu\n", time);
+	}
+
+	up(&rtc->ops_lock);
+
+	return retval;
+}
+static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
+
+/* insertion/removal hooks */
+
+static int __devinit rtc_sysfs_add_device(struct class_device *class_dev,
+					   struct class_interface *class_intf)
+{
+	class_device_create_file(class_dev, &class_device_attr_name);
+	class_device_create_file(class_dev, &class_device_attr_date);
+	class_device_create_file(class_dev, &class_device_attr_time);
+	class_device_create_file(class_dev, &class_device_attr_since_epoch);
+	dev_info(class_dev->dev, "rtc intf: sysfs\n");
+	return 0;
+}
+
+static void rtc_sysfs_remove_device(struct class_device *class_dev,
+				      struct class_interface *class_intf)
+{
+	class_device_remove_file(class_dev, &class_device_attr_name);
+	class_device_remove_file(class_dev, &class_device_attr_date);
+	class_device_remove_file(class_dev, &class_device_attr_time);
+	class_device_remove_file(class_dev, &class_device_attr_since_epoch);
+}
+
+/* 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-nslu2.orig/drivers/rtc/Kconfig	2006-01-08 17:02:53.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-08 18:54:50.000000000 +0100
@@ -19,6 +19,17 @@ config RTC_CLASS
 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-nslu2.orig/drivers/rtc/Makefile	2006-01-08 17:02:53.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-08 18:54:50.000000000 +0100
@@ -5,4 +5,4 @@
 obj-y				+= utils.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] 16+ messages in thread

* [PATCH 4/8] RTC subsystem, proc interface
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
                   ` (2 preceding siblings ...)
  2006-01-08 23:12 ` [PATCH 3/8] RTC subsystem, sysfs interface Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
       [not found]   ` <200601082056.30227.dtor_core@ameritech.net>
  2006-01-08 23:12 ` [PATCH 5/8] RTC subsystem, dev interface Alessandro Zummo
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-intf-proc.patch --]
[-- Type: text/plain, Size: 5591 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-nslu2/drivers/rtc/rtc-proc.c	2006-01-04 01:27:14.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 DECLARE_MUTEX(rtc_sem);
+
+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)
+{
+	down(&rtc_sem);
+	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;
+	}
+	up(&rtc_sem);
+
+	return 0;
+}
+
+static void rtc_proc_remove_device(struct class_device *class_dev,
+					      struct class_interface *class_intf)
+{
+	down(&rtc_sem);
+	if (rtc_dev == class_dev) {
+		remove_proc_entry("driver/rtc", NULL);
+		rtc_dev = NULL;
+	}
+	up(&rtc_sem);
+}
+
+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-nslu2.orig/drivers/rtc/Kconfig	2006-01-04 01:27:12.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-04 01:27:14.000000000 +0100
@@ -30,6 +30,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-nslu2.orig/drivers/rtc/Makefile	2006-01-04 01:27:12.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-04 01:27:14.000000000 +0100
@@ -6,3 +6,4 @@ obj-y				+= utils.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] 16+ messages in thread

* [PATCH 5/8] RTC subsystem, dev interface
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
                   ` (3 preceding siblings ...)
  2006-01-08 23:12 ` [PATCH 4/8] RTC subsystem, proc interface Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  2006-01-09  2:50   ` Dmitry Torokhov
  2006-01-08 23:12 ` [PATCH 6/8] RTC subsystem, X1205 driver Alessandro Zummo
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

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

This patch adds the dev interface to the RTC
subsystem.

The first RTC driver that registers with the class
will be available under /dev/rtc .

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

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

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-nslu2/drivers/rtc/rtc-dev.c	2006-01-04 01:27:15.000000000 +0100
@@ -0,0 +1,372 @@
+/*
+ * 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 (down_trylock(&rtc->char_sem))
+		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 */
+	up(&rtc->char_sem);
+	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) {
+		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);
+
+	spin_lock_irq(&rtc->irq_lock);
+	data = rtc->irq_data;
+	spin_unlock_irq(&rtc->irq_lock);
+
+	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 < 0 && 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);
+
+	spin_lock_irq(&rtc->irq_lock);
+	rtc->irq_data = 0;
+	spin_unlock_irq(&rtc->irq_lock);
+
+	up(&rtc->char_sem);
+	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;
+	}
+
+	init_MUTEX(&rtc->char_sem);
+	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_hotplug(&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_hotplug(&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-nslu2.orig/drivers/rtc/Kconfig	2006-01-04 01:27:14.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-04 01:27:15.000000000 +0100
@@ -41,6 +41,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-nslu2.orig/drivers/rtc/Makefile	2006-01-04 01:27:14.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-04 01:27:15.000000000 +0100
@@ -7,3 +7,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] 16+ messages in thread

* [PATCH 6/8] RTC subsystem, X1205 driver
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
                   ` (4 preceding siblings ...)
  2006-01-08 23:12 ` [PATCH 5/8] RTC subsystem, dev interface Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 7/8] RTC subsystem, test device/driver Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 8/8] RTC subsystem, DS1672 driver Alessandro Zummo
  7 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-x1205.patch --]
[-- Type: text/plain, Size: 20425 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 |  725 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 738 insertions(+)

--- linux-nslu2.orig/drivers/rtc/Kconfig	2006-01-08 17:08:14.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-08 18:23:00.000000000 +0100
@@ -55,4 +55,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-nslu2.orig/drivers/rtc/Makefile	2006-01-08 17:08:14.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-08 18:23:00.000000000 +0100
@@ -8,3 +8,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-nslu2/drivers/rtc/rtc-x1205.c	2006-01-08 18:26:08.000000000 +0100
@@ -0,0 +1,725 @@
+/*
+ * An i2c driver for the Xicor/Intersil 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 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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#define DRV_VERSION "1.0.5"
+
+/* 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 struct i2c_driver x1205_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "x1205",
+	.flags		= I2C_DF_NOTIFY,
+	.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 */
+	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_fix_osc(struct i2c_client *client)
+{
+	int err;
+	struct rtc_time tm;
+
+	tm.tm_hour = 0;
+	tm.tm_min = 0;
+	tm.tm_sec = 0;
+
+	if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0)
+		dev_err(&client->dev,
+			"unable to restart the clock\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;
+}
+
+static int x1205_hctosys(struct i2c_client *client)
+{
+	int err;
+
+	struct rtc_time tm;
+	struct timespec tv;
+	unsigned char sr;
+
+	if ((err = x1205_get_status(client, &sr)) < 0)
+		return err;
+
+	/* Don't set if we had a power failure */
+	if (sr & X1205_SR_RTCF)
+		return -EINVAL;
+
+	if ((err = x1205_get_datetime(client, &tm, X1205_CCR_BASE)) < 0)
+		return err;
+
+	/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
+	 * whether it stores the most close value or the value with partial
+	 * seconds truncated. However, it is important that we use it to store
+	 * the truncated value. This is because otherwise it is necessary,
+	 * in an rtc sync function, to read both xtime.tv_sec and
+	 * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
+	 * of >32bits is not possible. So storing the most close value would
+	 * slow down the sync API. So here we have the truncated value and
+	 * the best guess is to add 0.5s.
+	 */
+
+	tv.tv_nsec = NSEC_PER_SEC >> 1;
+
+	rtc_tm_to_time(&tm, &tv.tv_sec);
+
+	do_settimeofday(&tv);
+
+	dev_info(&client->dev,
+		"setting the system clock to %02d-%02d-%d %02d:%02d:%02d\n",
+		tm.tm_year + 1900, tm.tm_mon + 1,
+		tm.tm_mday, tm.tm_hour, tm.tm_min,
+		tm.tm_sec);
+
+	return 0;
+}
+
+struct x1205_limit
+{
+	unsigned char reg;
+	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_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_set_mmss(struct device *dev, unsigned long secs)
+{
+	int err;
+
+	struct rtc_time new_tm, old_tm;
+
+	if ((err = x1205_rtc_read_time(dev, &old_tm) == 0))
+		return err;
+
+	/* FIXME      xtime.tv_nsec = old_tm.tm_sec * 10000000; */
+	new_tm.tm_sec  = secs % 60;
+	secs /= 60;
+	new_tm.tm_min  = secs % 60;
+	secs /= 60;
+	new_tm.tm_hour = secs % 24;
+
+       /*
+	* avoid writing when we're going to change the day
+	* of the month.  We will retry in the next minute.
+	* This basically means that if the RTC must not drift
+	* by more than 1 minute in 11 minutes.
+	*/
+	if ((old_tm.tm_hour == 23 && old_tm.tm_min == 59) ||
+	    (new_tm.tm_hour == 23 && new_tm.tm_min == 59))
+		return 1;
+
+	return x1205_rtc_set_time(dev, &new_tm);
+}
+
+static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+	int err, dtrim, atrim;
+
+	seq_printf(seq, "24hr\t\t: yes\n");
+
+	err = x1205_get_dtrim(to_i2c_client(dev), &dtrim);
+	if (err == 0)
+		seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim);
+
+	err = x1205_get_atrim(to_i2c_client(dev), &atrim);
+	if (err == 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,
+	.set_mmss = x1205_rtc_set_mmss,
+};
+
+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.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.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");
+
+	/* If requested, set the system time */
+	if (hctosys) {
+		if ((err = x1205_hctosys(client)) < 0)
+			dev_err(&client->dev,
+				"unable to set the system clock\n");
+	}
+
+	device_create_file(&client->dev, &dev_attr_atrim);
+	device_create_file(&client->dev, &dev_attr_dtrim);
+
+	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 <kas11@tampabay.rr.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] 16+ messages in thread

* [PATCH 7/8] RTC subsystem, test device/driver
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
                   ` (5 preceding siblings ...)
  2006-01-08 23:12 ` [PATCH 6/8] RTC subsystem, X1205 driver Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  2006-01-08 23:12 ` [PATCH 8/8] RTC subsystem, DS1672 driver Alessandro Zummo
  7 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-test.patch --]
[-- Type: text/plain, Size: 6401 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-nslu2.orig/drivers/rtc/Kconfig	2006-01-07 11:32:23.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-08 15:14:10.000000000 +0100
@@ -65,4 +65,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-nslu2.orig/drivers/rtc/Makefile	2006-01-07 11:32:23.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-08 15:14:10.000000000 +0100
@@ -10,4 +10,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-nslu2/drivers/rtc/rtc-test.c	2006-01-08 15:35:44.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] 16+ messages in thread

* [PATCH 8/8] RTC subsystem, DS1672 driver
  2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
                   ` (6 preceding siblings ...)
  2006-01-08 23:12 ` [PATCH 7/8] RTC subsystem, test device/driver Alessandro Zummo
@ 2006-01-08 23:12 ` Alessandro Zummo
  7 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-08 23:12 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: rtc-drv-ds1672.patch --]
[-- Type: text/plain, Size: 8104 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 |  266 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 277 insertions(+)

--- linux-nslu2.orig/drivers/rtc/Kconfig	2006-01-08 18:44:16.000000000 +0100
+++ linux-nslu2/drivers/rtc/Kconfig	2006-01-08 18:44:16.000000000 +0100
@@ -65,6 +65,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-nslu2.orig/drivers/rtc/Makefile	2006-01-08 18:44:16.000000000 +0100
+++ linux-nslu2/drivers/rtc/Makefile	2006-01-08 18:44:16.000000000 +0100
@@ -11,4 +11,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-nslu2/drivers/rtc/rtc-ds1672.c	2006-01-08 18:44:16.000000000 +0100
@@ -0,0 +1,266 @@
+/*
+ * 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.1"
+
+/* Addresses to scan: none. This chip cannot be detected. */
+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");
+
+/* 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 buf[4];
+
+	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_hctosys(struct i2c_client *client)
+{
+	int err;
+	struct rtc_time tm;
+	struct timespec tv;
+
+	if ((err = ds1672_get_datetime(client, &tm)) != 0)
+		return err;
+
+	/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
+	 * whether it stores the most close value or the value with partial
+	 * seconds truncated. However, it is important that we use it to store
+	 * the truncated value. This is because otherwise it is necessary,
+	 * in an rtc sync function, to read both xtime.tv_sec and
+	 * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
+	 * of >32bits is not possible. So storing the most close value would
+	 * slow down the sync API. So here we have the truncated value and
+	 * the best guess is to add 0.5s.
+	 */
+
+	tv.tv_nsec = NSEC_PER_SEC >> 1;
+
+	rtc_tm_to_time(&tm, &tv.tv_sec);
+
+	do_settimeofday(&tv);
+
+	dev_info(&client->dev,
+		"setting the system clock to %d-%02d-%02d %02d:%02d:%02d\n",
+		tm.tm_year + 1900, tm.tm_mon + 1,
+		tm.tm_mday, tm.tm_hour, tm.tm_min,
+		tm.tm_sec);
+
+	return 0;
+}
+
+static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	return ds1672_get_datetime(to_i2c_client(dev), tm);
+}
+
+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 = {
+	.owner		= THIS_MODULE,
+	.name		= "ds1672",
+	.flags		= I2C_DF_NOTIFY,
+	.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.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.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);
+
+	/* If requested, set the system time */
+	if (hctosys) {
+		if ((err = ds1672_hctosys(client)) < 0)
+			dev_err(&client->dev,
+				"unable to set the system clock\n");
+	}
+
+	return 0;
+
+exit_detach:
+	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] 16+ messages in thread

* Re: [PATCH 4/8] RTC subsystem, proc interface
       [not found]   ` <200601082056.30227.dtor_core@ameritech.net>
@ 2006-01-09  2:04     ` Alessandro Zummo
  2006-01-09  2:50       ` Dmitry Torokhov
  0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-09  2:04 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Alessandro Zummo, linux-kernel

On Sun, 8 Jan 2006 20:56:29 -0500
Dmitry Torokhov <dtor_core@ameritech.net> wrote:

> > +static void rtc_proc_remove_device(struct class_device *class_dev,
> > +                                             struct class_interface *class_intf)
> > +{
> > +       down(&rtc_sem);
> > +       if (rtc_dev == class_dev) {
> > +               remove_proc_entry("driver/rtc", NULL);
> > +               rtc_dev = NULL;
> > +       }
> > +       up(&rtc_sem);
> > +}
> 
> What if I happen to remove (unregister) rtc devices in order other
> than they were registered in? You need a counter there instead of
> storing the first device created.

 Only the first device that registers will get the /proc/driver/rtc
 entry, which will be removed when the driver unregisters.

 /proc/driver/rtc is a legacy interface, thus supporting it
 for more than one RTC is useless. Any system that uses
 more than one RTCs should access them via /dev/rtcX or
 via sysfs.


-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


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

* Re: [PATCH 3/8] RTC subsystem, sysfs interface
       [not found]   ` <200601082102.40992.dtor_core@ameritech.net>
@ 2006-01-09  2:13     ` Alessandro Zummo
  0 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-09  2:13 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-kernel

On Sun, 8 Jan 2006 21:02:40 -0500
Dmitry Torokhov <dtor_core@ameritech.net> wrote:

> On Sunday 08 January 2006 18:12, Alessandro Zummo wrote:
> > +static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
> > +{
> > +       ssize_t retval = -ENODEV;
> > +       struct rtc_device *rtc = to_rtc_device(dev);
> > +       struct rtc_time tm;

 [...]

> ops locking is a mess here. Half of the code accesses it under protection
> of ops_lock while other half is unlocked. I think it would be better if
> that lock was taken in rtc_read_time and friends.

 Good point. I'll move them, thanks.
 

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


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

* Re: [PATCH 5/8] RTC subsystem, dev interface
  2006-01-08 23:12 ` [PATCH 5/8] RTC subsystem, dev interface Alessandro Zummo
@ 2006-01-09  2:50   ` Dmitry Torokhov
  2006-01-09  3:12     ` Alessandro Zummo
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry Torokhov @ 2006-01-09  2:50 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel

On Sunday 08 January 2006 18:12, Alessandro Zummo wrote:
> +
> +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 (down_trylock(&rtc->char_sem))
> +		return -EBUSY;
> +

Does the device have to be opened for exclusively? Can it support
concurrent reads?


> +	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 */
> +	up(&rtc->char_sem);
> +	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);
> +


The above looks very much like open-coded wait_event_interruptible();

> +	if (ret == 0) {
> +		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);
> +
> +	spin_lock_irq(&rtc->irq_lock);
> +	data = rtc->irq_data;
> +	spin_unlock_irq(&rtc->irq_lock);
> +
> +	return data != 0 ? POLLIN | POLLRDNORM : 0;
> +}

What does the lock above protect? Once it is released rtc->irq_data may
change so reader that was woken up may not see any data anyway.

> +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 < 0 && 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);
> +
> +	spin_lock_irq(&rtc->irq_lock);
> +	rtc->irq_data = 0;
> +	spin_unlock_irq(&rtc->irq_lock);
> +

Why is the above needed?

> +	up(&rtc->char_sem);
> +	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;
> +	}
> +
> +	init_MUTEX(&rtc->char_sem);
> +	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_hotplug(&class_dev->kobj, KOBJ_ADD);
> +

This is kobject_hotplug abuse; you are not adding a new object here.

> +	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_hotplug(&class_dev->kobj, KOBJ_REMOVE);
> +

Same here...

> +		class_dev->devt = MKDEV(0, 0);
> +	}
> +}
> +
> +/* interface registration */
> +
> +struct class_interface rtc_dev_interface = {
> +	.add = &rtc_dev_add_device,
> +	.remove = &rtc_dev_remove_device,
> +};
> +

I wonder if doing rtc dev as a class device interface is a good idea.
It may be cleaner to fold it into the core.

> +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-nslu2.orig/drivers/rtc/Kconfig	2006-01-04 01:27:14.000000000 +0100
> +++ linux-nslu2/drivers/rtc/Kconfig	2006-01-04 01:27:15.000000000 +0100
> @@ -41,6 +41,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-nslu2.orig/drivers/rtc/Makefile	2006-01-04 01:27:14.000000000 +0100
> +++ linux-nslu2/drivers/rtc/Makefile	2006-01-04 01:27:15.000000000 +0100
> @@ -7,3 +7,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
> 
> --
> -
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 

-- 
Dmitry

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

* Re: [PATCH 4/8] RTC subsystem, proc interface
  2006-01-09  2:04     ` Alessandro Zummo
@ 2006-01-09  2:50       ` Dmitry Torokhov
  0 siblings, 0 replies; 16+ messages in thread
From: Dmitry Torokhov @ 2006-01-09  2:50 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: Alessandro Zummo, linux-kernel

On Sunday 08 January 2006 21:04, Alessandro Zummo wrote:
> On Sun, 8 Jan 2006 20:56:29 -0500
> Dmitry Torokhov <dtor_core@ameritech.net> wrote:
> 
> > > +static void rtc_proc_remove_device(struct class_device *class_dev,
> > > +                                             struct class_interface *class_intf)
> > > +{
> > > +       down(&rtc_sem);
> > > +       if (rtc_dev == class_dev) {
> > > +               remove_proc_entry("driver/rtc", NULL);
> > > +               rtc_dev = NULL;
> > > +       }
> > > +       up(&rtc_sem);
> > > +}
> > 
> > What if I happen to remove (unregister) rtc devices in order other
> > than they were registered in? You need a counter there instead of
> > storing the first device created.
> 
>  Only the first device that registers will get the /proc/driver/rtc
>  entry, which will be removed when the driver unregisters.
> 
>  /proc/driver/rtc is a legacy interface, thus supporting it
>  for more than one RTC is useless. Any system that uses
>  more than one RTCs should access them via /dev/rtcX or
>  via sysfs.
> 

Oh, I see. Ignore me then... 

-- 
Dmitry

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

* Re: [PATCH 5/8] RTC subsystem, dev interface
  2006-01-09  2:50   ` Dmitry Torokhov
@ 2006-01-09  3:12     ` Alessandro Zummo
  2006-01-09  6:39       ` Dmitry Torokhov
  0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-09  3:12 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-kernel

On Sun, 8 Jan 2006 21:50:21 -0500
Dmitry Torokhov <dtor_core@ameritech.net> wrote:

 Hi,

   I will reply in two different emails as I need
 to do some research on some questions of yours.

> > +	if (down_trylock(&rtc->char_sem))
> > +		return -EBUSY;
> > +
> 
> Does the device have to be opened for exclusively? Can it support
> concurrent reads?

 I'm trying to make the things work the same way as with the old
 interface. Once this new code will be as stable as the old one, new
 features can be added. 

 Concurrent reads are certainly possible, but we'd need to lock
 out writes in proper places, and I do not feel safe
 to do it at this stage.

 [..]

> > +			ret = -ERESTARTSYS;
> > +			break;
> > +		}
> > +		schedule();
> > +	} while (1);
> > +	set_current_state(TASK_RUNNING);
> > +	remove_wait_queue(&rtc->irq_queue, &wait);
> > +
> 
> 
> The above looks very much like open-coded wait_event_interruptible();

 Ditto, plus there's the irq data inside 

> > +	kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
> > +
> 
> This is kobject_hotplug abuse; you are not adding a new object here.

 Yes. But I've checked the code and should not harm. I've asked
 for that a couple of weeks ago in this same mailing list
 and got no answer. If there's a working alternative to obtain
 the same result, I'm obviously willing to try.

> > +/* interface registration */
> > +
> > +struct class_interface rtc_dev_interface = {
> > +	.add = &rtc_dev_add_device,
> > +	.remove = &rtc_dev_remove_device,
> > +};
> > +
> 
> I wonder if doing rtc dev as a class device interface is a good idea.
> It may be cleaner to fold it into the core.

 What the code implements is actually an interface, so this should
 be the riht place. It is also fully optional, everything could work
 without it. Probably the interface implementation hasn't all the primitives
 to handle this kind of work, but I'm not willing to go into that right now ;)

 Thank you for your hard work Dmitry, after weeks viewing the same
 code I'm going to be lost in all of those locks issues ;)

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


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

* Re: [PATCH 5/8] RTC subsystem, dev interface
  2006-01-09  3:12     ` Alessandro Zummo
@ 2006-01-09  6:39       ` Dmitry Torokhov
  2006-01-09  9:14         ` Alessandro Zummo
  0 siblings, 1 reply; 16+ messages in thread
From: Dmitry Torokhov @ 2006-01-09  6:39 UTC (permalink / raw)
  To: Alessandro Zummo; +Cc: linux-kernel

On Sunday 08 January 2006 22:12, Alessandro Zummo wrote:
> > > +/* interface registration */
> > > +
> > > +struct class_interface rtc_dev_interface = {
> > > +   .add = &rtc_dev_add_device,
> > > +   .remove = &rtc_dev_remove_device,
> > > +};
> > > +
> > 
> > I wonder if doing rtc dev as a class device interface is a good idea.
> > It may be cleaner to fold it into the core.
> 
>  What the code implements is actually an interface, so this should
>  be the riht place. It is also fully optional, everything could work
>  without it. Probably the interface implementation hasn't all the primitives
>  to handle this kind of work, but I'm not willing to go into that right now ;)
> 

Yes, it is an interface. What I am trying to say - is it a main interface?
What is the preferred, most efficient way to interface with RTC? If it is
through this interface it may make sence to fold it into the core. Otherwise
do what input layer does and have interface create another class device which
reprsesents your /dev node.

-- 
Dmitry

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

* Re: [PATCH 5/8] RTC subsystem, dev interface
  2006-01-09  6:39       ` Dmitry Torokhov
@ 2006-01-09  9:14         ` Alessandro Zummo
  0 siblings, 0 replies; 16+ messages in thread
From: Alessandro Zummo @ 2006-01-09  9:14 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-kernel

On Mon, 9 Jan 2006 01:39:29 -0500
Dmitry Torokhov <dtor_core@ameritech.net> wrote:

> >  What the code implements is actually an interface, so this should
> >  be the riht place. It is also fully optional, everything could work
> >  without it. Probably the interface implementation hasn't all the primitives
> >  to handle this kind of work, but I'm not willing to go into that right now ;)
> > 
> 
> Yes, it is an interface. What I am trying to say - is it a main interface?
> What is the preferred, most efficient way to interface with RTC? If it is
> through this interface it may make sence to fold it into the core. Otherwise
> do what input layer does and have interface create another class device which
> reprsesents your /dev node.

 I think it depends on what you want to do. On desktop systems is certainly
 the dev interface, on some embedded you may want to go via sysfs.

 I would keep it that way until the system can react on a change of
 the dev attribute.

-- 

 Best regards,

 Alessandro Zummo,
  Tower Technologies - Turin, Italy

  http://www.towertech.it


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

end of thread, other threads:[~2006-01-09  9:19 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-01-08 23:12 [PATCH 0/8] RTC subsystem Alessandro Zummo
2006-01-08 23:12 ` [PATCH 1/8] RTC subsystem, class Alessandro Zummo
2006-01-08 23:12 ` [PATCH 2/8] RTC subsystem, ARM cleanup Alessandro Zummo
2006-01-08 23:12 ` [PATCH 3/8] RTC subsystem, sysfs interface Alessandro Zummo
     [not found]   ` <200601082102.40992.dtor_core@ameritech.net>
2006-01-09  2:13     ` Alessandro Zummo
2006-01-08 23:12 ` [PATCH 4/8] RTC subsystem, proc interface Alessandro Zummo
     [not found]   ` <200601082056.30227.dtor_core@ameritech.net>
2006-01-09  2:04     ` Alessandro Zummo
2006-01-09  2:50       ` Dmitry Torokhov
2006-01-08 23:12 ` [PATCH 5/8] RTC subsystem, dev interface Alessandro Zummo
2006-01-09  2:50   ` Dmitry Torokhov
2006-01-09  3:12     ` Alessandro Zummo
2006-01-09  6:39       ` Dmitry Torokhov
2006-01-09  9:14         ` Alessandro Zummo
2006-01-08 23:12 ` [PATCH 6/8] RTC subsystem, X1205 driver Alessandro Zummo
2006-01-08 23:12 ` [PATCH 7/8] RTC subsystem, test device/driver Alessandro Zummo
2006-01-08 23:12 ` [PATCH 8/8] RTC subsystem, DS1672 driver 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).