: Add support for Dallas/Maxim DS1685/1687 RTC
PATCH 1/2 : Add support for Dallas/Maxim DS1685/1687 RTC
Add Dallas/Maxim DS1685/1687 RTC Support.
--- drivers/rtc/Kconfig | 13 drivers/rtc/Makefile | 1 drivers/rtc/rtc-ds1685.c | 875 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtc/ds1685.h | 401 ++++++++++++++++++++ 4 files changed, 1290 insertions(+)
drivers/rtc/Kconfig +13 -0
@@ -499,6 +499,19 @@ config RTC_DRV_DS1553 This driver can also be built as a module. If so, the module will be called rtc-ds1553. +config RTC_DRV_DS1685 + tristate "Dallas/Maxim DS1685/DS1687" + depends on I2C + help + If you say yes here you get support for the Dallas/Maxim + DS1685/DS1687 timekeeping chip. + + Systems that use this chip include EPPC-405-UC modules, by + electronic system design GmbH. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1685. + config RTC_DRV_DS1742 tristate "Maxim/Dallas DS1742/1743" help
drivers/rtc/Makefile +1 -0
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds13 obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o +obj-$(CONFIG_RTC_DRV_DS1685) += rtc-ds1685.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
drivers/rtc/rtc-ds1685.c +875 -0
875 lines not on this page · raw message
include/linux/rtc/ds1685.h +401 -0
401 lines not on this page · raw message
Quoted 18 lines · Kumba
Add Dallas/Maxim DS1685/1687 RTC Support.
Signed-off-by Joshua Kinard <[email protected]>--- drivers/rtc/Kconfig | 13 drivers/rtc/Makefile | 1 drivers/rtc/rtc-ds1685.c | 875 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtc/ds1685.h | 401 ++++++++++++++++++++ 4 files changed, 1290 insertions(+)diff -Naurp linux-2.6.37.orig/drivers/rtc/Kconfig
linux-2.6.37.rtc-ds1685/drivers/rtc/Kconfig
--- linux-2.6.37.orig/drivers/rtc/Kconfig 2011-02-15
02:58:36.512076002 -0500
+++ linux-2.6.37.rtc-ds1685/drivers/rtc/Kconfig 2011-02-15
04:20:59.932076001 -0500
drivers/rtc/Kconfig config RTC_DRV_DS1553 · line 504
Quoted 6 lines
drivers/rtc/Kconfig +3
-0@@ -499,6 +499,19 @@ config RTC_DRV_DS1553 This driver can also be built as a module. If so, the module will be called rtc-ds1553. +config RTC_DRV_DS1685 + tristate "Dallas/Maxim DS1685/DS1687" + depends on I2C
The driver does not use any i2c functions.
Quoted 91 lines
drivers/rtc/Kconfig +10
-0+ help + If you say yes here you get support for the Dallas/Maxim + DS1685/DS1687 timekeeping chip. + + Systems that use this chip include EPPC-405-UC modules, by + electronic system design GmbH. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1685. + config RTC_DRV_DS1742 tristate "Maxim/Dallas DS1742/1743" helpdiff -Naurp linux-2.6.37.orig/drivers/rtc/Makefile linux-2.6.37.rtc-ds1685/drivers/rtc/Makefile
--- linux-2.6.37.orig/drivers/rtc/Makefile 2011-02-15
02:58:36.512076002 -0500
+++ linux-2.6.37.rtc-ds1685/drivers/rtc/Makefile 2011-02-15
04:17:15.372075999 -0500drivers/rtc/Makefile +1
-0@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds13 obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o +obj-$(CONFIG_RTC_DRV_DS1685) += rtc-ds1685.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.odiff -Naurp
linux-2.6.37.orig/drivers/rtc/rtc-ds1685.clinux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c
---linux-2.6.37.orig/drivers/rtc/rtc-ds1685.c1969-12-31
19:00:00.000000000 -0500
+++linux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c2011-02-15
04:22:50.032076002 -0500drivers/rtc/rtc-ds1685.c +57
-0@@ -0,0 +1,875 @@ +/* +* An rtc driver for the Dallas DS1685/DS1687. +* +* Copyright (C) 2009 Matthias Fuchs <[email protected]>. +* Copyright (C) 2010 Joshua Kinard <[email protected]>. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#include <linux/bcd.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/rtc/ds1685.h> +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> +#endif + +#define DRV_VERSION "0.3" + +static int +ds1685_rtc_read_time (struct device *dev,struct rtc_time *tm) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned long flags, start = jiffies; +unsigned int data; +unsigned int ctrlb, century; +unsigned int seconds, minutes, hours, wday, mday, month, years; + +/* Fetch the time info from the RTC registers. */ + ds1685_rtc_begin_data_access; + seconds =readb (®s->time.sec); + minutes =readb (®s->time.min); + hours =readb (®s->time.hour); + wday =readb (®s->time.wday); + mday =readb (®s->time.mday); + month =readb (®s->time.month); + years =readb (®s->time.year); + century =readb (®s->bank1.century); + ctrlb =readb (®s->time.ctrlb); + ds1685_rtc_end_data_access; + +/* Convert to Binary, perform fixups, and store to rtc_time. */ + tm->tm_sec =bcd2bin (seconds); + tm->tm_min =bcd2bin (minutes); + tm->tm_hour =bcd2bin (hours); + tm->tm_wday = (bcd2bin (wday) -1 ); + tm->tm_mday =bcd2bin (mday);
drivers/rtc/rtc-ds1685.c line 67
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+ tm->tm_mon = (bcd2bin (month) -1 ); + tm->tm_year = ((bcd2bin (years) + (bcd2bin (century) *100 )) -1900 ); + tm->tm_yday =rtc_year_days (tm->tm_mday, tm->tm_mon, tm->tm_year); + tm->tm_isdst = ((ctrlb &RTC_CTRL_B_DSE ) ?1 :0 ); + +/* Make sure valid time was received. */ +if (rtc_valid_tm (tm) <0 ) { +dev_err (dev,"retrieved date/time is not valid.\n" ); +rtc_time_to_tm (0 , tm); + }
Just pass the error up to rtc core.
Quoted 29 lines
drivers/rtc/rtc-ds1685.c +29
-0+return 0 ; +} + +static int +ds1685_rtc_set_time (struct device *dev,struct rtc_time *tm) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags, start = jiffies; +unsigned int seconds, minutes, hours, wday, mday, month, years; +unsigned int century; + +/* Fetch the time info from rtc_time. */ + seconds =bin2bcd (tm->tm_sec); + minutes =bin2bcd (tm->tm_min); + hours =bin2bcd (tm->tm_hour); + wday =bin2bcd (tm->tm_wday +1 ); + mday =bin2bcd (tm->tm_mday); + month =bin2bcd (tm->tm_mon +1 ); + years =bin2bcd (tm->tm_year %100 ); + century =bin2bcd ((tm->tm_year +1900 ) /100 ); + +/* +* Perform Sanity Checks: +* - Months: !> 12, Month Day != 0. +* - Month Day !> Max days in current month. +* - Hours !>= 24, Mins !>= 60, Secs !>= 60, & Weekday !> 7.
drivers/rtc/rtc-ds1685.c line 106
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+*/ +if ((month >12 ) || (mday ==0 )) +return -EDOM ; + +if (tm->tm_mday >rtc_month_days (tm->tm_mon, tm->tm_year)) +return -EDOM ; + +if ((tm->tm_hour >=24 ) || (tm->tm_min >=60 ) || (tm->tm_sec >=60 ) || + (wday >7 )) +return -EDOM ;
There is no need for theese checks the core takes care that the values are valid.
Quoted 35 lines
drivers/rtc/rtc-ds1685.c +35
-0+ +/* +* Force datamode to BCD (DM=0) and store the time values in the +* RTC registers. +*/ + ds1685_rtc_begin_data_access; + data =readb (®s->time.ctrlb) & ~(RTC_CTRL_B_DM ); +writeb (data, ®s->time.ctrlb); +writeb (seconds, ®s->time.sec); +writeb (minutes, ®s->time.min); +writeb (hours, ®s->time.hour); +writeb (wday, ®s->time.wday); +writeb (mday, ®s->time.mday); +writeb (month, ®s->time.month); +writeb (years, ®s->time.year); +writeb (century, ®s->bank1.century); + ds1685_rtc_end_data_access; + +return 0 ; +} + +static int +ds1685_rtc_read_alarm (struct device *dev,struct rtc_wkalrm *alrm) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags, start = jiffies; +unsigned int seconds, minutes, hours, mday; + +/* Fetch the alarm info from the RTC alarm registers. */ + ds1685_rtc_begin_data_access; + seconds =readb (®s->time.sec_alrm); + minutes =readb (®s->time.min_alrm);
drivers/rtc/rtc-ds1685.c line 151
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+ hours =readb (®s->time.hour_alrm); + mday =readb (®s->bank1.mday_alrm); + ds1685_rtc_end_data_access; + +/* Convert to Binary format and store in rtc_wkalrm. */ + alrm->time.tm_sec =bcd2bin (seconds); + alrm->time.tm_min =bcd2bin (minutes); + alrm->time.tm_hour =bcd2bin (hours); + alrm->time.tm_mday =bcd2bin (mday); +
return rtc_valid_tm(&arlm->time);
Quoted 39 lines
drivers/rtc/rtc-ds1685.c +33
-0+return 0 ; +} + +static int +ds1685_rtc_set_alarm (struct device *dev,struct rtc_wkalrm *alrm) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags, start = jiffies; +unsigned int seconds, minutes, hours, mday; + +/* Fetch the alarm info and convert to BCD. */ + seconds =bin2bcd (alrm->time.tm_sec); + minutes =bin2bcd (alrm->time.tm_min); + hours =bin2bcd (alrm->time.tm_hour); + mday =bin2bcd (alrm->time.tm_mday); + +/* Write to the four RTC alarm registers. */ + ds1685_rtc_begin_data_access; +writeb (seconds, ®s->time.sec_alrm); +writeb (minutes, ®s->time.min_alrm); +writeb (hours, ®s->time.hour_alrm); +writeb (mday, ®s->bank1.mday_alrm); + ds1685_rtc_end_data_access; + +return 0 ; +} + +#ifdef CONFIG_RTC_INTF_DEV +/* +* This function enables/disables an interrupt, depending on what is passed
+ * inirq_bit. PIE/AIE/UIE are read/written in Ctrl B, and RIE/WIE/KSE in
+ * Ctrl 4B.
+ *
+ * XXX: Only handles PIE/AIE/UIE at present.
+ */
drivers/rtc/rtc-ds1685.c line 191
Quoted 2 lines
drivers/rtc/rtc-ds1685.c +2
-0+static inline void +ds1685_rtc_irq_ctrl(volatile unsigned char *reg, spinlock_t *lock,
void __iomem *reg,
Quoted 1 line
+ const unsigned int *enabled, const unsigned int
irq_bit)
Why has 'enabled' to be a pointer?
Quoted 478 lines
drivers/rtc/rtc-ds1685.c +59
-0+{ +unsigned long flags; + +if (*enabled) { +spin_lock_irqsave (lock, flags); +writeb ((readb (reg) | irq_bit), reg); +spin_unlock_irqrestore (lock, flags); + }else { +spin_lock_irqsave (lock, flags); +writeb ((readb (reg) & ~(irq_bit)), reg); +spin_unlock_irqrestore (lock, flags); + } +} + +/* Replaces ioctl() RTC_PIE on/off. */ +/* 2nd arg should be 'unsigned int', but needs fix in RTC core. */ +static int +ds1685_rtc_periodic_irq_enable (struct device *dev,int enabled) +{ +struct ds1685_priv *rtc =dev_get_drvdata (dev); + +ds1685_rtc_irq_ctrl (&rtc->regs->time.ctrlb, &rtc->lock, + &enabled,RTC_CTRL_B_PIE ); + + rtc->p_intr = enabled; + +return 0 ; +} + +/* Replaces ioctl() RTC_AIE on/off. */ +static int +ds1685_rtc_alarm_irq_enable (struct device *dev,unsigned int enabled) +{ +struct ds1685_priv *rtc =dev_get_drvdata (dev); + +ds1685_rtc_irq_ctrl (&rtc->regs->time.ctrlb, &rtc->lock, + &enabled,RTC_CTRL_B_AIE ); + + rtc->a_intr = enabled; + +return 0 ; +} + +/* Replaces ioctl() RTC_UIE on/off. */ +static int +ds1685_rtc_update_irq_enable (struct device *dev,unsigned int enabled) +{ +struct ds1685_priv *rtc =dev_get_drvdata (dev); + +ds1685_rtc_irq_ctrl (&rtc->regs->time.ctrlb, &rtc->lock, + &enabled,RTC_CTRL_B_UIE ); + + rtc->u_intr = enabled; + +return 0 ; +} + +/* +* Defunct; Will be fully replaced by IRQ API above once RTC Core is modified
+ * to handle RIE/WIE/KSE.
+ */drivers/rtc/rtc-ds1685.c +101
-0+static int +ds1685_ioctl (struct device *dev,unsigned int cmd,unsigned long arg) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags; + +switch (cmd) { +case RTC_WIE_ON : +/* Allow Wake-up Alarm Interrupts */ + ds1685_rtc_begin_ctrl_access; + data =readb (®s->bank1.ctrl4b) |RTC_CTRL_4B_WIE ; +writeb (data, ®s->bank1.ctrl4b); + ds1685_rtc_end_ctrl_access; +break ; + +case RTC_WIE_OFF : +/* Disable Wake-up Alarm Interrupts */ + ds1685_rtc_begin_ctrl_access; + data =readb (®s->bank1.ctrl4b) & ~(RTC_CTRL_4B_WIE ); +writeb (data, ®s->bank1.ctrl4b); + ds1685_rtc_end_ctrl_access; +break ; + +default : +return -ENOIOCTLCMD ; + } + +return 0 ; +} +#else +#define ds1685_ioctl NULL +#define ds1685_rtc_periodic_irq_enable NULL +#define ds1685_rtc_alarm_irq_enable NULL +#define ds1685_rtc_update_irq_enable NULL +#endif /* CONFIG_RTC_INTF_DEV */ + +static irqreturn_t +ds1685_rtc_irq_handler (int irq,void *dev_id) +{ +struct platform_device *pdev = dev_id; +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned int ctrlb, ctrlc; +#if 0 +/* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */ +unsigned int ctrl4a, ctrl4b; +#endif +unsigned long flags, events =RTC_IRQF ; +unsigned int num_irqs =0 ; + +/* Fetch data from the four registers holding IRQ info. */ + ds1685_rtc_begin_ctrl_access; + ctrlb =readb (®s->time.ctrlb); + ctrlc =readb (®s->time.ctrlc); +#if 0 +/* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */ + ctrl4a =readb (®s->bank1.ctrl4a); + ctrl4b =readb (®s->bank1.ctrl4b); +#endif + ds1685_rtc_end_ctrl_access; + +/* Check to see if the IRQF bit is set. */ +if (!(ctrlc &RTC_CTRL_C_IRQF )) +return IRQ_NONE ; + +/* Check for alarm interrupts. */ +if ((ctrlc &RTC_CTRL_C_AF ) && + (ctrlb &RTC_CTRL_B_AIE )) { + events |=RTC_AF ; + num_irqs++; + } + +/* Check for timer interrupts. */ +else if ((ctrlc &RTC_CTRL_C_UF ) && + (ctrlb &RTC_CTRL_B_UIE )) { + events |=RTC_UF ; + num_irqs++; + } + +/* Check for periodic interrupts. */ +else if ((ctrlc &RTC_CTRL_C_PF ) && + (ctrlb &RTC_CTRL_B_PIE )) { + events |=RTC_PF ; + num_irqs++; + } + +rtc_update_irq (pdata->rtc, num_irqs, events); +return IRQ_HANDLED ; +} + +#ifdef CONFIG_PROC_FS +#define NUM_REGS 8 +#define NUM_SPACES 4 + +/* +* This prints out the flags of the registers for ds1685_rtc_proc. +* It's basically a hex --> binary function, just with extra spacing between
+ * the binary digits. It only works on single-byte hex values (8 bits),
+ * which is all that we need.
+ */drivers/rtc/rtc-ds1685.c +311
-0+static char * +print_regs (unsigned int *hex,char *dest) +{ +unsigned int i, j; +char *tmp = dest; + +for (i =0 ; i <NUM_REGS ; i++) { + *tmp++ = ((*hex &0x80 ) !=0 ?'1' :'0' ); +for (j =0 ; j <NUM_SPACES ; j++) + *tmp++ =' ' ; + *hex <<=1 ; + } + *tmp++ ='\0' ; + +return dest; +} + +static int +ds1685_rtc_proc (struct device *dev,struct seq_file *seq) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags; +unsigned int ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b; +char bits[NUM_REGS ][(NUM_REGS *NUM_SPACES ) +NUM_REGS +1 ]; +u8 ssn[8 ]; + + ds1685_rtc_begin_ctrl_access; + ds1685_rtc_get_ssn; + ctrla =readb (®s->time.ctrla); + ctrlb =readb (®s->time.ctrlb); + ctrlc =readb (®s->time.ctrlc); + ctrld =readb (®s->time.ctrld); + ctrl4a =readb (®s->bank1.ctrl4a); + ctrl4b =readb (®s->bank1.ctrl4b); + ds1685_rtc_end_ctrl_access; + +seq_printf (seq, +"Oscillator\t: %s\n" +"12/24hr\t\t: %s\n" +"DST\t\t: %s\n" +"Data mode\t: %s\n" +"Battery\t\t: %s\n" +"Aux batt\t: %s\n" +"Periodic IRQ\t: %s\n" +"SQW Freq\t: %s\n" +"Serial #\t: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n" +"Register Status\t:\n" +" Ctrl A\t: " +"UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0\n\t\t: %s\n" +" Ctrl B\t: " +"SET PIE AIE UIE SQWE DM 24hr DSE\n\t\t: %s\n" +" Ctrl C\t: " +"IRQF PF AF UF --- --- --- ---\n\t\t: %s\n" +" Ctrl D\t: " +"VRT --- --- --- --- --- --- ---\n\t\t: %s\n" +" Ctrl 4A\t: " +"VRT2 INCR BME --- PAB RF WF KF\n\t\t: %s\n" +" Ctrl 4B\t: " +"ABE E32k CS RCE PRS RIE WIE KSE\n\t\t: %s\n" , + ((ctrla &RTC_CTRL_A_DV1 ) ?"enabled" :"disabled" ), + ((ctrlb &RTC_CTRL_B_2412 ) ?"24-hour" :"12-hour" ), + ((ctrlb &RTC_CTRL_B_DSE ) ?"enabled" :"disabled" ), + ((ctrlb &RTC_CTRL_B_DM ) ?"binary" :"BCD" ), + ((ctrld &RTC_CTRL_D_VRT ) ?"ok" :"exhausted or n/a" ), + ((ctrl4a &RTC_CTRL_4A_VRT2 ) ?"ok" :"exhausted or n/a" ), + (!(ctrl4b &RTC_CTRL_4B_E32K ) ? + pirq_rate[(ctrla &RTC_CTRL_A_RS_MASK )] :"*" ), + (!((ctrl4b &RTC_CTRL_4B_E32K )) ? + sqw_freq[(ctrla &RTC_CTRL_A_RS_MASK )] :"32.768kHz" ), + ssn[0 ], ssn[1 ], ssn[2 ], ssn[3 ], ssn[4 ], ssn[5 ], + ssn[6 ], ssn[7 ], +print_regs (&ctrla, bits[0 ]), +print_regs (&ctrlb, bits[1 ]), +print_regs (&ctrlc, bits[2 ]), +print_regs (&ctrld, bits[3 ]), +print_regs (&ctrl4a, bits[4 ]), +print_regs (&ctrl4b, bits[5 ])); + +return 0 ; +} +#else +#define ds1685_rtc_proc NULL +#endif /* CONFIG_PROC_FS */ + +static const struct rtc_class_ops ds1685_rtc_ops = { + .ioctl = ds1685_ioctl, + .proc = ds1685_rtc_proc, + .read_time = ds1685_rtc_read_time, + .set_time = ds1685_rtc_set_time, + .read_alarm = ds1685_rtc_read_alarm, + .set_alarm = ds1685_rtc_set_alarm, + .irq_set_state = ds1685_rtc_periodic_irq_enable, + .alarm_irq_enable = ds1685_rtc_alarm_irq_enable, + .update_irq_enable = ds1685_rtc_update_irq_enable, +}; + +#ifdef CONFIG_SYSFS +static ssize_t +ds1685_nvram_read (struct kobject *kobj, +struct bin_attribute *bin_attr, +char *buf,loff_t pos,size_t size) +{ +struct platform_device *pdev = + to_platform_device(container_of(kobj, struct device, kobj)); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +ssize_t count; +unsigned int data; +unsigned long flags; + +spin_lock_irqsave (&pdata->lock, flags); + ds1685_rtc_switch_to_bank0; +for (count =0 ; size >0 && pos <NVRAM_TOTAL_SZ_BANK0 ; + count++, size--) +if (count <NVRAM_SZ_TIME ) + *buf++ =readb (®s->time.nvram1 + pos++); +else + *buf++ =readb (®s->bank0.nvram2 + pos++); + +if (size >0 ) { + ds1685_rtc_switch_to_bank1; + +for (count =0 ; size >0 && pos <NVRAM_TOTAL_SZ ; + count++, size--) { +writeb ((pos -NVRAM_TOTAL_SZ_BANK0 ), + ®s->bank1.ext_nvram_addr); + *buf++ =readb (®s->bank1.ext_nvram_dport); + pos++; + } + + ds1685_rtc_switch_to_bank0; + } +spin_unlock_irqrestore (&pdata->lock, flags); +return count; +} + +static ssize_t +ds1685_nvram_write (struct kobject *kobj, +struct bin_attribute *bin_attr, +char *buf,loff_t pos,size_t size) +{ +struct platform_device *pdev = + to_platform_device(container_of(kobj, struct device, kobj)); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +ssize_t count; +unsigned int data; +unsigned long flags; + +spin_lock_irqsave (&pdata->lock, flags); + ds1685_rtc_switch_to_bank0; +for (count =0 ; size >0 && pos <NVRAM_TOTAL_SZ_BANK0 ; + count++, size--) +if (count <NVRAM_SZ_TIME ) +writeb (*buf++, ®s->time.nvram1 + pos++); +else +writeb (*buf++, ®s->bank0.nvram2 + pos++); + +if (size >0 ) { + ds1685_rtc_switch_to_bank1; + +for (count =0 ; size >0 && pos <NVRAM_TOTAL_SZ ; + count++, size--) { +writeb ((pos -NVRAM_TOTAL_SZ_BANK0 ), + ®s->bank1.ext_nvram_addr); +writeb (*buf++, ®s->bank1.ext_nvram_dport); + pos++; + } + + ds1685_rtc_switch_to_bank0; + } +spin_unlock_irqrestore (&pdata->lock, flags); + +return count; +} + +static struct bin_attribute ds1685_nvram_attr = { + .attr = { + .name ="nvram" , + .mode =S_IRUGO |S_IWUSR , + }, + .read = ds1685_nvram_read, + .write = ds1685_nvram_write, + .size =NVRAM_TOTAL_SZ +}; + +static ssize_t +ds1685_sysfs_show_battery (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags; + + ds1685_rtc_begin_ctrl_access; + data =readb (®s->time.ctrld); + ds1685_rtc_end_ctrl_access; + +return sprintf (buf,"%s\n" , + (data &RTC_CTRL_D_VRT ) ?"ok" :"exhausted or n/a" ); +} + +static DEVICE_ATTR(battery, S_IRUGO, ds1685_sysfs_show_battery, NULL); + +static ssize_t +ds1685_sysfs_show_auxbatt (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int data; +unsigned long flags; + + ds1685_rtc_begin_ctrl_access; + data =readb (®s->bank1.ctrl4a); + ds1685_rtc_end_ctrl_access; + +return sprintf (buf,"%s\n" , + (data &RTC_CTRL_4A_VRT2 ) ?"ok" :"exhausted or n/a" ); +} + +static DEVICE_ATTR(auxbatt, S_IRUGO, ds1685_sysfs_show_auxbatt, NULL); + +static ssize_t +ds1685_sysfs_show_serial (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct platform_device *pdev =to_platform_device (dev); +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +u8 ssn[8 ]; +unsigned int data; +unsigned long flags; + + ds1685_rtc_begin_ctrl_access; + ds1685_rtc_get_ssn; + ds1685_rtc_end_ctrl_access; + +return sprintf (buf,"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n" , + ssn[0 ], ssn[1 ], ssn[2 ], ssn[3 ], + ssn[4 ], ssn[5 ], ssn[6 ], ssn[7 ]); + +return 0 ; +} + +static DEVICE_ATTR(serial, S_IRUGO, ds1685_sysfs_show_serial, NULL); + +static int +ds1685_sysfs_register (struct device *dev) +{ +int err; + + err =sysfs_create_bin_file (&dev->kobj, &ds1685_nvram_attr); +if (err) +return err; + + err =device_create_file (dev, &dev_attr_battery); +if (err) { +device_remove_file (dev, &dev_attr_battery); + goto out; + } + + err =device_create_file (dev, &dev_attr_auxbatt); +if (err) { +device_remove_file (dev, &dev_attr_auxbatt); + goto out; + } + + err =device_create_file (dev, &dev_attr_serial); +if (err) { +device_remove_file (dev, &dev_attr_serial); + goto out; + } + +return 0 ; + +out: +sysfs_remove_bin_file (&dev->kobj, &ds1685_nvram_attr); +return err; +} + +static int +ds1685_sysfs_unregister (struct device *dev) +{ +sysfs_remove_bin_file (&dev->kobj, &ds1685_nvram_attr); +device_remove_file (dev, &dev_attr_battery); +device_remove_file (dev, &dev_attr_auxbatt); +device_remove_file (dev, &dev_attr_serial); + +return 0 ; +} +#endif /* CONFIG_SYSFS */ + +static int __devinit +ds1685_rtc_probe (struct platform_device *pdev) +{ +struct rtc_device *rtc =NULL ; +struct device *dev =NULL ; +struct resource *res =NULL ; +struct ds1685_priv *pdata =NULL ; +struct ds1685_rtc_regs __iomem *regs =NULL ; +int ret =0 ; +unsigned int data, ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b; +unsigned long flags; +
drivers/rtc/rtc-ds1685.c line 678
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+/* Get the platform resources. */ + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res) +return -ENXIO ; + +/* Kzalloc() some memory for the rtc device structure. */ + pdata =kzalloc (sizeof (struct ds1685_priv ),GFP_KERNEL ); +if (!pdata) +return -ENOMEM ; + pdata->size = res->end - res->start +1 ;
resource_size(res) instead of res->end - res->start + 1
and it would be easier to just save the pointer to res instead of saving both size and start;
Quoted 93 lines
drivers/rtc/rtc-ds1685.c +93
-0+ +/* Request a memory region. */ +if (!request_mem_region (res->start, pdata->size, pdev->name)) { + ret = -EBUSY ; + goto out; + } + +/* Set the base address for the rtc, and ioremap() its registers. */ + pdata->baseaddr = res->start; + pdata->regs =ioremap (pdata->baseaddr, pdata->size); +if (!pdata->regs) { + ret = -ENOMEM ; + goto out; + } + +/* Fetch the assigned IRQ, and init the spinlock. */ + pdata->irq =platform_get_irq (pdev,0 ); +spin_lock_init (&pdata->lock); + +/* Begin the RTC setup. */ + regs = pdata->regs; + dev = &pdev->dev; + ds1685_rtc_begin_ctrl_access; + +/* +* Turn the RTC on, if it was not already on and/or Enable the +* countdown chain. +*/ + ctrla =readb (®s->time.ctrla); +if (!(ctrla &RTC_CTRL_A_DV1 )) { +dev_warn (&pdev->dev, +"oscillator stop detected - enabled!\n" ); + ctrla |=RTC_CTRL_A_DV1 ; + } + ctrla &= ~RTC_CTRL_A_DV2 ; +writeb (ctrla, ®s->time.ctrla); + +/* Prefer BCD mode (DM = 0). */ + ctrlb =readb (®s->time.ctrlb); +if (ctrlb &RTC_CTRL_B_DM ) { +dev_info (&pdev->dev,"Setting data mode to BCD\n" ); + ctrlb &= ~RTC_CTRL_B_DM ; +writeb (ctrlb, ®s->time.ctrlb); + } + +/* Check the batteries. There can be a main and/or an aux battery. */ + ctrld =readb (®s->time.ctrld); +if (!(ctrld &RTC_CTRL_D_VRT )) +dev_warn (&pdev->dev, +"Main battery is exhausted or not available.\n" ); + ctrl4a =readb (®s->bank1.ctrl4a); +if (!(ctrl4a &RTC_CTRL_4A_VRT2 )) +dev_warn (&pdev->dev, +"Aux battery is exhausted or not available.\n" ); + +/* Setup the interrupt handler. */ +if (pdata->irq >0 ) { +/* Read Ctrl B and clear PIE/AIE/UIE. */ + ctrlb =readb (®s->time.ctrlb); + ctrlb &= ~(RTC_CTRL_B_PIE &RTC_CTRL_B_AIE &RTC_CTRL_B_UIE ); +writeb (ctrlb, ®s->time.ctrlb); + +/* Reading Ctrl C auto-clears PF/AF/UF. */ + ctrlc =readb (®s->time.ctrlc); + +/* Read Ctrl 4B and clear RIE/WIE/KSE. */ + ctrl4b =readb (®s->bank1.ctrl4b); + ctrl4b &= ~(RTC_CTRL_4B_RIE &RTC_CTRL_4B_WIE &RTC_CTRL_4B_KSE ); +writeb (ctrl4b, ®s->bank1.ctrl4b); + +/* Manually clear RF/WF/KF in Ctrl 4A. */ + ctrl4a =readb (®s->bank1.ctrl4a); + ctrl4a &= ~(RTC_CTRL_4A_RF &RTC_CTRL_4A_WF &RTC_CTRL_4A_KF ); +writeb (ctrl4a, ®s->bank1.ctrl4a); + +/* Request an IRQ. */ + ret =request_irq (pdata->irq, ds1685_rtc_irq_handler, +IRQF_SHARED , pdev->name, pdev); + +/* Check to see if something came back. */ +if (unlikely (ret)) { +dev_warn (&pdev->dev,"RTC interrupt not available\n" ); + pdata->irq =0 ; + } + } + +/* Setup complete. */ + ds1685_rtc_end_ctrl_access; + +/* Register the device as an RTC. */ + rtc =rtc_device_register (pdev->name, &pdev->dev, + &ds1685_rtc_ops,THIS_MODULE ); +
drivers/rtc/rtc-ds1685.c line 781
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+/* Success? */ +if (IS_ERR (rtc)) { + ret =PTR_ERR (rtc); + goto out; + } + pdata->rtc = rtc; + +/* Set driver data, register w/ sysfs. */ +platform_set_drvdata (pdev, pdata); + ret =ds1685_sysfs_register (&pdev->dev);
If CONFIG_SYSFS is not defined you'll get an compile error.
Quoted 43 lines
drivers/rtc/rtc-ds1685.c +43
-0+if (ret) { + goto out; + } + +/* Done! */ +return 0 ; + + + out: +/* If error, clean up. */ +if (pdata->rtc) +rtc_device_unregister (pdata->rtc); +if (pdata->irq >0 ) +free_irq (pdata->irq, pdev); +if (pdata->regs) +iounmap (pdata->regs); +if (pdata->baseaddr) +release_mem_region (pdata->baseaddr, pdata->size); +kfree (pdata); + +return ret; +} + +static int __devexit +ds1685_rtc_remove (struct platform_device *pdev) +{ +struct ds1685_priv *pdata =platform_get_drvdata (pdev); +struct ds1685_rtc_regs __iomem *regs = pdata->regs; +unsigned int ctrlb, ctrlc, ctrl4a, ctrl4b; + +ds1685_sysfs_unregister (&pdev->dev); +rtc_device_unregister (pdata->rtc); +if (pdata->irq >0 ) { +/* Read Ctrl B and clear PIE/AIE/UIE. */ + ctrlb =readb (®s->time.ctrlb); + ctrlb &= ~(RTC_CTRL_B_PIE &RTC_CTRL_B_AIE &RTC_CTRL_B_UIE ); +writeb (ctrlb, ®s->time.ctrlb); + +/* Reading Ctrl C auto-clears PF/AF/UF. */ + ctrlc =readb (®s->time.ctrlc); + +/* Read Ctrl 4B and clear RIE/WIE/KSE. */ + ctrl4b =readb (®s->bank1.ctrl4b);
drivers/rtc/rtc-ds1685.c line 834
Quoted 10 lines
drivers/rtc/rtc-ds1685.c +10
-0+ ctrl4b &= ~(RTC_CTRL_4B_RIE &RTC_CTRL_4B_WIE &RTC_CTRL_4B_KSE ); +writeb (ctrl4b, ®s->bank1.ctrl4b); + +/* Manually clear RF/WF/KF in Ctrl 4A. */ + ctrl4a =readb (®s->bank1.ctrl4a); + ctrl4a &= ~(RTC_CTRL_4A_RF &RTC_CTRL_4A_WF &RTC_CTRL_4A_KF ); +writeb (ctrl4a, ®s->bank1.ctrl4a); + +/* Free the IRQ. */ +free_irq (pdata->irq, pdev);
Since the irq handler references the rtc device it should be freed before the rtc device.
Review
Quoted 48 lines
drivers/rtc/rtc-ds1685.c +41
-0+ } + +iounmap (pdata->regs); + +release_mem_region (pdata->baseaddr, pdata->size); +kfree (pdata); + +return 0 ; +} + +static struct platform_driver ds1685_rtc_driver = { + .driver = { + .name ="rtc-ds1685" , + .owner =THIS_MODULE , + }, + .probe = ds1685_rtc_probe, + .remove =__devexit_p (ds1685_rtc_remove), +}; + +static __init +int ds1685_init (void ) +{ +return platform_driver_register (&ds1685_rtc_driver); +} + +static __exit +void ds1685_exit(void) +{ +platform_driver_unregister (&ds1685_rtc_driver); +} + + +module_init (ds1685_init); +module_exit (ds1685_exit); + +MODULE_AUTHOR ("Matthias Fuchs <[email protected]>, " +"Joshua Kinard <[email protected]>" ); +MODULE_DESCRIPTION ("Dallas/Maxim DS1685/DS1687 RTC driver" ); +MODULE_LICENSE ("GPL" ); +MODULE_VERSION (DRV_VERSION ); +MODULE_ALIAS ("platform:rtc-ds1685" );diff -Naurp
linux-2.6.37.orig/include/linux/rtc/ds1685.hlinux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h
---linux-2.6.37.orig/include/linux/rtc/ds1685.h1969-12-31
19:00:00.000000000 -0500
+++linux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h2011-02-15
04:28:04.582076001 -0500(quoted diff) +0
-0@@ -0,0 +1,401 @@
There doesn't seem to be any code inside this file which is used outside of ds1685.c so it might be a good idea to merge the two files, or at least move this file to drivers/rtc/
Quoted 66 lines
include/linux/rtc/ds1685.h +19
-0+/* +* include/linux/rtc/ds1685.h +* +* Definitions for the control registers and platform data of the +* DS1685/DS1687 RTC chip driver. +* +* Copyright (C) 2009 Matthias Fuchs <[email protected]> +* Copyright (C) 2010 Joshua Kinard <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef _LINUX_RTC_DS1685_H_ +#define _LINUX_RTC_DS1685_H_ + +/* +* Found in the original RTC driver for SGI IP30 (Octane) systems, it is used
+ * in theds1685_begin_accessmacro while loop to avoid RTC access lockouts.
+ */include/linux/rtc/ds1685.h +43
-0+#define DS1685_MAGIC 137 + + +/* +* NVRAM. +* - 50 bytes of NVRAM are available just past the clock registers. +* - 64 additional bytes are available in Bank0. +* - 128 additional bytes are available in Bank1. +*/ +#define NVRAM_SZ_TIME 50 +#define NVRAM_SZ_BANK0 64 +#define NVRAM_SZ_BANK1 128 +#define NVRAM_TOTAL_SZ_BANK0 (NVRAM_SZ_TIME + NVRAM_SZ_BANK0) +#define NVRAM_TOTAL_SZ (NVRAM_TOTAL_SZ_BANK0 + NVRAM_SZ_BANK1) + + +/* +* Some of the register names below are not used in the actual code, but +* are made available per the DS1685/DS1687 documentation for possible +* future use if the need arises. +*/ +#define RTC_SECONDS 0x00 +#define RTC_SECONDS_ALARM 0x01 +#define RTC_MINUTES 0x02 +#define RTC_MINUTES_ALARM 0x03 +#define RTC_HOURS 0x04 +#define RTC_HOURS_ALARM 0x05 +#define RTC_DAY 0x06 +#define RTC_DATE 0x07 +#define RTC_MONTH 0x08 +#define RTC_YEAR 0x09 + +#define RTC_CTRL_A 0x0a/* Control Register A */ +#define RTC_CTRL_B 0x0b/* Control Register B */ +#define RTC_CTRL_C 0x0c/* Control Register C */ +#define RTC_CTRL_D 0x0d/* Control Register D */ +#define RTC_EXT_CTRL_4A 0x4a/* Extended Control Register 4A */ +#define RTC_EXT_CTRL_4B 0x4b/* Extended Control Register 4B */ +#define RTC_NVRAM_START_B0 0x0e +#define RTC_NVRAM_BANK1_BASE 0x3f00 + + +/*
include/linux/rtc/ds1685.h line 74
Quoted 10 lines
include/linux/rtc/ds1685.h +10
-0+* Values of the RTC bits. +*/ +#define BIT_0 0x01 +#define BIT_1 0x02 +#define BIT_2 0x04 +#define BIT_3 0x08 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80
Just use BIT(x) instead of adding these defines
Quoted 114 lines
include/linux/rtc/ds1685.h +44
-0+ +/* +* Bit names in Control Register A. +*/ +#define RTC_CTRL_A_RS0 BIT_0/* Rate-Selection Bit 0 */ +#define RTC_CTRL_A_RS1 BIT_1/* Rate-Selection Bit 1 */ +#define RTC_CTRL_A_RS2 BIT_2/* Rate-Selection Bit 2 */ +#define RTC_CTRL_A_RS3 BIT_3/* Rate-Selection Bit 3 */ +#define RTC_CTRL_A_DV0 BIT_4/* Bank Select */ +#define RTC_CTRL_A_DV1 BIT_5/* Oscillator Enable */ +#define RTC_CTRL_A_DV2 BIT_6/* Countdown Chain */ +#define RTC_CTRL_A_UIP BIT_7/* Update In Progress */ +#define RTC_CTRL_A_RS_MASK (RTC_CTRL_A_RS0 + RTC_CTRL_A_RS1 + \ + RTC_CTRL_A_RS2 + RTC_CTRL_A_RS3) + +/* +* Bit names in Control Register B. +*/ +#define RTC_CTRL_B_DSE BIT_0/* Daylight Savings Enable */ +#define RTC_CTRL_B_2412 BIT_1/* 12-Hr/24-Hr Mode */ +#define RTC_CTRL_B_DM BIT_2/* Data Mode */ +#define RTC_CTRL_B_SQWE BIT_3/* Square-Wave Enable */ +#define RTC_CTRL_B_UIE BIT_4/* Update-Ended Interrupt-Enable */ +#define RTC_CTRL_B_AIE BIT_5/* Alarm-Interrupt Enable */ +#define RTC_CTRL_B_PIE BIT_6/* Periodic-Interrupt Enable */ +#define RTC_CTRL_B_SET BIT_7/* SET Bit */ + + +/* +* Bit names in Control Register C. +* +* BIT_0, BIT_1, BIT_2, & BIT_3 are unused, always return 0, and cannot be +* written to. +*/ +#define RTC_CTRL_C_UF BIT_4/* Update-Ended Interrupt Flag */ +#define RTC_CTRL_C_AF BIT_5/* Alarm-Interrupt Flag */ +#define RTC_CTRL_C_PF BIT_6/* Periodic-Interrupt Flag */ +#define RTC_CTRL_C_IRQF BIT_7/* Interrupt-Request Flag */ + + +/* +* Bit names in Control Register D. +* +* BIT_0 through BIT_6 are unused, always return 0, and cannot be written to.
+ */include/linux/rtc/ds1685.h +22
-0+#define RTC_CTRL_D_VRT BIT_7/* Valid RAM and Time */ + + +/* +* Bit names in Extended Control Register 4A. +* +* BIT_4 and BIT_5 are reserved for future use. They can be read from and +* written to, but have no effect on the RTC's operation. +*/ +#define RTC_CTRL_4A_KF BIT_0/* Kickstart Flag */ +#define RTC_CTRL_4A_WF BIT_1/* Wake-Up Alarm Flag */ +#define RTC_CTRL_4A_RF BIT_2/* RAM Clear Flag */ +#define RTC_CTRL_4A_PAB BIT_3/* Power-Active Bar Control */ +#define RTC_CTRL_4A_INCR BIT_6/* Increment-in-Progress Status */ +#define RTC_CTRL_4A_VRT2 BIT_7/* Auxillary Battery Status */ + + +/* +* Bit names in Extended Control Register 4B. +*/ +#define RTC_CTRL_4B_KSE BIT_0/* Kickstart Interrupt-Enable */ +#define RTC_CTRL_4B_WIE BIT_1/* Wake-Up Alarm-Interrupt Enable */
include/linux/rtc/ds1685.h +5
-0+#define RTC_CTRL_4B_RIE BIT_2/* RAM Clear-Interrupt Enable */ +#define RTC_CTRL_4B_PRS BIT_3/* PAB Reset-Select */ +#define RTC_CTRL_4B_RCE BIT_4/* RAM Clear-Enable */ +#define RTC_CTRL_4B_CS BIT_5/* Crystal Select */ +#define RTC_CTRL_4B_E32K BIT_6/* Enable 32.768Hz Output on SQW Pin */
include/linux/rtc/ds1685.h +26
-0+#define RTC_CTRL_4B_ABE BIT_7/* Auxillary Battery Enable */ + + +/* +* Register names in Bank 1. +* +* The DV0 bit in Control Register A must be set to 1 for these registers +* to become available, including Extended Control Registers 4A & 4B. +*/ +#define RTC_BANK1_MODEL 0x40/* Model Number */ +#define RTC_BANK1_SERIAL_BYTE_1 0x41/* 1st Byte of Serial Number */ +#define RTC_BANK1_SERIAL_BYTE_2 0x42/* 2nd Byte of Serial Number */ +#define RTC_BANK1_SERIAL_BYTE_3 0x43/* 3rd Byte of Serial Number */ +#define RTC_BANK1_SERIAL_BYTE_4 0x44/* 4th Byte of Serial Number */ +#define RTC_BANK1_SERIAL_BYTE_5 0x45/* 5th Byte of Serial Number */ +#define RTC_BANK1_SERIAL_BYTE_6 0x46/* 6th Byte of Serial Number */ +#define RTC_BANK1_SERIAL_CRC 0x47/* Serial CRC Byte */ +#define RTC_BANK1_CENTURY 0x48/* Century Counter */ +#define RTC_BANK1_DATE_ALARM 0x49/* Date Alarm */ +#define RTC_BANK1_RAM_ADDR 0x50/* RAM Address */ +#define RTC_BANK1_RAM_DATA_PORT 0x53/* RAM Data Port */ + + +#ifdef CONFIG_PROC_FS +/* +* Periodic Interrupt Rates. A static character array is used for displaying
+ * these values in /proc when procfs is enabled.
+ */include/linux/rtc/ds1685.h +10
-0+static const char *pirq_rate[16 ] = { +"none" ,"3.90625ms" ,"7.8125ms" ,"122.070µs" ,"244.141µs" , +"488.281µs" ,"976.5625µs" ,"1.953125ms" ,"3.90625ms" ,"7.8125ms" , +"15.625ms" ,"31.25ms" ,"62.5ms" ,"125ms" ,"250ms" ,"500ms" +}; + +/* +* Square-Wave Output Frequencies. A static character array is used for +* displaying these values in /proc when procfs is enabled. +*/
include/linux/rtc/ds1685.h line 194
Quoted 10 lines
include/linux/rtc/ds1685.h +10
-0+static const char *sqw_freq[16 ] = { +"none" ,"256Hz" ,"128Hz" ,"8.192kHz" ,"4.096kHz" ,"2.048kHz" , +"1.024kHz" ,"512Hz" ,"256Hz" ,"128Hz" ,"64Hz" ,"32Hz" ,"16Hz" , +"8Hz" ,"4Hz" ,"2Hz" +}; +#endif /* CONFIG_PROC_FS */ + + +#define DS1685_REG (r) volatile unsigned char r; +
I think you should really use readb(pdata->regs + REG) instead of the following structs. Maybe add a helper function in the form of:
static uint8_t ds1685_read(struct ds1685_priv *ds1685, unsigned int reg) {
return readb(pdata->regs + REG);
}That should also help with the different paddings introduced in patch 2.
Quoted 88 lines
include/linux/rtc/ds1685.h +65
-0+ +/* +* This structure defines the standard DS1286-style time registers +* that exist in both bank0 and bank1. +*/ +struct ds1685_time_regs { +DS1685_REG (sec);/* Seconds */ +DS1685_REG (sec_alrm);/* Seconds Alarm */ +DS1685_REG (min);/* Minutes */ +DS1685_REG (min_alrm);/* Minutes Alarm */ +DS1685_REG (hour);/* Hours */ +DS1685_REG (hour_alrm);/* Hours Alarm */ +DS1685_REG (wday);/* Day of the Week */ +DS1685_REG (mday);/* Day of the Month */ +DS1685_REG (month);/* Current Month */ +DS1685_REG (year);/* Current Year */ +DS1685_REG (ctrla);/* Control Register A */ +DS1685_REG (ctrlb);/* Control Register B */ +DS1685_REG (ctrlc);/* Control Register C */ +DS1685_REG (ctrld);/* Control Register D */ +volatile unsigned char nvram1[NVRAM_SZ_TIME ]; +}; + + +/* +* Bank0-specific registers. This is usually NVRAM. +*/ +struct ds1685_bank0_regs { +volatile unsigned char nvram2[NVRAM_SZ_BANK0 ]; +}; + + +/* +* Bank1-specific registers. These access extended capabilities present +* in the DS1685. The DS17285/DS17287 has minor differences, including an +* RTC write counter, and two extended NVRAM address registers, for MSB +* or LSB forms of the address. +*/ +struct ds1685_bank1_regs { +DS1685_REG (model);/* Model Number */ +DS1685_REG (ssn1);/* 1st Byte of Serial Number */ +DS1685_REG (ssn2);/* 2nd Byte of Serial Number */ +DS1685_REG (ssn3);/* 3rd Byte of Serial Number */ +DS1685_REG (ssn4);/* 4th Byte of Serial Number */ +DS1685_REG (ssn5);/* 5th Byte of Serial Number */ +DS1685_REG (ssn6);/* 6th Byte of Serial Number */ +DS1685_REG (crc);/* Serial # CRC Byte */ +DS1685_REG (century);/* Current Century */ +DS1685_REG (mday_alrm);/* Day of the Month Alarm */ +DS1685_REG (ctrl4a);/* Ext. Control Register 4A */ +DS1685_REG (ctrl4b);/* Ext. Control Register 4B */ +DS1685_REG (rsvrd1);/* Reserved; provides SMI */ +DS1685_REG (rsvrd2);/* Recovery Stack. Holds last */ +DS1685_REG (rtc_addr2);/* four RTC addresses for the */ +DS1685_REG (rtc_addr3);/* BIOS to recover from an SMI. */ +DS1685_REG (ext_nvram_addr);/* Ext. NVRAM Addr; DS1685/7 */ +DS1685_REG (rsvrd3);/* Reserved */ +DS1685_REG (rsvrd4);/* Reserved */ +DS1685_REG (ext_nvram_dport);/* Ext. NVRAM Data Port */ +}; + + +/* +* The actual register struct. Uses a union to combine bank0 and bank1, +* since both use the same address space, but are accessed depending on the
+ * state of the DV0 bit in Control Register A.
+ */include/linux/rtc/ds1685.h +20
-0+struct ds1685_rtc_regs { +struct ds1685_time_regs time; +union { +struct ds1685_bank0_regs bank0; +struct ds1685_bank1_regs bank1; + }; +}; + + +/* +* DS1685/1687 data structure. +*/ +struct ds1685_priv { +struct rtc_device *rtc;/* RTC device pointer */ +struct ds1685_rtc_regs __iomem *regs;/* RTC Registers */ +resource_size_t baseaddr;/* Resource base address */ +size_t size;/* Resource size */ +spinlock_t lock;/* Spinlock struct */ +int irq;/* RTC IRQ # */ +unsigned int p_intr;/* Periodic IRQ status */
include/linux/rtc/ds1685.h line 291
Quoted 10 lines
include/linux/rtc/ds1685.h +10
-0+unsigned int a_intr;/* Alarm IRQ status */ +unsigned int u_intr;/* Update IRQ status */ +#if 0 /* Not used just yet; See comments in rtc-ds1685.c */ +unsigned int r_intr;/* RAM-Clear IRQ status */ +unsigned int w_intr;/* Watchdog IRQ status */ +unsigned int k_intr;/* Kickstart IRQ status */ +#endif +}; + +
All these macros that follow should really be functions.
Quoted 114 lines
include/linux/rtc/ds1685.h +17
-0+/* +* These two macros set and unset the SET bit in Control Register B. The +* SET bit inhibits update transfers and allows a safe read/write of the +* time and calendar bits. +*/ +#define ds1685_rtc_set_set_bit \ + data = readb(®s->time.ctrlb) | RTC_CTRL_B_SET; \ + writeb(data, ®s->time.ctrlb) + +#define ds1685_rtc_clear_set_bit \ + data = readb(®s->time.ctrlb) & ~(RTC_CTRL_B_SET); \ + writeb(data, ®s->time.ctrlb) + + +/* +* These two macros switch between bank0 and bank1. Bank0 provides access +* to the standard RTC capabilities originally defined with the DS1286 RTC.
+ * Bank1 provides access to extended capabilities, including extended
+ * control registers, silicon serial number, century counter, aux battery
+ * capabilities, wake-up/kick-start features and additional amounts of nvram.
+ */include/linux/rtc/ds1685.h +89
-0+#define ds1685_rtc_switch_to_bank0 \ + data = readb(®s->time.ctrla) & ~(RTC_CTRL_A_DV0); \ + writeb(data, ®s->time.ctrla) + +#define ds1685_rtc_switch_to_bank1 \ + data = readb(®s->time.ctrla) | RTC_CTRL_A_DV0; \ + writeb(data, ®s->time.ctrla) + + +/* +* This begins the RTC data access, such as reading/writing clock/alarm +* registers. It performs several steps in a common block of code that is +* used quite frequently: +* +* - Sets a spinlock on the IRQ. +* - Sets the SET bit in Control Register B. +* - Reads Control Register A. +* - Checks the UIP bit in Control Register A. If UIP is active, +* a delay is forced and a check is run to see if RTC access was +* locked out. The loop runs until UIP is not set. +* - A switch to bank1 occurs. This allows access to all the relevant +* time data, since the time registers are available regardless of +* which bank is currently selected. +*/ +#define ds1685_rtc_begin_data_access \ + spin_lock_irqsave(&pdata->lock, flags); \ + ds1685_rtc_set_set_bit; \ + data = readb(®s->time.ctrla); \ + while (data & RTC_CTRL_A_UIP) { \ + udelay(10); \ + if (jiffies > start + DS1685_MAGIC) { \ + dev_err(dev, "Access lockout!\n"); \ + return 1; \ + } \ + data = readb(®s->time.ctrla); \ + } \ + ds1685_rtc_switch_to_bank1 + +/* +* This ends the RTC data access: +* - It switches back to bank0. +* - It clears the SET bit in Control Register B. +* - It unsets the spinlock on the IRQ. +*/ +#define ds1685_rtc_end_data_access \ + ds1685_rtc_switch_to_bank0; \ + ds1685_rtc_clear_set_bit; \ + spin_unlock_irqrestore(&pdata->lock, flags) + + +/* +* This begins the RTC access to the control registers only. Such +* accesses need far less handling, just a spinlock and a switch to +* bank1. +*/ +#define ds1685_rtc_begin_ctrl_access \ + spin_lock_irqsave(&pdata->lock, flags); \ + ds1685_rtc_switch_to_bank1 + +/* +* This ends the RTC ctrl access: +* - It switches back to bank0. +* - It unsets the spinlock on the IRQ. +*/ +#define ds1685_rtc_end_ctrl_access \ + ds1685_rtc_switch_to_bank0; \ + spin_unlock_irqrestore(&pdata->lock, flags) + + +/* +* This fetches the Silicon Serial Number, a unique ID specific to every +* DS1685/1687. +* +* This number starts at 0x40, and is 8-bytes long, ending at 0x47. +* The first byte is the model #, the next six bytes are the serial +* number digits, and the final byte is a CRC check byte. Together, +* they form the SSN of the RTC. +*/ +#define ds1685_rtc_get_ssn \ + ssn[0] = readb(®s->bank1.model); \ + ssn[1] = readb(®s->bank1.ssn1); \ + ssn[2] = readb(®s->bank1.ssn2); \ + ssn[3] = readb(®s->bank1.ssn3); \ + ssn[4] = readb(®s->bank1.ssn4); \ + ssn[5] = readb(®s->bank1.ssn5); \ + ssn[6] = readb(®s->bank1.ssn6); \ + ssn[7] = readb(®s->bank1.crc) + +#endif /* _LINUX_RTC_DS1685_H_ */
Quoted 7 lines · Lars-Peter Clausen
I think you should really use
readb(pdata->regs + REG)instead of the following structs. Maybe add a helper function in the form of:static uint8_t ds1685_read(struct ds1685_priv *ds1685, unsigned int reg) { return readb(pdata->regs + REG); }That should also help with the different paddings introduced in patch 2.
Lots of good feedback, thanks! Ralf already suggested using offsets instead of a struct. I'm tinkering now with getting this to work, as I have to have this done before I can address many of your other points.
I have determined the following formula specific to the SGI O2 to read the RTC registers:
readb(pdata->regs + RTC_<REGISTER> * 0x100);
is equivalent to
readb(pdata->regs.time.<REGISTER>);
I'll assume writeb() changes are the same. The question is, how do I wire in the 0x100 padding value in such a way that I keep the IP32-specific bits out of generic code? Ralf mentioned using some field in platform_data, but I haven't quite learned the platform stuff (this is my first real attempt at a kernle driver).
Also, one thing I can quickly address, I put the ds1685.h file under include/linux/rtc because I saw that folder as already existing. I figured that's where rtc header files went. Right now, nothing outside of the driver uses it, but SGI O2 will need to eventually, as it uses the RTC to trigger a system poweroff by accessing a few of the extended control registers.
It currently uses similarly-duplicated #defines in a local header file, but I figured, if I can get this driver fully working, and other platforms could theoretically use the same trick, would not include/linux/rtc be the best place for the header? If there's a better place, please let me know!
Thanks,
Signature · 7 lines
Joshua Kinard Gentoo/MIPS [email protected] "The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between." --Emperor Turhan, Centauri Republic
Quoted 7 lines
I have determined the following formula specific to the SGI O2 to read the RTC registers:
readb(pdata->regs + RTC_<REGISTER> * 0x100);is equivalent to
Quoted 7 lines
readb(pdata->regs.time.<REGISTER>);I'll assume
writeb()changes are the same. The question is, how do I wire in the0x100padding value in such a way that I keep the IP32-specific bits out of generic code? Ralf mentioned using some field inplatform_data, but I haven't quite learned the platform stuff (this is my first real attempt at a kernle driver).
Have a look at i2c-ocores.c: Basically you use platform_data to specify
register spacing on the bus.
Manuel
Quoted 5 lines · Manuel Lauss
Have a look at
i2c-ocores.c: Basically you useplatform_datato specify
register spacing on the bus.Manuel
I think I get most of it here. i2c-ocores.c defines struct ocores_i2c, which has regstep in it. I assume the equivalent to this in the RTC driver is going to be ds1685_priv. But in i2c_ocores.h, struct ocores_i2c_platform_data is defined, which also carries a regstep. In i2c-ocores.c, this struct becomes *pdata while ocores_i2c becomes *i2c, and *i2c is used to access the registers.
I don't think I have an equivalent to either of these two with the way the driver was originally written and how I modified it. The ds1685_priv kinda does both right now. I assume platform_data is not really defined...I have to implement one specific to this RTC driver, giving it specific variables that need to be customizable at the platform level, and then set those in the machine-specific areas, i.e., somewhere in IP32's platform file.
Sound correct?
Signature · 7 lines
Joshua Kinard Gentoo/MIPS [email protected] "The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between." --Emperor Turhan, Centauri Republic
Quoted 14 lines · Kumba
Quoted 5 lines · Manuel Lauss
Have a look at
i2c-ocores.c: Basically you useplatform_datato specify register spacing on the bus.Manuel
I think I get most of it here. i2c-ocores.c defines
struct ocores_i2c, which has regstep in it. I assume the equivalent to this in the RTC driver is going to beds1685_priv. But ini2c_ocores.h,struct ocores_i2c_platform_datais defined, which also carries a regstep. Ini2c-ocores.c, this struct becomes *pdata whileocores_i2cbecomes *i2c, and *i2c is used to access the registers.
Quoted 8 lines
I don't think I have an equivalent to either of these two with the way the driver was originally written and how I modified it. The
ds1685_privkinda does both right now. I assumeplatform_datais not really defined...I have to implement one specific to this RTC driver, giving it specific variables that need to be customizable at the platform level, and then set those in the machine-specific areas, i.e., somewhere in IP32's platform file.Sound correct?
Yep.
Manuel
Quoted 1 line · Lars-Peter Clausen
Just pass the error up to rtc core.
How? I looked at a few other drivers, but they, too, call dev_err() or dev_dbg(). Others don't appear to send any kind of string-based error value anywhere, they just return a -E* value.
Quoted 1 line
There is no need for these checks the core takes care that the values are valid.
I've seen a few other RTC drivers implement these checks. It's really hard to tell what drivers are, I guess, "right" and which ones are "wrong" in their approach when you've got already-accepted drivers in the tree doing things that I'm trying to fix in this driver.
That said, how is the core running these checks when I quickly turn around and write the values back to the RTC?
Quoted 1 line
return rtc_valid_tm(&arlm->time);
Noted -- Probably form when I copied one of the time read functions or such. Alarm support wasn't in the original version of this driver when I found it.
Quoted 1 line
Why has 'enabled' to be a pointer?
No idea to be honest. I think I copied it from another driver. I'll re-review it when I get to that point in fixing things.
Quoted 3 lines
resource_size(res)instead ofres->end-res->start+ 1
and it would be easier to just save the pointer to res instead of saving both size and start;
Noted, I see a few drivers using this syntax, so I'll adapt to it.
Quoted 1 line
If
CONFIG_SYSFSis not defined you'll get an compile error.
Noted, thanks!
Quoted 2 lines
Since the irq handler references the rtc device it should be freed before the rtc device.
Noted, thanks!
Quoted 3 lines
There doesn't seem to be any code inside this file which is used outside of
ds1685.cso it might be a good idea to merge the two files, or at least move this file todrivers/rtc/
I wasn't quite sure where headers typically went. include/linux/rtc already existed, so I thought it was created at some point for holding .h files for RTC drivers. IP32 will need to reference this header down the road anyways. No harm if it has to look into drivers/rtc?
Quoted 1 line
Just use BIT(x) instead of adding these defines
Noted, will research.
Quoted 7 lines
I think you should really use
readb(pdata->regs + REG)instead of the following structs. Maybe add a helper function in the form of:static uint8_t ds1685_read(struct ds1685_priv *ds1685, unsigned int reg) { return readb(pdata->regs + REG); }That should also help with the different paddings introduced in patch 2.
Working on this now. Ran into some road blocks with gcc and inlining, but I worked around it.
Quoted 1 line
All these macros that follow should really be functions.
Even the large ds1685_begin_data_access macro? I can stick it into a inlined function, but I thought a macro was better. Or am I trying to outfox the compiler by doing so?
If I do inline it, I need a fix for passing errors back to the RTC core. I can't use dev_err() because it needs the device struct to work with, and I want to avoid passing too many arguments to an inlinable function.
Thoughts? The rest should be easy to convert into inlined functions.
Thanks!,
Signature · 7 lines
Joshua Kinard Gentoo/MIPS [email protected] "The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between." --Emperor Turhan, Centauri Republic
Quoted 7 lines · Kumba
Quoted 1 line · Lars-Peter Clausen
Just pass the error up to rtc core.
How? I looked at a few other drivers, but they, too, call
dev_err()ordev_dbg(). Others don't appear to send any kind of string-based error value anywhere, they just return a -E* value.
That is what I meant. Pass the return value of rtc_valid_tm on, instead of setting the time to 0 and pretend everything went fine.
You can still keep the dev_err though, no problem with that.
Quoted 5 lines
Quoted 4 lines
There is no need for these checks the core takes care that the values are valid.
Quoted 8 lines
I've seen a few other RTC drivers implement these checks. It's really hard to tell what drivers are, I guess, "right" and which ones are "wrong" in their approach when you've got already-accepted drivers in the tree doing things that I'm trying to fix in this driver.
That said, how is the core running these checks when I quickly turn around and write the values back to the RTC?
What do you mean by 'quickly turning around and writing the values back'? The rtc_time struct passed to the set_time callback is supposed to contain only valid values.
Quoted 8 lines
Quoted 7 lines
There doesn't seem to be any code inside this file which is used outside of
ds1685.cso it might be a good idea to merge the two files, or at least move
this file todrivers/rtc/
Quoted 4 lines
I wasn't quite sure where headers typically went.
include/linux/rtcalready existed, so I thought it was created at some point for holding .h files for RTC drivers. IP32 will need to reference this header down the road anyways. No harm if it has to look intodrivers/rtc?
Well, if it is going to be shared it should probably remain somewhere in include/, but everything thats not shared should be moved to rtc-ds1685.c like for example ds1685_priv.
Quoted 21 lines
Quoted 3 lines
Just use BIT(x) instead of adding these defines
Noted, will research.
Quoted 9 lines
I think you should really use
readb(pdata->regs + REG)instead of the following
structs. Maybe add a helper function in the form of:
staticuint8_tds1685_read(structds1685_priv*ds1685, unsigned intreg) { return readb(pdata->regs + REG); }That should also help with the different paddings introduced in patch 2.
Working on this now. Ran into some road blocks with gcc and inlining, but I worked around it.
Quoted 5 lines
Quoted 1 line
All these macros that follow should really be functions.
Even the large
ds1685_begin_data_accessmacro? I can stick it into a inlined function, but I thought a macro was better. Or am I trying to outfox the compiler by doing so?
I don't know what you are trying to do, but the current code is extremely unreadable.
You have all those variables declared in your functions which are on first sight not used, because they are only referenced from the macros. Furthermore the invocation of the macro has not the syntax of a function call, although semantically that is what it is.
And especially ds1685_rtc_begin_data_access is dangerous, because of the 'return 1', there is no indication when you read the code that a function could magically exit upon invoking that macro.
Quoted 5 lines
If I do inline it, I need a fix for passing errors back to the RTC core. I can't use
dev_err()because it needs the device struct to work with, and I want to avoid passing too many arguments to an inlinable function.
Why? The compiled code will probably be exactly the same as now.
Quoted 6 lines
Thoughts? The rest should be easy to convert into inlined functions.
Thanks!,
Quoted 3 lines · Lars-Peter Clausen
That is what I meant. Pass the return value of
rtc_valid_tmon, instead of setting the time to 0 and pretend everything went fine.
You can still keep thedev_errthough, no problem with that.
Well, that particular return is in the while loop to check for an access lockout. I'll be honest, I got that little bit out of a driver originally written for SGI Octane systems running Linux (an unofficial patch that has since suffered severe bitrot).
I know what bit to check to avoid an access lockout, but the DS1685 manual offers several options, and not being a hardware person, I'm not real sure how to represent them in code.
Here's what it says:
Quoted 14 lines
There are three methods that can handle access of the RTC that avoid any possibility of accessing inconsistent time and calendar data. The first method uses the update-ended interrupt. If enabled, an interrupt occurs after every update cycle that indicates that over 999ms is available to read valid time and date information. If this interrupt is used, the IRQF bit in Register C should be cleared before leaving the interrupt routine.
A second method uses the UIP bit in Register A to determine if the update cycle is in progress. The UIP bit pulses once per second. After the UIP bit goes high, the update transfer occurs 244μs later. If a low is read on the UIP bit, the user has at least 244μs before the time/calendar data is changed. Therefore, the user should avoid interrupt service routines that would cause the time needed to read valid time/calendar data to exceed 244μs.
Quoted 6 lines
The third method uses a periodic interrupt to determine if an update cycle is in progress. The UIP bit in Register A is set high between the setting of the PF bit in Register C (Figure 4). Periodic interrupts that occur at a rate of greater than tBUC allow valid time and date information to be reached at each occurrence of the periodic interrupt. The reads should be complete within (tPI / 2 + tBUC) to ensure that data is not read during the update cycle.
I believe the driver is currently using #2. Other drivers (rtc-sh.c) appear to use a do/while loop and check a bit that I assume is functionally equivalent to UIP here, but I'm not sure if that is accurate or not (and I have not dug up the manual for whatever chip rtc-sh.c touches).
What's the best approach to use here to avoid an access lockout? Should I run into an access lockout, what's the best way to handle that?
Quoted 3 lines
What do you mean by 'quickly turning around and writing the values back'? The
rtc_timestruct passed to theset_timecallback is supposed to contain only valid values.
The way I wrote ds1685_rtc_set_time copies the values from rtc_time out to local variables, runs the checks you say I need to remove, then if those pass, writes those values to the RTC registers. I guess what your saying is that the checks are unnecessary because rtc_time has already been checked, so I'm just duplicating work and can write the values straight to the RTC registers?
Quoted 3 lines
Well, if it is going to be shared it should probably remain somewhere in
include/, but everything thats not shared should be moved tortc-ds1685.clike for exampleds1685_priv.
I think for now, moving everything into drivers/rtc works best. I'll work from the linux-mips end on how IP32 needs to access this header file when I get that far.
Quoted 9 lines
I don't know what you are trying to do, but the current code is extremely unreadable.
You have all those variables declared in your functions which are on first sight not used, because they are only referenced from the macros. Furthermore the invocation of the macro has not the syntax of a function call, although semantically that is what it is.
And especiallyds1685_rtc_begin_data_accessis dangerous, because of the 'return 1', there is no indication when you read the code that a function could magically exit upon invoking that macro.
Well, I thought I was being fancy and cute, but that's what code review is all about. I'm working on breaking these up into inlinable functions and leave that bit of work up to the compiler.
Quoted 1 line
Why? The compiled code will probably be exactly the same as now.
Per above, if I can re-work the RTC access loop, I can eliminate this one bit and then this concern becomes moot.
Thanks!,
Signature · 7 lines
Joshua Kinard Gentoo/MIPS [email protected] "The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between." --Emperor Turhan, Centauri Republic
Quoted 4 lines · Lars-Peter Clausen
That is what I meant. Pass the return value of
rtc_valid_tmon, instead of setting the time to 0 and pretend everything went fine.
You can still keep thedev_errthough, no problem with that.
Okay, I got confused by this and thought you were referring to the quoted test box below in your first response. My bad. You're referring to the tail end of ds1685_rtc_read_time.
To clarify, this construct exists in four existing RTC drivers. If this is wrong, I suggest fixing these four drivers, lest someone else come along and try to copy the idea, thinking it's the RightThing();
drivers/rtc/rtc-ds1553.c:131 drivers/rtc/rtc-ds1742.c:119 drivers/rtc/rtc-rs5c348.c:136 drivers/rtc/rtc-stk17ta8.c:133
I am going to assume the proper approach is:
return rtc_Valid_tm(tm);
Cheers!,
Signature · 7 lines
Joshua Kinard Gentoo/MIPS [email protected] "The past tempts us, the present confuses us, the future frightens us. And our lives slip away, moment by moment, lost in that vast, terrible in-between." --Emperor Turhan, Centauri Republic