summaryrefslogtreecommitdiff
path: root/packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch
diff options
context:
space:
mode:
authorRod Whitby <rod@whitby.id.au>2006-02-09 11:59:03 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2006-02-09 11:59:03 +0000
commit4f999c3661ce6d85fda1bb97019c61f31d5edc11 (patch)
tree7d536095ccc98beb3ded1f10120331b87953288e /packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch
parent08f23753d0eab9c906b0fd43e36f83f66c1352e2 (diff)
ixp4xx-kernel: Added patchset and metadata for 2.6.16-rc2 kernel.
Diffstat (limited to 'packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch')
-rw-r--r--packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch3188
1 files changed, 3188 insertions, 0 deletions
diff --git a/packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch b/packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch
new file mode 100644
index 0000000000..38359a1bc0
--- /dev/null
+++ b/packages/linux/ixp4xx-kernel/2.6.16/40-rtc-class.patch
@@ -0,0 +1,3188 @@
+--- linux-nslu2.orig/include/linux/rtc.h 2006-02-06 20:37:43.000000000 +0100
++++ linux-nslu2/include/linux/rtc.h 2006-02-06 21:41:39.000000000 +0100
+@@ -93,8 +93,91 @@ struct rtc_pll_info {
+ #define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */
+ #define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
+
++/* 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(const char *name,
++ struct device *dev,
++ struct rtc_class_ops *ops,
++ struct module *owner);
++extern void rtc_device_unregister(struct rtc_device *rdev);
++extern int rtc_interface_register(struct class_interface *intf);
++
++extern int rtc_month_days(unsigned int month, unsigned int year);
++extern int rtc_valid_tm(struct rtc_time *tm);
++extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time);
++extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm);
++
++extern int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm);
++extern int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm);
++extern int rtc_read_alarm(struct class_device *class_dev,
++ struct rtc_wkalrm *alrm);
++extern int rtc_set_alarm(struct class_device *class_dev,
++ struct rtc_wkalrm *alrm);
++extern void rtc_update_irq(struct class_device *class_dev,
++ unsigned long num, unsigned long events);
++
++extern struct class_device *rtc_open(char *name);
++extern void rtc_close(struct class_device *class_dev);
++extern int rtc_irq_register(struct class_device *class_dev,
++ struct rtc_task *task);
++extern void rtc_irq_unregister(struct class_device *class_dev,
++ struct rtc_task *task);
++extern int rtc_irq_set_state(struct class_device *class_dev,
++ struct rtc_task *task, int enabled);
++extern int rtc_irq_set_freq(struct class_device *class_dev,
++ struct rtc_task *task, int freq);
++
+ typedef struct rtc_task {
+ void (*func)(void *private_data);
+ void *private_data;
+--- linux-nslu2.orig/drivers/Kconfig 2006-02-06 20:37:14.000000000 +0100
++++ linux-nslu2/drivers/Kconfig 2006-02-06 21:11:33.000000000 +0100
+@@ -70,4 +70,6 @@ source "drivers/sn/Kconfig"
+
+ source "drivers/edac/Kconfig"
+
++source "drivers/rtc/Kconfig"
++
+ endmenu
+--- linux-nslu2.orig/drivers/Makefile 2006-02-06 20:37:14.000000000 +0100
++++ linux-nslu2/drivers/Makefile 2006-02-06 21:11:33.000000000 +0100
+@@ -56,6 +56,7 @@ obj-$(CONFIG_USB_GADGET) += usb/gadget/
+ obj-$(CONFIG_GAMEPORT) += input/gameport/
+ obj-$(CONFIG_INPUT) += input/
+ obj-$(CONFIG_I2O) += message/
++obj-y += rtc/
+ obj-$(CONFIG_I2C) += i2c/
+ obj-$(CONFIG_W1) += w1/
+ obj-$(CONFIG_HWMON) += hwmon/
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/class.c 2006-02-06 21:41:25.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(const char *name, struct device *dev,
++ struct rtc_class_ops *ops,
++ struct module *owner)
++{
++ struct rtc_device *rtc;
++ int id, err;
++
++ if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
++ err = -ENOMEM;
++ goto exit;
++ }
++
++
++ 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-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,121 @@
++#
++# RTC class/drivers configuration
++#
++
++menu "Real Time Clock"
++
++config RTC_CLASS
++ tristate "RTC class"
++ depends on EXPERIMENTAL
++ default y
++ help
++ Generic RTC class support. If you say yes here, you will
++ be allowed to plug one or more RTCs to your system. You will
++ probably want to enable one of more of the interfaces below.
++
++ This driver can also be built as a module. If so, the module
++ will be called rtc-class.
++
++config RTC_HCTOSYS
++ bool "Set system time from RTC on startup"
++ depends on RTC_CLASS = y
++ default y
++ help
++ If you say yes here, the system time will be set using
++ the value read from the specified RTC device. This is useful
++ in order to avoid unnecessary fschk runs.
++
++config RTC_HCTOSYS_DEVICE
++ string "The RTC to read the time from"
++ depends on RTC_HCTOSYS = y
++ default "rtc0"
++ help
++ The RTC device that will be used as the source for
++ the system time, usually rtc0.
++
++comment "RTC interfaces"
++ depends on RTC_CLASS
++
++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.
++
++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.
++
++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
++
++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.
++
++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_PCF8563
++ tristate "Philips PCF8563/Epson RTC8564"
++ depends on RTC_CLASS && I2C
++ help
++ If you say yes here you get support for the
++ Philips PCF8563 RTC chip. The Epson RTC8564
++ should work as well.
++
++ This driver can also be built as a module. If so, the module
++ will be called rtc-pcf8563.
++
++config RTC_DRV_TEST
++ tristate "Test driver/device"
++ depends on RTC_CLASS
++ 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
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/Makefile 2006-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,17 @@
++#
++# Makefile for RTC class/drivers.
++#
++
++obj-y += utils.o
++obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
++obj-$(CONFIG_RTC_CLASS) += rtc-core.o
++rtc-core-y := class.o interface.o
++obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
++obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
++obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
++
++obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
++obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
++obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
++obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
++
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/interface.c 2006-02-07 01:32:10.000000000 +0100
+@@ -0,0 +1,232 @@
++/*
++ * RTC subsystem, interface functions
++ *
++ * Copyright (C) 2005 Tower Technologies
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ *
++ * based on arch/arm/common/rtctime.c
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++*/
++
++#include <linux/rtc.h>
++
++extern struct class *rtc_class;
++
++int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
++{
++ int err;
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ if ((err = down_interruptible(&rtc->ops_lock)))
++ return err;
++
++ if (!rtc->ops)
++ err = -ENODEV;
++ else if (!rtc->ops->read_time)
++ err = -EINVAL;
++ else {
++ memset(tm, 0, sizeof(struct rtc_time));
++ err = rtc->ops->read_time(class_dev->dev, tm);
++ }
++
++ up(&rtc->ops_lock);
++ return err;
++}
++EXPORT_SYMBOL(rtc_read_time);
++
++int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
++{
++ int err;
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ if ((err = rtc_valid_tm(tm)) != 0)
++ return err;
++
++ if ((err = down_interruptible(&rtc->ops_lock)))
++ return err;
++
++ if (!rtc->ops)
++ err = -ENODEV;
++ else if (!rtc->ops->set_time)
++ err = -EINVAL;
++ else
++ err = rtc->ops->set_time(class_dev->dev, tm);
++
++ up(&rtc->ops_lock);
++ return err;
++}
++EXPORT_SYMBOL(rtc_set_time);
++
++int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
++{
++ int err;
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ if ((err = down_interruptible(&rtc->ops_lock)))
++ return err;
++
++ if (rtc->ops == NULL)
++ err = -ENODEV;
++ else if (!rtc->ops->read_alarm)
++ err = -EINVAL;
++ else {
++ memset(alarm, 0, sizeof(struct rtc_wkalrm));
++ err = rtc->ops->read_alarm(class_dev->dev, alarm);
++ }
++
++ up(&rtc->ops_lock);
++ return err;
++}
++EXPORT_SYMBOL(rtc_read_alarm);
++
++int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
++{
++ int err;
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ if ((err = down_interruptible(&rtc->ops_lock)))
++ return err;
++
++ if (!rtc->ops)
++ err = -ENODEV;
++ else if (!rtc->ops->set_alarm)
++ err = -EINVAL;
++ else
++ err = rtc->ops->set_alarm(class_dev->dev, alarm);
++
++ up(&rtc->ops_lock);
++ return err;
++}
++EXPORT_SYMBOL(rtc_set_alarm);
++
++void rtc_update_irq(struct class_device *class_dev,
++ unsigned long num, unsigned long events)
++{
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ spin_lock(&rtc->irq_lock);
++ rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
++ spin_unlock(&rtc->irq_lock);
++
++ spin_lock(&rtc->irq_task_lock);
++ if (rtc->irq_task)
++ rtc->irq_task->func(rtc->irq_task->private_data);
++ spin_unlock(&rtc->irq_task_lock);
++
++ wake_up_interruptible(&rtc->irq_queue);
++ kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
++}
++EXPORT_SYMBOL(rtc_update_irq);
++
++struct class_device *rtc_open(char *name)
++{
++ struct class_device *class_dev = NULL,
++ *class_dev_tmp;
++
++ down(&rtc_class->sem);
++ list_for_each_entry(class_dev_tmp, &rtc_class->children, node) {
++ if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) {
++ class_dev = class_dev_tmp;
++ break;
++ }
++ }
++
++ if (class_dev) {
++ if (!try_module_get(to_rtc_device(class_dev)->owner))
++ class_dev = NULL;
++ }
++ up(&rtc_class->sem);
++
++ return class_dev;
++}
++EXPORT_SYMBOL(rtc_open);
++
++void rtc_close(struct class_device *class_dev)
++{
++ module_put(to_rtc_device(class_dev)->owner);
++}
++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-02-06 21:11:33.000000000 +0100
+@@ -0,0 +1,97 @@
++/*
++ * RTC subsystem, utility functions
++ *
++ * Copyright (C) 2005 Tower Technologies
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ *
++ * based on arch/arm/common/rtctime.c
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++*/
++
++#include <linux/rtc.h>
++
++static const unsigned char rtc_days_in_month[] = {
++ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
++};
++
++#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
++#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
++
++int rtc_month_days(unsigned int month, unsigned int year)
++{
++ return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
++}
++EXPORT_SYMBOL(rtc_month_days);
++
++/*
++ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
++ */
++void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
++{
++ int days, month, year;
++
++ days = time / 86400;
++ time -= days * 86400;
++
++ tm->tm_wday = (days + 4) % 7;
++
++ year = 1970 + days / 365;
++ days -= (year - 1970) * 365
++ + LEAPS_THRU_END_OF(year - 1)
++ - LEAPS_THRU_END_OF(1970 - 1);
++ if (days < 0) {
++ year -= 1;
++ days += 365 + LEAP_YEAR(year);
++ }
++ tm->tm_year = year - 1900;
++ tm->tm_yday = days + 1;
++
++ for (month = 0; month < 11; month++) {
++ int newdays;
++
++ newdays = days - rtc_month_days(month, year);
++ if (newdays < 0)
++ break;
++ days = newdays;
++ }
++ tm->tm_mon = month;
++ tm->tm_mday = days + 1;
++
++ tm->tm_hour = time / 3600;
++ time -= tm->tm_hour * 3600;
++ tm->tm_min = time / 60;
++ tm->tm_sec = time - tm->tm_min * 60;
++}
++EXPORT_SYMBOL(rtc_time_to_tm);
++
++/*
++ * Does the rtc_time represent a valid date/time?
++ */
++int rtc_valid_tm(struct rtc_time *tm)
++{
++ if (tm->tm_year < 70 ||
++ tm->tm_mon >= 12 ||
++ tm->tm_mday < 1 ||
++ tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900) ||
++ tm->tm_hour >= 24 ||
++ tm->tm_min >= 60 ||
++ tm->tm_sec >= 60)
++ return -EINVAL;
++
++ return 0;
++}
++EXPORT_SYMBOL(rtc_valid_tm);
++
++/*
++ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
++ */
++int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
++{
++ *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++ return 0;
++}
++EXPORT_SYMBOL(rtc_tm_to_time);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/hctosys.c 2006-02-06 21:11:33.000000000 +0100
+@@ -0,0 +1,62 @@
++/*
++ * RTC subsystem, initialize system time on startup
++ *
++ * Copyright (C) 2005 Tower Technologies
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++*/
++
++#include <linux/rtc.h>
++
++/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
++ * whether it stores the most close value or the value with partial
++ * seconds truncated. However, it is important that we use it to store
++ * the truncated value. This is because otherwise it is necessary,
++ * in an rtc sync function, to read both xtime.tv_sec and
++ * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
++ * of >32bits is not possible. So storing the most close value would
++ * slow down the sync API. So here we have the truncated value and
++ * the best guess is to add 0.5s.
++ */
++
++static int __init rtc_hctosys(void)
++{
++ int err;
++ struct class_device *class_dev = rtc_open(CONFIG_RTC_HCTOSYS_DEVICE);
++
++ if (class_dev) {
++ struct rtc_time tm;
++
++ if ((err = rtc_read_time(class_dev, &tm)) == 0) {
++ struct timespec tv;
++
++ tv.tv_nsec = NSEC_PER_SEC >> 1;
++
++ rtc_tm_to_time(&tm, &tv.tv_sec);
++
++ do_settimeofday(&tv);
++
++ dev_info(class_dev->dev,
++ "setting the system clock to "
++ "%d-%02d-%02d %02d:%02d:%02d (%u)\n",
++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++ tm.tm_hour, tm.tm_min, tm.tm_sec,
++ (unsigned int) tv.tv_sec);
++ }
++ else
++ dev_err(class_dev->dev,
++ "unable to set the system clock\n");
++
++ rtc_close(class_dev);
++ }
++ else
++ printk("%s: unable to open rtc device (%s)\n",
++ __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
++
++ return 0;
++}
++
++late_initcall(rtc_hctosys);
+--- linux-nslu2.orig/arch/arm/Kconfig 2006-02-06 23:52:28.000000000 +0100
++++ linux-nslu2/arch/arm/Kconfig 2006-02-06 23:53:25.000000000 +0100
+@@ -784,6 +784,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-02-06 23:52:28.000000000 +0100
++++ linux-nslu2/arch/arm/common/rtctime.c 2006-02-06 23:53:25.000000000 +0100
+@@ -42,89 +42,6 @@ static struct rtc_ops *rtc_ops;
+
+ #define rtc_epoch 1900UL
+
+-static const unsigned char days_in_month[] = {
+- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+-};
+-
+-#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+-#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+-
+-static int month_days(unsigned int month, unsigned int year)
+-{
+- return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+-}
+-
+-/*
+- * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+- */
+-void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+-{
+- int days, month, year;
+-
+- days = time / 86400;
+- time -= days * 86400;
+-
+- tm->tm_wday = (days + 4) % 7;
+-
+- year = 1970 + days / 365;
+- days -= (year - 1970) * 365
+- + LEAPS_THRU_END_OF(year - 1)
+- - LEAPS_THRU_END_OF(1970 - 1);
+- if (days < 0) {
+- year -= 1;
+- days += 365 + LEAP_YEAR(year);
+- }
+- tm->tm_year = year - 1900;
+- tm->tm_yday = days + 1;
+-
+- for (month = 0; month < 11; month++) {
+- int newdays;
+-
+- newdays = days - month_days(month, year);
+- if (newdays < 0)
+- break;
+- days = newdays;
+- }
+- tm->tm_mon = month;
+- tm->tm_mday = days + 1;
+-
+- tm->tm_hour = time / 3600;
+- time -= tm->tm_hour * 3600;
+- tm->tm_min = time / 60;
+- tm->tm_sec = time - tm->tm_min * 60;
+-}
+-EXPORT_SYMBOL(rtc_time_to_tm);
+-
+-/*
+- * Does the rtc_time represent a valid date/time?
+- */
+-int rtc_valid_tm(struct rtc_time *tm)
+-{
+- if (tm->tm_year < 70 ||
+- tm->tm_mon >= 12 ||
+- tm->tm_mday < 1 ||
+- tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) ||
+- tm->tm_hour >= 24 ||
+- tm->tm_min >= 60 ||
+- tm->tm_sec >= 60)
+- return -EINVAL;
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(rtc_valid_tm);
+-
+-/*
+- * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+- */
+-int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+-{
+- *time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+- tm->tm_hour, tm->tm_min, tm->tm_sec);
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(rtc_tm_to_time);
+-
+ /*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+@@ -143,13 +60,13 @@ void rtc_next_alarm_time(struct rtc_time
+ next->tm_sec = alrm->tm_sec;
+ }
+
+-static inline int rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
++static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm)
+ {
+ memset(tm, 0, sizeof(struct rtc_time));
+ return ops->read_time(tm);
+ }
+
+-static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
++static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm)
+ {
+ int ret;
+
+@@ -160,7 +77,7 @@ static inline int rtc_set_time(struct rt
+ return ret;
+ }
+
+-static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
++static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+ {
+ int ret = -EINVAL;
+ if (ops->read_alarm) {
+@@ -170,7 +87,7 @@ static inline int rtc_read_alarm(struct
+ return ret;
+ }
+
+-static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
++static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+ {
+ int ret = -EINVAL;
+ if (ops->set_alarm)
+@@ -258,7 +175,7 @@ static int rtc_ioctl(struct inode *inode
+
+ switch (cmd) {
+ case RTC_ALM_READ:
+- ret = rtc_read_alarm(ops, &alrm);
++ ret = rtc_arm_read_alarm(ops, &alrm);
+ if (ret)
+ break;
+ ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
+@@ -280,11 +197,11 @@ static int rtc_ioctl(struct inode *inode
+ alrm.time.tm_wday = -1;
+ alrm.time.tm_yday = -1;
+ alrm.time.tm_isdst = -1;
+- ret = rtc_set_alarm(ops, &alrm);
++ ret = rtc_arm_set_alarm(ops, &alrm);
+ break;
+
+ case RTC_RD_TIME:
+- ret = rtc_read_time(ops, &tm);
++ ret = rtc_arm_read_time(ops, &tm);
+ if (ret)
+ break;
+ ret = copy_to_user(uarg, &tm, sizeof(tm));
+@@ -302,7 +219,7 @@ static int rtc_ioctl(struct inode *inode
+ ret = -EFAULT;
+ break;
+ }
+- ret = rtc_set_time(ops, &tm);
++ ret = rtc_arm_set_time(ops, &tm);
+ break;
+
+ case RTC_EPOCH_SET:
+@@ -333,11 +250,11 @@ static int rtc_ioctl(struct inode *inode
+ ret = -EFAULT;
+ break;
+ }
+- ret = rtc_set_alarm(ops, &alrm);
++ ret = rtc_arm_set_alarm(ops, &alrm);
+ break;
+
+ case RTC_WKALM_RD:
+- ret = rtc_read_alarm(ops, &alrm);
++ ret = rtc_arm_read_alarm(ops, &alrm);
+ if (ret)
+ break;
+ ret = copy_to_user(uarg, &alrm, sizeof(alrm));
+@@ -353,7 +270,7 @@ static int rtc_ioctl(struct inode *inode
+ return ret;
+ }
+
+-static int rtc_open(struct inode *inode, struct file *file)
++static int rtc_arm_open(struct inode *inode, struct file *file)
+ {
+ int ret;
+
+@@ -408,7 +325,7 @@ static struct file_operations rtc_fops =
+ .read = rtc_read,
+ .poll = rtc_poll,
+ .ioctl = rtc_ioctl,
+- .open = rtc_open,
++ .open = rtc_arm_open,
+ .release = rtc_release,
+ .fasync = rtc_fasync,
+ };
+@@ -427,7 +344,7 @@ static int rtc_read_proc(char *page, cha
+ struct rtc_time tm;
+ char *p = page;
+
+- if (rtc_read_time(ops, &tm) == 0) {
++ if (rtc_arm_read_time(ops, &tm) == 0) {
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n"
+@@ -437,7 +354,7 @@ static int rtc_read_proc(char *page, cha
+ rtc_epoch);
+ }
+
+- if (rtc_read_alarm(ops, &alrm) == 0) {
++ if (rtc_arm_read_alarm(ops, &alrm) == 0) {
+ p += sprintf(p, "alrm_time\t: ");
+ if ((unsigned int)alrm.time.tm_hour <= 24)
+ p += sprintf(p, "%02d:", alrm.time.tm_hour);
+--- linux-nslu2.orig/include/asm-arm/rtc.h 2006-02-06 23:52:28.000000000 +0100
++++ linux-nslu2/include/asm-arm/rtc.h 2006-02-06 23:53:25.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 *);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-sysfs.c 2006-02-06 23:56:37.000000000 +0100
+@@ -0,0 +1,113 @@
++/*
++ * 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 = rtc_read_time(dev, &tm)) == 0) {
++ retval = sprintf(buf, "%04d-%02d-%02d\n",
++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++ }
++
++ return retval;
++}
++static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL);
++
++static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
++{
++ ssize_t retval = -ENODEV;
++ struct rtc_device *rtc = to_rtc_device(dev);
++ struct rtc_time tm;
++
++ if ((retval = rtc_read_time(dev, &tm)) == 0) {
++ retval = sprintf(buf, "%02d:%02d:%02d\n",
++ tm.tm_hour, tm.tm_min, tm.tm_sec);
++ }
++
++ return retval;
++}
++static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL);
++
++static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
++{
++ ssize_t retval = -ENODEV;
++ struct rtc_device *rtc = to_rtc_device(dev);
++ struct rtc_time tm;
++
++ if ((retval = rtc_read_time(dev, &tm)) == 0) {
++ unsigned long time;
++ rtc_tm_to_time(&tm, &time);
++ retval = sprintf(buf, "%lu\n", time);
++ }
++
++ return retval;
++}
++static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
++
++/* 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");
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-proc.c 2006-02-06 23:58:32.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");
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-dev.c 2006-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,366 @@
++/*
++ * 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);
++
++ data = rtc->irq_data;
++
++ return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
++}
++
++static int rtc_dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
++ unsigned long arg)
++{
++ int err = 0;
++ struct class_device *class_dev = file->private_data;
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++ struct rtc_class_ops *ops = rtc->ops;
++ struct rtc_time tm;
++ struct rtc_wkalrm alarm;
++ void __user *uarg = (void __user *) arg;
++
++ /* avoid conflicting IRQ users */
++ if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
++ spin_lock(&rtc->irq_task_lock);
++ if (rtc->irq_task)
++ err = -EBUSY;
++ spin_unlock(&rtc->irq_task_lock);
++
++ if (err < 0)
++ return err;
++ }
++
++ /* try the driver's ioctl interface */
++ if (ops->ioctl) {
++ err = ops->ioctl(class_dev->dev, cmd, arg);
++ if (err != -EINVAL)
++ return err;
++ }
++
++ /* if the driver does not provide the ioctl interface
++ * or if that particular ioctl was not implemented
++ * (-EINVAL), we will try to emulate here.
++ */
++
++ switch (cmd) {
++ case RTC_ALM_READ:
++ if ((err = rtc_read_alarm(class_dev, &alarm)) < 0)
++ return err;
++
++ if ((err = copy_to_user(uarg, &alarm.time, sizeof(tm))))
++ return -EFAULT;
++ break;
++
++ case RTC_ALM_SET:
++ if ((err = copy_from_user(&alarm.time, uarg, sizeof(tm))))
++ return -EFAULT;
++
++ alarm.enabled = 0;
++ alarm.pending = 0;
++ alarm.time.tm_mday = -1;
++ alarm.time.tm_mon = -1;
++ alarm.time.tm_year = -1;
++ alarm.time.tm_wday = -1;
++ alarm.time.tm_yday = -1;
++ alarm.time.tm_isdst = -1;
++ err = rtc_set_alarm(class_dev, &alarm);
++ break;
++
++ case RTC_RD_TIME:
++ if ((err = rtc_read_time(class_dev, &tm)) < 0)
++ return err;
++
++ if ((err = copy_to_user(uarg, &tm, sizeof(tm))))
++ return -EFAULT;
++ break;
++
++ case RTC_SET_TIME:
++ if (!capable(CAP_SYS_TIME))
++ return -EACCES;
++
++ if ((err = copy_from_user(&tm, uarg, sizeof(tm))))
++ return -EFAULT;
++
++ err = rtc_set_time(class_dev, &tm);
++ break;
++#if 0
++ case RTC_EPOCH_SET:
++#ifndef rtc_epoch
++ /*
++ * There were no RTC clocks before 1900.
++ */
++ if (arg < 1900) {
++ err = -EINVAL;
++ break;
++ }
++ if (!capable(CAP_SYS_TIME)) {
++ err = -EACCES;
++ break;
++ }
++ rtc_epoch = arg;
++ err = 0;
++#endif
++ break;
++
++ case RTC_EPOCH_READ:
++ err = put_user(rtc_epoch, (unsigned long __user *)uarg);
++ break;
++#endif
++ case RTC_WKALM_SET:
++ if ((err = copy_from_user(&alarm, uarg, sizeof(alarm))))
++ return -EFAULT;
++
++ err = rtc_set_alarm(class_dev, &alarm);
++ break;
++
++ case RTC_WKALM_RD:
++ if ((err = rtc_read_alarm(class_dev, &alarm)) < 0)
++ return err;
++
++ if ((err = copy_to_user(uarg, &alarm, sizeof(alarm))))
++ return -EFAULT;
++ break;
++
++ default:
++ err = -EINVAL;
++ break;
++ }
++
++ return err;
++}
++
++static int rtc_dev_release(struct inode *inode, struct file *file)
++{
++ struct rtc_device *rtc = to_rtc_device(file->private_data);
++
++ if (rtc->ops->release)
++ rtc->ops->release(rtc->class_dev.dev);
++
++ 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_uevent(&class_dev->kobj, KOBJ_ADD);
++
++ return 0;
++}
++
++static void rtc_dev_remove_device(struct class_device *class_dev,
++ struct class_interface *class_intf)
++{
++ struct rtc_device *rtc = to_rtc_device(class_dev);
++
++ class_device_remove_file(class_dev, &class_device_attr_dev);
++
++ if (MAJOR(class_dev->devt)) {
++ dev_dbg(class_dev->dev, "removing char %d:%d\n",
++ MAJOR(class_dev->devt),
++ MINOR(class_dev->devt));
++ cdev_del(&rtc->char_dev);
++
++ kobject_uevent(&class_dev->kobj, KOBJ_REMOVE);
++
++ class_dev->devt = MKDEV(0, 0);
++ }
++}
++
++/* interface registration */
++
++struct class_interface rtc_dev_interface = {
++ .add = &rtc_dev_add_device,
++ .remove = &rtc_dev_remove_device,
++};
++
++static int __init rtc_dev_init(void)
++{
++ int err;
++
++ if ((err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc")) < 0) {
++ printk(KERN_ERR "%s: failed to allocate char dev region\n",
++ __FILE__);
++ return err;
++ }
++
++ if ((err = rtc_interface_register(&rtc_dev_interface)) < 0) {
++ printk(KERN_ERR "%s: failed to register the interface\n",
++ __FILE__);
++ unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
++ return err;
++ }
++
++ return 0;
++}
++
++static void __exit rtc_dev_exit(void)
++{
++ class_interface_unregister(&rtc_dev_interface);
++
++ unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
++}
++
++module_init(rtc_dev_init);
++module_exit(rtc_dev_exit);
++
++MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
++MODULE_DESCRIPTION("RTC class dev interface");
++MODULE_LICENSE("GPL");
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-x1205.c 2006-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,672 @@
++/*
++ * 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;
++
++/* offsets into CCR area */
++
++#define CCR_SEC 0
++#define CCR_MIN 1
++#define CCR_HOUR 2
++#define CCR_MDAY 3
++#define CCR_MONTH 4
++#define CCR_YEAR 5
++#define CCR_WDAY 6
++#define CCR_Y2K 7
++
++#define X1205_REG_SR 0x3F /* status register */
++#define X1205_REG_Y2K 0x37
++#define X1205_REG_DW 0x36
++#define X1205_REG_YR 0x35
++#define X1205_REG_MO 0x34
++#define X1205_REG_DT 0x33
++#define X1205_REG_HR 0x32
++#define X1205_REG_MN 0x31
++#define X1205_REG_SC 0x30
++#define X1205_REG_DTR 0x13
++#define X1205_REG_ATR 0x12
++#define X1205_REG_INT 0x11
++#define X1205_REG_0 0x10
++#define X1205_REG_Y2K1 0x0F
++#define X1205_REG_DWA1 0x0E
++#define X1205_REG_YRA1 0x0D
++#define X1205_REG_MOA1 0x0C
++#define X1205_REG_DTA1 0x0B
++#define X1205_REG_HRA1 0x0A
++#define X1205_REG_MNA1 0x09
++#define X1205_REG_SCA1 0x08
++#define X1205_REG_Y2K0 0x07
++#define X1205_REG_DWA0 0x06
++#define X1205_REG_YRA0 0x05
++#define X1205_REG_MOA0 0x04
++#define X1205_REG_DTA0 0x03
++#define X1205_REG_HRA0 0x02
++#define X1205_REG_MNA0 0x01
++#define X1205_REG_SCA0 0x00
++
++#define X1205_CCR_BASE 0x30 /* Base address of CCR */
++#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */
++
++#define X1205_SR_RTCF 0x01 /* Clock failure */
++#define X1205_SR_WEL 0x02 /* Write Enable Latch */
++#define X1205_SR_RWEL 0x04 /* Register Write Enable */
++
++#define X1205_DTR_DTR0 0x01
++#define X1205_DTR_DTR1 0x02
++#define X1205_DTR_DTR2 0x04
++
++#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */
++
++/* Prototypes */
++static int x1205_attach(struct i2c_adapter *adapter);
++static int x1205_detach(struct i2c_client *client);
++static int x1205_probe(struct i2c_adapter *adapter, int address, int kind);
++
++static struct i2c_driver x1205_driver = {
++ .driver = {
++ .name = "x1205",
++ },
++ .attach_adapter = &x1205_attach,
++ .detach_client = &x1205_detach,
++};
++
++/*
++ * In the routines that deal directly with the x1205 hardware, we use
++ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
++ * Epoch is initialized as 2000. Time is set to UTC.
++ */
++static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
++ unsigned char reg_base)
++{
++ unsigned char dt_addr[2] = { 0, reg_base };
++
++ unsigned char buf[8];
++
++ struct i2c_msg msgs[] = {
++ { client->addr, 0, 2, dt_addr }, /* setup read ptr */
++ { client->addr, I2C_M_RD, 8, buf }, /* read date */
++ };
++
++ /* read date registers */
++ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
++ dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
++ return -EIO;
++ }
++
++ dev_dbg(&client->dev,
++ "%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
++ "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
++ __FUNCTION__,
++ buf[0], buf[1], buf[2], buf[3],
++ buf[4], buf[5], buf[6], buf[7]);
++
++ tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
++ tm->tm_min = BCD2BIN(buf[CCR_MIN]);
++ tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
++ tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
++ tm->tm_mon = BCD2BIN(buf[CCR_MONTH]) - 1; /* mon is 0-11 */
++ tm->tm_year = BCD2BIN(buf[CCR_YEAR])
++ + (BCD2BIN(buf[CCR_Y2K]) * 100) - 1900;
++ tm->tm_wday = buf[CCR_WDAY];
++
++ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ return 0;
++}
++
++static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
++{
++ static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
++
++ struct i2c_msg msgs[] = {
++ { client->addr, 0, 2, sr_addr }, /* setup read ptr */
++ { client->addr, I2C_M_RD, 1, sr }, /* read status */
++ };
++
++ /* read status register */
++ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
++ dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
++ int datetoo, u8 reg_base)
++{
++ int i, xfer;
++ unsigned char buf[8];
++
++ static const unsigned char wel[3] = { 0, X1205_REG_SR,
++ X1205_SR_WEL };
++
++ static const unsigned char rwel[3] = { 0, X1205_REG_SR,
++ X1205_SR_WEL | X1205_SR_RWEL };
++
++ static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
++
++ dev_dbg(&client->dev,
++ "%s: secs=%d, mins=%d, hours=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour);
++
++ buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
++ buf[CCR_MIN] = BIN2BCD(tm->tm_min);
++
++ /* set hour and 24hr bit */
++ buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL;
++
++ /* should we also set the date? */
++ if (datetoo) {
++ dev_dbg(&client->dev,
++ "%s: mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
++
++ /* month, 1 - 12 */
++ buf[CCR_MONTH] = BIN2BCD(tm->tm_mon + 1);
++
++ /* year, since the rtc epoch*/
++ buf[CCR_YEAR] = BIN2BCD(tm->tm_year % 100);
++ buf[CCR_WDAY] = tm->tm_wday & 0x07;
++ buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100);
++ }
++
++ /* this sequence is required to unlock the chip */
++ 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;
++}
++
++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.driver.name, I2C_NAME_SIZE);
++
++ /* Verify the chip is really an X1205 */
++ if (kind < 0) {
++ if (x1205_validate_client(client) < 0) {
++ err = -ENODEV;
++ goto exit_kfree;
++ }
++ }
++
++ /* Inform the i2c layer */
++ if ((err = i2c_attach_client(client)))
++ goto exit_kfree;
++
++ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
++
++ rtc = rtc_device_register(x1205_driver.driver.name, &client->dev,
++ &x1205_rtc_ops, THIS_MODULE);
++
++ if (IS_ERR(rtc)) {
++ err = PTR_ERR(rtc);
++ dev_err(&client->dev,
++ "unable to register the class device\n");
++ goto exit_detach;
++ }
++
++ i2c_set_clientdata(client, rtc);
++
++ /* Check for power failures and eventualy enable the osc */
++ if ((err = x1205_get_status(client, &sr)) == 0) {
++ if (sr & X1205_SR_RTCF) {
++ dev_err(&client->dev,
++ "power failure detected, "
++ "please set the clock\n");
++ udelay(50);
++ x1205_fix_osc(client);
++ }
++ }
++ else
++ dev_err(&client->dev, "couldn't read status\n");
++
++ device_create_file(&client->dev, &dev_attr_atrim);
++ device_create_file(&client->dev, &dev_attr_dtrim);
++
++ return 0;
++
++exit_detach:
++ i2c_detach_client(client);
++
++exit_kfree:
++ kfree(client);
++
++exit:
++ return err;
++}
++
++static int x1205_detach(struct i2c_client *client)
++{
++ int err;
++ struct rtc_device *rtc = i2c_get_clientdata(client);
++
++ dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++
++ if (rtc)
++ rtc_device_unregister(rtc);
++
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ kfree(client);
++
++ return 0;
++}
++
++static int __init x1205_init(void)
++{
++ return i2c_add_driver(&x1205_driver);
++}
++
++static void __exit x1205_exit(void)
++{
++ i2c_del_driver(&x1205_driver);
++}
++
++MODULE_AUTHOR(
++ "Karen Spearel <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);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-test.c 2006-02-06 23:58:32.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);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-ds1672.c 2006-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,234 @@
++/*
++ * An rtc/i2c driver for the Dallas DS1672
++ * Copyright 2005 Alessandro Zummo
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/rtc.h>
++
++#define DRV_VERSION "0.2"
++
++/* Addresses to scan: none. This chip cannot be detected. */
++static unsigned short normal_i2c[] = { I2C_CLIENT_END };
++
++/* Insmod parameters */
++I2C_CLIENT_INSMOD;
++
++/* Registers */
++
++#define DS1672_REG_CNT_BASE 0
++#define DS1672_REG_CONTROL 4
++#define DS1672_REG_TRICKLE 5
++
++
++/* Prototypes */
++static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind);
++
++/*
++ * In the routines that deal directly with the ds1672 hardware, we use
++ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
++ * Epoch is initialized as 2000. Time is set to UTC.
++ */
++static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
++{
++ unsigned long time;
++ unsigned char addr = DS1672_REG_CNT_BASE;
++ unsigned char buf[4];
++
++ struct i2c_msg msgs[] = {
++ { client->addr, 0, 1, &addr }, /* setup read ptr */
++ { client->addr, I2C_M_RD, 4, buf }, /* read date */
++ };
++
++ /* read date registers */
++ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
++ dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
++ return -EIO;
++ }
++
++ dev_dbg(&client->dev,
++ "%s: raw read data - counters=%02x,%02x,%02x,%02x\n"
++ __FUNCTION__,
++ buf[0], buf[1], buf[2], buf[3]);
++
++ time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
++
++ rtc_time_to_tm(time, tm);
++
++ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ return 0;
++}
++
++static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
++{
++ int xfer;
++ unsigned char buf[5];
++
++ buf[0] = DS1672_REG_CNT_BASE;
++ buf[1] = secs & 0x000000FF;
++ buf[2] = (secs & 0x0000FF00) >> 8;
++ buf[3] = (secs & 0x00FF0000) >> 16;
++ buf[4] = (secs & 0xFF000000) >> 24;
++
++ xfer = i2c_master_send(client, buf, 5);
++ if (xfer != 5) {
++ dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm)
++{
++ unsigned long secs;
++
++ dev_dbg(&client->dev,
++ "%s: secs=%d, mins=%d, hours=%d, ",
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ rtc_tm_to_time(tm, &secs);
++
++ return ds1672_set_mmss(client, secs);
++}
++
++static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ return ds1672_get_datetime(to_i2c_client(dev), tm);
++}
++
++static int ds1672_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ return ds1672_set_datetime(to_i2c_client(dev), tm);
++}
++
++static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs)
++{
++ return ds1672_set_mmss(to_i2c_client(dev), secs);
++}
++
++static struct rtc_class_ops ds1672_rtc_ops = {
++ .read_time = ds1672_rtc_read_time,
++ .set_time = ds1672_rtc_set_time,
++ .set_mmss = ds1672_rtc_set_mmss,
++};
++
++static int ds1672_attach(struct i2c_adapter *adapter)
++{
++ dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
++
++ return i2c_probe(adapter, &addr_data, ds1672_probe);
++}
++
++static int ds1672_detach(struct i2c_client *client)
++{
++ int err;
++ struct rtc_device *rtc = i2c_get_clientdata(client);
++
++ dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++
++ if (rtc)
++ rtc_device_unregister(rtc);
++
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ kfree(client);
++
++ return 0;
++}
++
++static struct i2c_driver ds1672_driver = {
++ .driver = {
++ .name = "ds1672",
++ },
++ .attach_adapter = &ds1672_attach,
++ .detach_client = &ds1672_detach,
++};
++
++static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
++{
++ int err = 0;
++ struct i2c_client *client;
++ struct rtc_device *rtc;
++
++ dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
++ err = -ENODEV;
++ goto exit;
++ }
++
++ if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto exit;
++ }
++
++ /* I2C client */
++ client->addr = address;
++ client->driver = &ds1672_driver;
++ client->adapter = adapter;
++
++ strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE);
++
++ /* Inform the i2c layer */
++ if ((err = i2c_attach_client(client)))
++ goto exit_kfree;
++
++ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
++
++ rtc = rtc_device_register(ds1672_driver.driver.name, &client->dev,
++ &ds1672_rtc_ops, THIS_MODULE);
++
++ if (IS_ERR(rtc)) {
++ err = PTR_ERR(rtc);
++ dev_err(&client->dev,
++ "unable to register the class device\n");
++ goto exit_detach;
++ }
++
++ i2c_set_clientdata(client, rtc);
++
++ return 0;
++
++exit_detach:
++ i2c_detach_client(client);
++
++exit_kfree:
++ kfree(client);
++
++exit:
++ return err;
++}
++
++static int __init ds1672_init(void)
++{
++ return i2c_add_driver(&ds1672_driver);
++}
++
++static void __exit ds1672_exit(void)
++{
++ i2c_del_driver(&ds1672_driver);
++}
++
++MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
++MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(DRV_VERSION);
++
++module_init(ds1672_init);
++module_exit(ds1672_exit);
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-nslu2/drivers/rtc/rtc-pcf8563.c 2006-02-06 23:58:32.000000000 +0100
+@@ -0,0 +1,384 @@
++/*
++ * An I2C driver for the Philips PCF8563 RTC
++ * Copyright 2005-06 Tower Technologies
++ *
++ * Author: Alessandro Zummo <a.zummo@towertech.it>
++ * Maintainers: http://www.nslu2-linux.org/
++ *
++ * based on the other drivers in this same directory.
++ *
++ * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/bcd.h>
++#include <linux/rtc.h>
++
++#define DRV_VERSION "0.4.0"
++
++/* Addresses to scan */
++static unsigned short normal_i2c[] = { 0x51, I2C_CLIENT_END };
++
++/* Module parameters */
++I2C_CLIENT_INSMOD;
++
++#define PCF8563_REG_ST1 0x00 /* status */
++#define PCF8563_REG_ST2 0x01
++
++#define PCF8563_REG_SC 0x02 /* datetime */
++#define PCF8563_REG_MN 0x03
++#define PCF8563_REG_HR 0x04
++#define PCF8563_REG_DM 0x05
++#define PCF8563_REG_DW 0x06
++#define PCF8563_REG_MO 0x07
++#define PCF8563_REG_YR 0x08
++
++#define PCF8563_REG_AMN 0x09 /* alarm */
++#define PCF8563_REG_AHR 0x0A
++#define PCF8563_REG_ADM 0x0B
++#define PCF8563_REG_ADW 0x0C
++
++#define PCF8563_REG_CLKO 0x0D /* clock out */
++#define PCF8563_REG_TMRC 0x0E /* timer control */
++#define PCF8563_REG_TMR 0x0F /* timer */
++
++#define PCF8563_SC_LV 0x80 /* low voltage */
++#define PCF8563_MO_C 0x80 /* century */
++
++/* Prototypes */
++static int pcf8563_attach(struct i2c_adapter *adapter);
++static int pcf8563_detach(struct i2c_client *client);
++static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind);
++
++static struct i2c_driver pcf8563_driver = {
++ .driver = {
++ .name = "pcf8563",
++ },
++ .attach_adapter = &pcf8563_attach,
++ .detach_client = &pcf8563_detach,
++};
++
++/*
++ * In the routines that deal directly with the pcf8563 hardware, we use
++ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
++ */
++static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
++{
++ unsigned char buf[13];
++ unsigned char addr = PCF8563_REG_ST1;
++
++ struct i2c_msg msgs[] = {
++ { client->addr, 0, 1, &addr }, /* setup read ptr */
++ { client->addr, I2C_M_RD, 13, buf }, /* read status + date */
++ };
++
++ /* read registers */
++ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
++ dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
++ return -EIO;
++ }
++
++ if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
++ dev_info(&client->dev,
++ "low voltage detected, date/time is not reliable.\n");
++
++ dev_dbg(&client->dev,
++ "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
++ "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
++ __FUNCTION__,
++ buf[0], buf[1], buf[2], buf[3],
++ buf[4], buf[5], buf[6], buf[7],
++ buf[8]);
++
++
++ tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
++ tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
++ tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
++ tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
++ tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
++ tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
++ tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
++ + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 100 : 0);
++
++ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ /* the clock can give out invalid datetime, but we cannot return
++ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
++ */
++ if (rtc_valid_tm(tm) < 0)
++ dev_err(&client->dev, "retrieved date/time is not valid.\n");
++
++ return 0;
++}
++
++static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
++{
++ int i, err;
++ unsigned char buf[9];
++
++ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
++ "mday=%d, mon=%d, year=%d, wday=%d\n",
++ __FUNCTION__,
++ tm->tm_sec, tm->tm_min, tm->tm_hour,
++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
++
++ /* hours, minutes and seconds */
++ buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
++ buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
++ buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
++
++ buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
++
++ /* month, 1 - 12 */
++ buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
++
++ /* year and century */
++ buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
++ if (tm->tm_year / 100)
++ buf[PCF8563_REG_MO] |= PCF8563_MO_C;
++
++ buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
++
++ /* write register's data */
++ for (i = 0; i < 7; i++) {
++ unsigned char data[2] = { PCF8563_REG_SC + i,
++ buf[PCF8563_REG_SC + i] };
++
++ err = i2c_master_send(client, data, sizeof(data));
++ if (err != sizeof(data)) {
++ dev_err(&client->dev,
++ "%s: err=%d addr=%02x, data=%02x\n",
++ __FUNCTION__, err, data[0], data[1]);
++ return -EIO;
++ }
++ };
++
++ return 0;
++}
++
++struct pcf8563_limit
++{
++ unsigned char reg;
++ unsigned char mask;
++ unsigned char min;
++ unsigned char max;
++};
++
++static int pcf8563_validate_client(struct i2c_client *client)
++{
++ int i, xfer;
++
++ static const struct pcf8563_limit probe_limits_pattern[] = {
++ /* register, mask, min, max */
++ { PCF8563_REG_SC, 0x7F, 0, 59 },
++ { PCF8563_REG_MN, 0x7F, 0, 59 },
++ { PCF8563_REG_HR, 0x3F, 0, 23 },
++ { PCF8563_REG_DM, 0x3F, 0, 31 },
++ { PCF8563_REG_MO, 0x1F, 0, 12 },
++ { PCF8563_REG_YR, 0xFF, 0, 99 },
++ };
++
++ /* check limits (only registers with bcd values) */
++ for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
++ unsigned char addr, buf, value;
++
++ addr = probe_limits_pattern[i].reg;
++
++ struct i2c_msg msgs[2] = {
++ { client->addr, 0, 2, &addr },
++ { client->addr, I2C_M_RD, 1, &buf },
++ };
++
++ xfer = i2c_transfer(client->adapter, msgs, 2);
++
++ if (xfer != 2) {
++ dev_err(&client->adapter->dev,
++ "%s: could not read register %x\n",
++ __FUNCTION__, probe_limits_pattern[i].reg);
++
++ return -EIO;
++ }
++
++ value = BCD2BIN(buf & probe_limits_pattern[i].mask);
++
++ if (value > probe_limits_pattern[i].max ||
++ value < probe_limits_pattern[i].min) {
++ dev_dbg(&client->adapter->dev,
++ "%s: register=%x, lim pattern=%d, value=%d\n",
++ __FUNCTION__, probe_limits_pattern[i].reg, i, value);
++
++ return -ENODEV;
++ }
++ }
++
++ return 0;
++}
++
++static int pcf8563_rtc_read_time(struct device *dev,
++ struct rtc_time *tm)
++{
++ return pcf8563_get_datetime(to_i2c_client(dev), tm);
++}
++
++static int pcf8563_rtc_set_time(struct device *dev,
++ struct rtc_time *tm)
++{
++ return pcf8563_set_datetime(to_i2c_client(dev), tm);
++}
++
++static int pcf8563_rtc_set_mmss(struct device *dev, unsigned long secs)
++{
++ int err;
++
++ struct rtc_time new_tm, old_tm;
++
++ if ((err = pcf8563_rtc_read_time(dev, &old_tm) == 0))
++ return err;
++
++ /* FIXME xtime.tv_nsec = old_tm.tm_sec * 10000000; */
++ new_tm.tm_sec = secs % 60;
++ secs /= 60;
++ new_tm.tm_min = secs % 60;
++ secs /= 60;
++ new_tm.tm_hour = secs % 24;
++
++ /*
++ * avoid writing when we're going to change the day
++ * of the month. We will retry in the next minute.
++ * This basically means that if the RTC must not drift
++ * by more than 1 minute in 11 minutes.
++ */
++ if ((old_tm.tm_hour == 23 && old_tm.tm_min == 59) ||
++ (new_tm.tm_hour == 23 && new_tm.tm_min == 59))
++ return 1;
++
++ return pcf8563_rtc_set_time(dev, &new_tm);
++}
++
++static int pcf8563_rtc_proc(struct device *dev, struct seq_file *seq)
++{
++ seq_printf(seq, "24hr\t\t: yes\n");
++ return 0;
++}
++
++static struct rtc_class_ops pcf8563_rtc_ops = {
++ .proc = pcf8563_rtc_proc,
++ .read_time = pcf8563_rtc_read_time,
++ .set_time = pcf8563_rtc_set_time,
++ .set_mmss = pcf8563_rtc_set_mmss,
++};
++
++static int pcf8563_attach(struct i2c_adapter *adapter)
++{
++ return i2c_probe(adapter, &addr_data, pcf8563_probe);
++}
++
++static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *client;
++ struct rtc_device *rtc;
++
++ int err = 0;
++
++ dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
++ err = -ENODEV;
++ goto exit;
++ }
++
++ if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto exit;
++ }
++
++ client->addr = address;
++ client->driver = &pcf8563_driver;
++ client->adapter = adapter;
++
++ strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE);
++
++ /* Verify the chip is really an PCF8563 */
++ if (kind < 0) {
++ if (pcf8563_validate_client(client) < 0) {
++ err = -ENODEV;
++ goto exit_kfree;
++ }
++ }
++
++ /* Inform the i2c layer */
++ if ((err = i2c_attach_client(client)))
++ goto exit_kfree;
++
++ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
++
++ rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev,
++ &pcf8563_rtc_ops, THIS_MODULE);
++
++ if (IS_ERR(rtc)) {
++ err = PTR_ERR(rtc);
++ dev_err(&client->dev,
++ "unable to register the class device\n");
++ goto exit_detach;
++ }
++
++ i2c_set_clientdata(client, rtc);
++
++ return 0;
++
++exit_detach:
++ i2c_detach_client(client);
++
++exit_kfree:
++ kfree(client);
++
++exit:
++ return err;
++}
++
++static int pcf8563_detach(struct i2c_client *client)
++{
++ int err;
++ struct rtc_device *rtc = i2c_get_clientdata(client);
++
++ dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++
++ if (rtc)
++ rtc_device_unregister(rtc);
++
++ if ((err = i2c_detach_client(client)))
++ return err;
++
++ kfree(client);
++
++ return 0;
++}
++
++static int __init pcf8563_init(void)
++{
++ return i2c_add_driver(&pcf8563_driver);
++}
++
++static void __exit pcf8563_exit(void)
++{
++ i2c_del_driver(&pcf8563_driver);
++}
++
++MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
++MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(DRV_VERSION);
++
++module_init(pcf8563_init);
++module_exit(pcf8563_exit);
++