All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
@ 2011-02-15 11:39 Kumba
  2011-02-16 23:23 ` Lars-Peter Clausen
  0 siblings, 1 reply; 10+ messages in thread
From: Kumba @ 2011-02-15 11:39 UTC (permalink / raw)
  To: Linux MIPS List

Add Dallas/Maxim DS1685/1687 RTC Support.

Signed-off-by: Joshua Kinard <kumba@gentoo.org>
---
  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
@@ -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
diff -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 -0500
@@ -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
diff -Naurp linux-2.6.37.orig/drivers/rtc/rtc-ds1685.c 
linux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c
--- linux-2.6.37.orig/drivers/rtc/rtc-ds1685.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c	2011-02-15 
04:22:50.032076002 -0500
@@ -0,0 +1,875 @@
+/*
+ * An rtc driver for the Dallas DS1685/DS1687.
+ *
+ * Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
+ * Copyright (C) 2010 Joshua Kinard <kumba@gentoo.org>.
+ *
+ * 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(&regs->time.sec);
+	minutes	= readb(&regs->time.min);
+	hours	= readb(&regs->time.hour);
+	wday	= readb(&regs->time.wday);
+	mday	= readb(&regs->time.mday);
+	month	= readb(&regs->time.month);
+	years	= readb(&regs->time.year);
+	century	= readb(&regs->bank1.century);
+	ctrlb	= readb(&regs->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);
+	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);
+	}
+	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.
+	 */
+	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;
+
+	/*
+	 * Force datamode to BCD (DM=0) and store the time values in the
+	 * RTC registers.
+	*/
+	ds1685_rtc_begin_data_access;
+	data = readb(&regs->time.ctrlb) & ~(RTC_CTRL_B_DM);
+	writeb(data, &regs->time.ctrlb);
+	writeb(seconds, &regs->time.sec);
+	writeb(minutes, &regs->time.min);
+	writeb(hours, &regs->time.hour);
+	writeb(wday, &regs->time.wday);
+	writeb(mday, &regs->time.mday);
+	writeb(month, &regs->time.month);
+	writeb(years, &regs->time.year);
+	writeb(century, &regs->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(&regs->time.sec_alrm);
+	minutes	= readb(&regs->time.min_alrm);
+	hours	= readb(&regs->time.hour_alrm);
+	mday	= readb(&regs->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 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, &regs->time.sec_alrm);
+	writeb(minutes, &regs->time.min_alrm);
+	writeb(hours, &regs->time.hour_alrm);
+	writeb(mday, &regs->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
+ * in irq_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.
+ */
+static inline void
+ds1685_rtc_irq_ctrl(volatile unsigned char *reg, spinlock_t *lock,
+		    const unsigned int *enabled, const unsigned int irq_bit)
+{
+	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.
+ */
+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(&regs->bank1.ctrl4b) | RTC_CTRL_4B_WIE;
+			writeb(data, &regs->bank1.ctrl4b);
+			ds1685_rtc_end_ctrl_access;
+			break;
+
+		case RTC_WIE_OFF:
+			/* Disable Wake-up Alarm Interrupts */
+			ds1685_rtc_begin_ctrl_access;
+			data = readb(&regs->bank1.ctrl4b) & ~(RTC_CTRL_4B_WIE);
+			writeb(data, &regs->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(&regs->time.ctrlb);
+	ctrlc = readb(&regs->time.ctrlc);
+#if 0
+	/* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */
+	ctrl4a = readb(&regs->bank1.ctrl4a);
+	ctrl4b = readb(&regs->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.
+ */
+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(&regs->time.ctrla);
+	ctrlb = readb(&regs->time.ctrlb);
+	ctrlc = readb(&regs->time.ctrlc);
+	ctrld = readb(&regs->time.ctrld);
+	ctrl4a = readb(&regs->bank1.ctrl4a);
+	ctrl4b = readb(&regs->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(&regs->time.nvram1 + pos++);
+		else
+			*buf++ = readb(&regs->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),
+			       &regs->bank1.ext_nvram_addr);
+			*buf++ = readb(&regs->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++, &regs->time.nvram1 + pos++);
+		else
+			writeb(*buf++, &regs->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),
+			       &regs->bank1.ext_nvram_addr);
+			writeb(*buf++, &regs->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(&regs->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(&regs->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;
+
+	/* 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;
+
+	/* 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(&regs->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, &regs->time.ctrla);
+
+	/* Prefer BCD mode (DM = 0). */
+	ctrlb = readb(&regs->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, &regs->time.ctrlb);
+	}
+
+	/* Check the batteries.  There can be a main and/or an aux battery. */
+	ctrld = readb(&regs->time.ctrld);
+	if (!(ctrld & RTC_CTRL_D_VRT))
+		dev_warn(&pdev->dev,
+			 "Main battery is exhausted or not available.\n");
+	ctrl4a = readb(&regs->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(&regs->time.ctrlb);
+		ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
+		writeb(ctrlb, &regs->time.ctrlb);
+
+		/* Reading Ctrl C auto-clears PF/AF/UF. */
+		ctrlc = readb(&regs->time.ctrlc);
+
+		/* Read Ctrl 4B and clear RIE/WIE/KSE. */
+		ctrl4b = readb(&regs->bank1.ctrl4b);
+		ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
+		writeb(ctrl4b, &regs->bank1.ctrl4b);
+
+		/* Manually clear RF/WF/KF in Ctrl 4A. */
+		ctrl4a = readb(&regs->bank1.ctrl4a);
+		ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
+		writeb(ctrl4a, &regs->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);
+
+	/* 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 (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(&regs->time.ctrlb);
+		ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
+		writeb(ctrlb, &regs->time.ctrlb);
+
+		/* Reading Ctrl C auto-clears PF/AF/UF. */
+		ctrlc = readb(&regs->time.ctrlc);
+
+		/* Read Ctrl 4B and clear RIE/WIE/KSE. */
+		ctrl4b = readb(&regs->bank1.ctrl4b);
+		ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
+		writeb(ctrl4b, &regs->bank1.ctrl4b);
+
+		/* Manually clear RF/WF/KF in Ctrl 4A. */
+		ctrl4a = readb(&regs->bank1.ctrl4a);
+		ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
+		writeb(ctrl4a, &regs->bank1.ctrl4a);
+
+		/* Free the IRQ. */
+		free_irq(pdata->irq, pdev);
+	}
+
+	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 <matthias.fuchs@esd-electronics.com>, "
+	      "Joshua Kinard <kumba@gentoo.org>");
+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.h 
linux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h
--- linux-2.6.37.orig/include/linux/rtc/ds1685.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h	2011-02-15 
04:28:04.582076001 -0500
@@ -0,0 +1,401 @@
+/*
+ * 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 <matthias.fuchs@esd-electronics.com>
+ * Copyright (C) 2010 Joshua Kinard <kumba@gentoo.org>
+ *
+ * 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 the ds1685_begin_access macro while loop to avoid RTC access lockouts.
+ */
+#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
+
+
+/*
+ * 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
+
+/*
+ * 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.
+ */
+#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 */
+#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 */
+#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.
+ */
+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.
+ */
+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;
+
+
+/*
+ * 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.
+ */
+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 */
+	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
+};
+
+
+/*
+ * 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(&regs->time.ctrlb) | RTC_CTRL_B_SET;	\
+	writeb(data, &regs->time.ctrlb)
+
+#define ds1685_rtc_clear_set_bit				\
+	data = readb(&regs->time.ctrlb) & ~(RTC_CTRL_B_SET);	\
+	writeb(data, &regs->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.
+ */
+#define ds1685_rtc_switch_to_bank0				\
+	data = readb(&regs->time.ctrla) & ~(RTC_CTRL_A_DV0);	\
+	writeb(data, &regs->time.ctrla)
+
+#define ds1685_rtc_switch_to_bank1				\
+	data = readb(&regs->time.ctrla) | RTC_CTRL_A_DV0;	\
+	writeb(data, &regs->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(&regs->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(&regs->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(&regs->bank1.model);			\
+	ssn[1] = readb(&regs->bank1.ssn1);			\
+	ssn[2] = readb(&regs->bank1.ssn2);			\
+	ssn[3] = readb(&regs->bank1.ssn3);			\
+	ssn[4] = readb(&regs->bank1.ssn4);			\
+	ssn[5] = readb(&regs->bank1.ssn5);			\
+	ssn[6] = readb(&regs->bank1.ssn6);			\
+	ssn[7] = readb(&regs->bank1.crc)
+
+#endif /* _LINUX_RTC_DS1685_H_ */

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-15 11:39 [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC Kumba
@ 2011-02-16 23:23 ` Lars-Peter Clausen
  2011-02-17  5:45   ` Kumba
  2011-02-17  9:57   ` Kumba
  0 siblings, 2 replies; 10+ messages in thread
