--- linux-nslu2.orig/include/linux/rtc.h 2006-01-03 15:31:18.000000000 +0100 +++ linux-nslu2/include/linux/rtc.h 2006-01-03 15:34:24.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 +#include +#include +#include + +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 rw_semaphore lock; + struct class_device class_dev; + 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-03 15:31:19.000000000 +0100 +++ linux-nslu2/drivers/Kconfig 2006-01-04 01:27:31.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-03 15:33:32.000000000 +0100 +++ linux-nslu2/drivers/Makefile 2006-01-04 01:27:31.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-03 15:45:19.000000000 +0100 @@ -0,0 +1,141 @@ +/* + * RTC subsystem, base class + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 +#include +#include +#include + +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_rwsem(&rtc->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_write(&rtc->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 "); +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-04 01:27:21.000000000 +0100 @@ -0,0 +1,93 @@ +# +# 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 + +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_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-01-04 01:27:21.000000000 +0100 @@ -0,0 +1,15 @@ +# +# Makefile for RTC class/drivers. +# + +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 +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/interface.c 2006-01-03 15:34:24.000000000 +0100 @@ -0,0 +1,189 @@ +/* + * RTC subsystem, interface functions + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 + +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-03 15:34:24.000000000 +0100 @@ -0,0 +1,97 @@ +/* + * RTC subsystem, utility functions + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 + +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); --- linux-nslu2.orig/arch/arm/Kconfig 2006-01-04 01:27:04.000000000 +0100 +++ linux-nslu2/arch/arm/Kconfig 2006-01-04 01:27:31.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 *); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/drivers/rtc/rtc-sysfs.c 2006-01-04 01:27:12.000000000 +0100 @@ -0,0 +1,125 @@ +/* + * RTC subsystem, sysfs interface + * + * Copyright (C) 2005 Tower Technologies + * Author: 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; version 2 of the License. +*/ + +#include +#include + +/* 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 = 0; + struct rtc_device *rtc = to_rtc_device(dev); + struct rtc_time tm; + + if (down_read_trylock(&rtc->lock) == 0) + return -ENODEV; + + if (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_read(&rtc->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 = 0; + struct rtc_device *rtc = to_rtc_device(dev); + struct rtc_time tm; + + if (down_read_trylock(&rtc->lock) == 0) + return -ENODEV; + + if (rtc_read_time(dev, &tm) == 0) { + retval = sprintf(buf, "%02d:%02d:%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec); + } + + up_read(&rtc->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 = 0; + struct rtc_device *rtc = to_rtc_device(dev); + struct rtc_time tm; + + if (down_read_trylock(&rtc->lock) == 0) + return -ENODEV; + + if (rtc_read_time(dev, &tm) == 0) { + unsigned long time; + rtc_tm_to_time(&tm, &time); + retval = sprintf(buf, "%lu\n", time); + } + + up_read(&rtc->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 "); +MODULE_DESCRIPTION("RTC class sysfs interface"); +MODULE_LICENSE("GPL"); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/drivers/rtc/rtc-proc.c 2006-01-04 01:27:14.000000000 +0100 @@ -0,0 +1,158 @@ +/* + * RTC subsystem, proc interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("RTC class proc interface"); +MODULE_LICENSE("GPL"); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/drivers/rtc/rtc-dev.c 2006-01-04 01:27:15.000000000 +0100 @@ -0,0 +1,372 @@ +/* + * RTC subsystem, dev interface + * + * Copyright (C) 2005 Tower Technologies + * Author: Alessandro Zummo + * + * 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 +#include + +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 "); +MODULE_DESCRIPTION("RTC class dev interface"); +MODULE_LICENSE("GPL"); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/drivers/rtc/rtc-x1205.c 2006-01-04 01:27:17.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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 %d-%d-%d %d:%d:%d\n", + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec); + + return 0; +} + +struct x1205_limit +{ + unsigned char reg; + unsigned char mask; + unsigned char min; + unsigned char max; +}; + +static int x1205_validate_client(struct i2c_client *client) +{ + int i, xfer; + + /* Probe array. We will read the register at the specified + * address and check if the given bits are zero. + */ + static const unsigned char probe_zero_pattern[] = { + /* register, mask */ + X1205_REG_SR, 0x18, + X1205_REG_DTR, 0xF8, + X1205_REG_ATR, 0xC0, + X1205_REG_INT, 0x18, + X1205_REG_0, 0xFF, + }; + + static const struct x1205_limit probe_limits_pattern[] = { + /* register, mask, min, max */ + { X1205_REG_Y2K, 0xFF, 19, 20 }, + { X1205_REG_DW, 0xFF, 0, 6 }, + { X1205_REG_YR, 0xFF, 0, 99 }, + { X1205_REG_MO, 0xFF, 0, 12 }, + { X1205_REG_DT, 0xFF, 0, 31 }, + { X1205_REG_HR, 0x7F, 0, 23 }, + { X1205_REG_MN, 0xFF, 0, 59 }, + { X1205_REG_SC, 0xFF, 0, 59 }, + { X1205_REG_Y2K1, 0xFF, 19, 20 }, + { X1205_REG_Y2K0, 0xFF, 19, 20 }, + }; + + /* check that registers have bits a 0 where expected */ + for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) { + unsigned char buf; + + unsigned char addr[2] = { 0, probe_zero_pattern[i] }; + + struct i2c_msg msgs[2] = { + { client->addr, 0, 2, addr }, + { client->addr, I2C_M_RD, 1, &buf }, + }; + + xfer = i2c_transfer(client->adapter, msgs, 2); + if (xfer != 2) { + dev_err(&client->adapter->dev, + "%s: could not read register %x\n", + __FUNCTION__, addr[1]); + + return -EIO; + } + + if ((buf & probe_zero_pattern[i+1]) != 0) { + dev_err(&client->adapter->dev, + "%s: register=%02x, zero pattern=%d, value=%x\n", + __FUNCTION__, addr[1], i, buf); + + return -ENODEV; + } + } + + /* check limits (only registers with bcd values) */ + for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) { + unsigned char reg, value; + + unsigned char addr[2] = { 0, probe_limits_pattern[i].reg }; + + struct i2c_msg msgs[2] = { + { client->addr, 0, 2, addr }, + { client->addr, I2C_M_RD, 1, ® }, + }; + + 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 , " + "Alessandro Zummo "); +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-01-04 01:27:19.000000000 +0100 @@ -0,0 +1,189 @@ +/* + * An RTC test device/driver + * Copyright (C) 2005 Tower Technologies + * Author: 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 +#include +#include +#include + +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 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 void test_release(struct device * dev) +{ +} + +struct platform_device test_dev_zero = { + .name = "rtc-test", + .id = 0, + .dev.release = test_release, +}; + +struct platform_device test_dev_one = { + .name = "rtc-test", + .id = 1, + .dev.release = test_release, +}; + +struct platform_driver test_drv = { + .probe = test_probe, + .remove = test_remove, + .driver = { + .name = "rtc-test", + .owner = THIS_MODULE, + }, +}; + +static int __init test_init(void) +{ + platform_device_register(&test_dev_zero); + platform_device_register(&test_dev_one); + platform_driver_register(&test_drv); + + return 0; +} + +static void __exit test_exit(void) +{ + platform_driver_unregister(&test_drv); + platform_device_unregister(&test_dev_zero); + platform_device_unregister(&test_dev_one); +} + +MODULE_AUTHOR("Alessandro Zummo "); +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-01-04 01:27:21.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 +#include +#include + +#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 "); +MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ds1672_init); +module_exit(ds1672_exit);