From: Lars-Peter Clausen @ 2011-02-16 23:23 UTC (permalink / raw)
  To: Kumba; +Cc: Linux MIPS List, rtc-linux

On 02/15/2011 12:39 PM, Kumba wrote:
> Add Dallas/Maxim DS1685/1687 RTC Support.
> 
> Signed-off-by: Joshua Kinard <kumba@gentoo.org>
> ---
>  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
> @@ -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.

> +    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
> diff -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 -0500
> @@ -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
> diff -Naurp linux-2.6.37.orig/drivers/rtc/rtc-ds1685.c
> linux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c
> --- linux-2.6.37.orig/drivers/rtc/rtc-ds1685.c    1969-12-31
> 19:00:00.000000000 -0500
> +++ linux-2.6.37.rtc-ds1685/drivers/rtc/rtc-ds1685.c    2011-02-15
> 04:22:50.032076002 -0500
> @@ -0,0 +1,875 @@
> +/*
> + * An rtc driver for the Dallas DS1685/DS1687.
> + *
> + * Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
> + * Copyright (C) 2010 Joshua Kinard <kumba@gentoo.org>.
> + *
> + * 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(&regs->time.sec);
> +    minutes    = readb(&regs->time.min);
> +    hours    = readb(&regs->time.hour);
> +    wday    = readb(&regs->time.wday);
> +    mday    = readb(&regs->time.mday);
> +    month    = readb(&regs->time.month);
> +    years    = readb(&regs->time.year);
> +    century    = readb(&regs->bank1.century);
> +    ctrlb    = readb(&regs->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);
> +    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.

> +    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.
> +     */
> +    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.

> +
> +    /*
> +     * Force datamode to BCD (DM=0) and store the time values in the
> +     * RTC registers.
> +    */
> +    ds1685_rtc_begin_data_access;
> +    data = readb(&regs->time.ctrlb) & ~(RTC_CTRL_B_DM);
> +    writeb(data, &regs->time.ctrlb);
> +    writeb(seconds, &regs->time.sec);
> +    writeb(minutes, &regs->time.min);
> +    writeb(hours, &regs->time.hour);
> +    writeb(wday, &regs->time.wday);
> +    writeb(mday, &regs->time.mday);
> +    writeb(month, &regs->time.month);
> +    writeb(years, &regs->time.year);
> +    writeb(century, &regs->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(&regs->time.sec_alrm);
> +    minutes    = readb(&regs->time.min_alrm);
> +    hours    = readb(&regs->time.hour_alrm);
> +    mday    = readb(&regs->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);

> +    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, &regs->time.sec_alrm);
> +    writeb(minutes, &regs->time.min_alrm);
> +    writeb(hours, &regs->time.hour_alrm);
> +    writeb(mday, &regs->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
> + * in irq_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.
> + */
> +static inline void
> +ds1685_rtc_irq_ctrl(volatile unsigned char *reg, spinlock_t *lock,
void __iomem *reg,
> +            const unsigned int *enabled, const unsigned int irq_bit)
Why has 'enabled' to be a pointer?

> +{
> +    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.
> + */
> +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(&regs->bank1.ctrl4b) | RTC_CTRL_4B_WIE;
> +            writeb(data, &regs->bank1.ctrl4b);
> +            ds1685_rtc_end_ctrl_access;
> +            break;
> +
> +        case RTC_WIE_OFF:
> +            /* Disable Wake-up Alarm Interrupts */
> +            ds1685_rtc_begin_ctrl_access;
> +            data = readb(&regs->bank1.ctrl4b) & ~(RTC_CTRL_4B_WIE);
> +            writeb(data, &regs->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(&regs->time.ctrlb);
> +    ctrlc = readb(&regs->time.ctrlc);
> +#if 0
> +    /* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */
> +    ctrl4a = readb(&regs->bank1.ctrl4a);
> +    ctrl4b = readb(&regs->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.
> + */
> +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(&regs->time.ctrla);
> +    ctrlb = readb(&regs->time.ctrlb);
> +    ctrlc = readb(&regs->time.ctrlc);
> +    ctrld = readb(&regs->time.ctrld);
> +    ctrl4a = readb(&regs->bank1.ctrl4a);
> +    ctrl4b = readb(&regs->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(&regs->time.nvram1 + pos++);
> +        else
> +            *buf++ = readb(&regs->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),
> +                   &regs->bank1.ext_nvram_addr);
> +            *buf++ = readb(&regs->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++, &regs->time.nvram1 + pos++);
> +        else
> +            writeb(*buf++, &regs->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),
> +                   &regs->bank1.ext_nvram_addr);
> +            writeb(*buf++, &regs->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(&regs->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(&regs->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;
> +
> +    /* 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;

> +
> +    /* 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(&regs->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, &regs->time.ctrla);
> +
> +    /* Prefer BCD mode (DM = 0). */
> +    ctrlb = readb(&regs->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, &regs->time.ctrlb);
> +    }
> +
> +    /* Check the batteries.  There can be a main and/or an aux battery. */
> +    ctrld = readb(&regs->time.ctrld);
> +    if (!(ctrld & RTC_CTRL_D_VRT))
> +        dev_warn(&pdev->dev,
> +             "Main battery is exhausted or not available.\n");
> +    ctrl4a = readb(&regs->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(&regs->time.ctrlb);
> +        ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
> +        writeb(ctrlb, &regs->time.ctrlb);
> +
> +        /* Reading Ctrl C auto-clears PF/AF/UF. */
> +        ctrlc = readb(&regs->time.ctrlc);
> +
> +        /* Read Ctrl 4B and clear RIE/WIE/KSE. */
> +        ctrl4b = readb(&regs->bank1.ctrl4b);
> +        ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
> +        writeb(ctrl4b, &regs->bank1.ctrl4b);
> +
> +        /* Manually clear RF/WF/KF in Ctrl 4A. */
> +        ctrl4a = readb(&regs->bank1.ctrl4a);
> +        ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
> +        writeb(ctrl4a, &regs->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);
> +
> +    /* 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.

> +    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(&regs->time.ctrlb);
> +        ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
> +        writeb(ctrlb, &regs->time.ctrlb);
> +
> +        /* Reading Ctrl C auto-clears PF/AF/UF. */
> +        ctrlc = readb(&regs->time.ctrlc);
> +
> +        /* Read Ctrl 4B and clear RIE/WIE/KSE. */
> +        ctrl4b = readb(&regs->bank1.ctrl4b);
> +        ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
> +        writeb(ctrl4b, &regs->bank1.ctrl4b);
> +
> +        /* Manually clear RF/WF/KF in Ctrl 4A. */
> +        ctrl4a = readb(&regs->bank1.ctrl4a);
> +        ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
> +        writeb(ctrl4a, &regs->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.

> +    }
> +
> +    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 <matthias.fuchs@esd-electronics.com>, "
> +          "Joshua Kinard <kumba@gentoo.org>");
> +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.h
> linux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h
> --- linux-2.6.37.orig/include/linux/rtc/ds1685.h    1969-12-31
> 19:00:00.000000000 -0500
> +++ linux-2.6.37.rtc-ds1685/include/linux/rtc/ds1685.h    2011-02-15
> 04:28:04.582076001 -0500
> @@ -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/

> +/*
> + * 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 <matthias.fuchs@esd-electronics.com>
> + * Copyright (C) 2010 Joshua Kinard <kumba@gentoo.org>
> + *
> + * 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 the ds1685_begin_access macro while loop to avoid RTC access
> lockouts.
> + */
> +#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
> +
> +
> +/*
> + * 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

> +
> +/*
> + * 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.
> + */
> +#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 */
> +#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 */
> +#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.
> + */
> +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.
> + */
> +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.


> +
> +/*
> + * 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.
> + */
> +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 */
> +    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.

> +/*
> + * 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(&regs->time.ctrlb) | RTC_CTRL_B_SET;    \
> +    writeb(data, &regs->time.ctrlb)
> +
> +#define ds1685_rtc_clear_set_bit                \
> +    data = readb(&regs->time.ctrlb) & ~(RTC_CTRL_B_SET);    \
> +    writeb(data, &regs->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.
> + */
> +#define ds1685_rtc_switch_to_bank0                \
> +    data = readb(&regs->time.ctrla) & ~(RTC_CTRL_A_DV0);    \
> +    writeb(data, &regs->time.ctrla)
> +
> +#define ds1685_rtc_switch_to_bank1                \
> +    data = readb(&regs->time.ctrla) | RTC_CTRL_A_DV0;    \
> +    writeb(data, &regs->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(&regs->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(&regs->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(&regs->bank1.model);            \
> +    ssn[1] = readb(&regs->bank1.ssn1);            \
> +    ssn[2] = readb(&regs->bank1.ssn2);            \
> +    ssn[3] = readb(&regs->bank1.ssn3);            \
> +    ssn[4] = readb(&regs->bank1.ssn4);            \
> +    ssn[5] = readb(&regs->bank1.ssn5);            \
> +    ssn[6] = readb(&regs->bank1.ssn6);            \
> +    ssn[7] = readb(&regs->bank1.crc)
> +
> +#endif /* _LINUX_RTC_DS1685_H_ */
> 
> 

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-16 23:23 ` Lars-Peter Clausen
@ 2011-02-17  5:45   ` Kumba
  2011-02-17  7:31     ` Manuel Lauss
  2011-02-17  9:57   ` Kumba
  1 sibling, 1 reply; 10+ messages in thread
From: Kumba @ 2011-02-17  5:45 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Linux MIPS List, rtc-linux

On 02/16/2011 18:23, Lars-Peter Clausen wrote:
> 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,

-- 
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org

"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

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17  5:45   ` Kumba
@ 2011-02-17  7:31     ` Manuel Lauss
  2011-02-17  8:17       ` Kumba
  0 siblings, 1 reply; 10+ messages in thread
From: Manuel Lauss @ 2011-02-17  7:31 UTC (permalink / raw)
  To: Kumba; +Cc: Lars-Peter Clausen, Linux MIPS List, rtc-linux

> 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).

Have a look at i2c-ocores.c:    Basically you use platform_data to specify
register spacing on the bus.

Manuel

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17  7:31     ` Manuel Lauss
@ 2011-02-17  8:17       ` Kumba
  2011-02-17  8:39         ` Manuel Lauss
  0 siblings, 1 reply; 10+ messages in thread
From: Kumba @ 2011-02-17  8:17 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: Lars-Peter Clausen, Linux MIPS List, rtc-linux

On 02/17/2011 02:31, Manuel Lauss wrote:
>
> Have a look at i2c-ocores.c:    Basically you use platform_data to 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?

-- 
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org

"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

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17  8:17       ` Kumba
@ 2011-02-17  8:39         ` Manuel Lauss
  0 siblings, 0 replies; 10+ messages in thread
From: Manuel Lauss @ 2011-02-17  8:39 UTC (permalink / raw)
  To: Kumba; +Cc: Lars-Peter Clausen, Linux MIPS List, rtc-linux

On Thu, Feb 17, 2011 at 9:17 AM, Kumba <kumba@gentoo.org> wrote:
> On 02/17/2011 02:31, Manuel Lauss wrote:
>>
>> Have a look at i2c-ocores.c:    Basically you use platform_data to 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?

Yep.

Manuel

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-16 23:23 ` Lars-Peter Clausen
  2011-02-17  5:45   ` Kumba
@ 2011-02-17  9:57   ` Kumba
  2011-02-17 11:43     ` Lars-Peter Clausen
  1 sibling, 1 reply; 10+ messages in thread
From: Kumba @ 2011-02-17  9:57 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Linux MIPS List, rtc-linux

On 02/16/2011 18:23, Lars-Peter Clausen wrote:

> 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.


> 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?


> 	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.


> 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.


> 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;

Noted, I see a few drivers using this syntax, so I'll adapt to it.


> If CONFIG_SYSFS is not defined you'll get an compile error.

Noted, thanks!


> Since the irq handler references the rtc device it should be freed before the
> rtc device.

Noted, thanks!


> 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/

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?


> Just use BIT(x) instead of adding these defines

Noted, will research.


> 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.


> 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!,

-- 
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org

"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

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17  9:57   ` Kumba
@ 2011-02-17 11:43     ` Lars-Peter Clausen
  2011-02-17 18:47       ` Kumba
  2011-02-18 11:02       ` Kumba
  0 siblings, 2 replies; 10+ messages in thread
From: Lars-Peter Clausen @ 2011-02-17 11:43 UTC (permalink / raw)
  To: Kumba; +Cc: Linux MIPS List, rtc-linux

On 02/17/2011 10:57 AM, Kumba wrote:
> On 02/16/2011 18:23, Lars-Peter Clausen wrote:
> 
>> 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.

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.

> 
> 
>> 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?
> 

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.

> 
> 
>> 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/
> 
> 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?

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.


> 
> 
>> Just use BIT(x) instead of adding these defines
> 
> Noted, will research.
> 
> 
>> 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.
> 
> 
>> 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?

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.

> 
> 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.

> 
> Thoughts?  The rest should be easy to convert into inlined functions.
> 
> 
> Thanks!,
> 

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17 11:43     ` Lars-Peter Clausen
@ 2011-02-17 18:47       ` Kumba
  2011-02-18 11:02       ` Kumba
  1 sibling, 0 replies; 10+ messages in thread
From: Kumba @ 2011-02-17 18:47 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Linux MIPS List, rtc-linux

On 02/17/2011 06:43, Lars-Peter Clausen wrote:
> 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.

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:

> 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.
>
> 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?


> 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.

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?


> 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.

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.


> 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.

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.


> 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!,

-- 
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org

"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

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

* Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
  2011-02-17 11:43     ` Lars-Peter Clausen
  2011-02-17 18:47       ` Kumba
@ 2011-02-18 11:02       ` Kumba
  1 sibling, 0 replies; 10+ messages in thread
From: Kumba @ 2011-02-18 11:02 UTC (permalink / raw)
  To: Lars-Peter Clausen; +Cc: Linux MIPS List, rtc-linux

On 02/17/2011 06:43, Lars-Peter Clausen wrote:
>
> 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.

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!,

-- 
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org

"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

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

end of thread, other threads:[~2011-02-18 11:02 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-02-15 11:39 [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC Kumba
2011-02-16 23:23 ` Lars-Peter Clausen
2011-02-17  5:45   ` Kumba
2011-02-17  7:31     ` Manuel Lauss
2011-02-17  8:17       ` Kumba
2011-02-17  8:39         ` Manuel Lauss
2011-02-17  9:57   ` Kumba
2011-02-17 11:43     ` Lars-Peter Clausen
2011-02-17 18:47       ` Kumba
2011-02-18 11:02       ` Kumba

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.