Add Dallas/Maxim DS1685/1687 RTC Support.
Signed-off-by: Joshua Kinard <kumba@xxxxxxxxxx>
---
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@xxxxxxxxxxxxxxxxxxx>.
+ * Copyright (C) 2010 Joshua Kinard <kumba@xxxxxxxxxx>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/rtc/ds1685.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#define DRV_VERSION "0.3"
+
+static int
+ds1685_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned long flags, start = jiffies;
+ unsigned int data;
+ unsigned int ctrlb, century;
+ unsigned int seconds, minutes, hours, wday, mday, month, years;
+
+ /* Fetch the time info from the RTC registers. */
+ ds1685_rtc_begin_data_access;
+ seconds = readb(®s->time.sec);
+ minutes = readb(®s->time.min);
+ hours = readb(®s->time.hour);
+ wday = readb(®s->time.wday);
+ mday = readb(®s->time.mday);
+ month = readb(®s->time.month);
+ years = readb(®s->time.year);
+ century = readb(®s->bank1.century);
+ ctrlb = readb(®s->time.ctrlb);
+ ds1685_rtc_end_data_access;
+
+ /* Convert to Binary, perform fixups, and store to rtc_time. */
+ tm->tm_sec = bcd2bin(seconds);
+ tm->tm_min = bcd2bin(minutes);
+ tm->tm_hour = bcd2bin(hours);
+ tm->tm_wday = (bcd2bin(wday) - 1);
+ tm->tm_mday = bcd2bin(mday);
+ 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(®s->time.ctrlb) & ~(RTC_CTRL_B_DM);
+ writeb(data, ®s->time.ctrlb);
+ writeb(seconds, ®s->time.sec);
+ writeb(minutes, ®s->time.min);
+ writeb(hours, ®s->time.hour);
+ writeb(wday, ®s->time.wday);
+ writeb(mday, ®s->time.mday);
+ writeb(month, ®s->time.month);
+ writeb(years, ®s->time.year);
+ writeb(century, ®s->bank1.century);
+ ds1685_rtc_end_data_access;
+
+ return 0;
+}
+
+static int
+ds1685_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned long flags, start = jiffies;
+ unsigned int seconds, minutes, hours, mday;
+
+ /* Fetch the alarm info from the RTC alarm registers. */
+ ds1685_rtc_begin_data_access;
+ seconds = readb(®s->time.sec_alrm);
+ minutes = readb(®s->time.min_alrm);
+ hours = readb(®s->time.hour_alrm);
+ mday = readb(®s->bank1.mday_alrm);
+ ds1685_rtc_end_data_access;
+
+ /* Convert to Binary format and store in rtc_wkalrm. */
+ alrm->time.tm_sec = bcd2bin(seconds);
+ alrm->time.tm_min = bcd2bin(minutes);
+ alrm->time.tm_hour = bcd2bin(hours);
+ alrm->time.tm_mday = bcd2bin(mday);
+
+ return 0;
+}
+
+static int
+ds1685_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned long flags, start = jiffies;
+ unsigned int seconds, minutes, hours, mday;
+
+ /* Fetch the alarm info and convert to BCD. */
+ seconds = bin2bcd(alrm->time.tm_sec);
+ minutes = bin2bcd(alrm->time.tm_min);
+ hours = bin2bcd(alrm->time.tm_hour);
+ mday = bin2bcd(alrm->time.tm_mday);
+
+ /* Write to the four RTC alarm registers. */
+ ds1685_rtc_begin_data_access;
+ writeb(seconds, ®s->time.sec_alrm);
+ writeb(minutes, ®s->time.min_alrm);
+ writeb(hours, ®s->time.hour_alrm);
+ writeb(mday, ®s->bank1.mday_alrm);
+ ds1685_rtc_end_data_access;
+
+ return 0;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+/*
+ * This function enables/disables an interrupt, depending on what is passed
+ * 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(®s->bank1.ctrl4b) | RTC_CTRL_4B_WIE;
+ writeb(data, ®s->bank1.ctrl4b);
+ ds1685_rtc_end_ctrl_access;
+ break;
+
+ case RTC_WIE_OFF:
+ /* Disable Wake-up Alarm Interrupts */
+ ds1685_rtc_begin_ctrl_access;
+ data = readb(®s->bank1.ctrl4b) & ~(RTC_CTRL_4B_WIE);
+ writeb(data, ®s->bank1.ctrl4b);
+ ds1685_rtc_end_ctrl_access;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+#else
+#define ds1685_ioctl NULL
+#define ds1685_rtc_periodic_irq_enable NULL
+#define ds1685_rtc_alarm_irq_enable NULL
+#define ds1685_rtc_update_irq_enable NULL
+#endif /* CONFIG_RTC_INTF_DEV */
+
+static irqreturn_t
+ds1685_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned int ctrlb, ctrlc;
+#if 0
+ /* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */
+ unsigned int ctrl4a, ctrl4b;
+#endif
+ unsigned long flags, events = RTC_IRQF;
+ unsigned int num_irqs = 0;
+
+ /* Fetch data from the four registers holding IRQ info. */
+ ds1685_rtc_begin_ctrl_access;
+ ctrlb = readb(®s->time.ctrlb);
+ ctrlc = readb(®s->time.ctrlc);
+#if 0
+ /* XXX: Ctrl4a/Ctrl4b info unused; needs support in RTC core. */
+ ctrl4a = readb(®s->bank1.ctrl4a);
+ ctrl4b = readb(®s->bank1.ctrl4b);
+#endif
+ ds1685_rtc_end_ctrl_access;
+
+ /* Check to see if the IRQF bit is set. */
+ if (!(ctrlc & RTC_CTRL_C_IRQF))
+ return IRQ_NONE;
+
+ /* Check for alarm interrupts. */
+ if ((ctrlc & RTC_CTRL_C_AF) &&
+ (ctrlb & RTC_CTRL_B_AIE)) {
+ events |= RTC_AF;
+ num_irqs++;
+ }
+
+ /* Check for timer interrupts. */
+ else if ((ctrlc & RTC_CTRL_C_UF) &&
+ (ctrlb & RTC_CTRL_B_UIE)) {
+ events |= RTC_UF;
+ num_irqs++;
+ }
+
+ /* Check for periodic interrupts. */
+ else if ((ctrlc & RTC_CTRL_C_PF) &&
+ (ctrlb & RTC_CTRL_B_PIE)) {
+ events |= RTC_PF;
+ num_irqs++;
+ }
+
+ rtc_update_irq(pdata->rtc, num_irqs, events);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PROC_FS
+#define NUM_REGS 8
+#define NUM_SPACES 4
+
+/*
+ * This prints out the flags of the registers for ds1685_rtc_proc.
+ * It's basically a hex --> binary function, just with extra spacing between
+ * the binary digits. It only works on single-byte hex values (8 bits),
+ * which is all that we need.
+ */
+static char*
+print_regs(unsigned int *hex, char *dest)
+{
+ unsigned int i, j;
+ char *tmp = dest;
+
+ for(i = 0; i < NUM_REGS; i++) {
+ *tmp++ = ((*hex & 0x80) !=0 ? '1' : '0');
+ for (j = 0; j < NUM_SPACES; j++)
+ *tmp++ = ' ';
+ *hex <<= 1;
+ }
+ *tmp++ = '\0';
+
+ return dest;
+}
+
+static int
+ds1685_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned long flags;
+ unsigned int ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b;
+ char bits[NUM_REGS][(NUM_REGS * NUM_SPACES) + NUM_REGS + 1];
+ u8 ssn[8];
+
+ ds1685_rtc_begin_ctrl_access;
+ ds1685_rtc_get_ssn;
+ ctrla = readb(®s->time.ctrla);
+ ctrlb = readb(®s->time.ctrlb);
+ ctrlc = readb(®s->time.ctrlc);
+ ctrld = readb(®s->time.ctrld);
+ ctrl4a = readb(®s->bank1.ctrl4a);
+ ctrl4b = readb(®s->bank1.ctrl4b);
+ ds1685_rtc_end_ctrl_access;
+
+ seq_printf(seq,
+ "Oscillator\t: %s\n"
+ "12/24hr\t\t: %s\n"
+ "DST\t\t: %s\n"
+ "Data mode\t: %s\n"
+ "Battery\t\t: %s\n"
+ "Aux batt\t: %s\n"
+ "Periodic IRQ\t: %s\n"
+ "SQW Freq\t: %s\n"
+ "Serial #\t: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
+ "Register Status\t:\n"
+ " Ctrl A\t: "
+ "UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0\n\t\t: %s\n"
+ " Ctrl B\t: "
+ "SET PIE AIE UIE SQWE DM 24hr DSE\n\t\t: %s\n"
+ " Ctrl C\t: "
+ "IRQF PF AF UF --- --- --- ---\n\t\t: %s\n"
+ " Ctrl D\t: "
+ "VRT --- --- --- --- --- --- ---\n\t\t: %s\n"
+ " Ctrl 4A\t: "
+ "VRT2 INCR BME --- PAB RF WF KF\n\t\t: %s\n"
+ " Ctrl 4B\t: "
+ "ABE E32k CS RCE PRS RIE WIE KSE\n\t\t: %s\n",
+ ((ctrla & RTC_CTRL_A_DV1) ? "enabled" : "disabled"),
+ ((ctrlb & RTC_CTRL_B_2412) ? "24-hour" : "12-hour"),
+ ((ctrlb & RTC_CTRL_B_DSE) ? "enabled" : "disabled"),
+ ((ctrlb & RTC_CTRL_B_DM) ? "binary" : "BCD"),
+ ((ctrld & RTC_CTRL_D_VRT) ? "ok" : "exhausted or n/a"),
+ ((ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "exhausted or n/a"),
+ (!(ctrl4b & RTC_CTRL_4B_E32K) ?
+ pirq_rate[(ctrla & RTC_CTRL_A_RS_MASK)] : "*"),
+ (!((ctrl4b & RTC_CTRL_4B_E32K)) ?
+ sqw_freq[(ctrla & RTC_CTRL_A_RS_MASK)] : "32.768kHz"),
+ ssn[0], ssn[1], ssn[2], ssn[3], ssn[4], ssn[5],
+ ssn[6], ssn[7],
+ print_regs(&ctrla, bits[0]),
+ print_regs(&ctrlb, bits[1]),
+ print_regs(&ctrlc, bits[2]),
+ print_regs(&ctrld, bits[3]),
+ print_regs(&ctrl4a, bits[4]),
+ print_regs(&ctrl4b, bits[5]));
+
+ return 0;
+}
+#else
+#define ds1685_rtc_proc NULL
+#endif /* CONFIG_PROC_FS */
+
+static const struct rtc_class_ops ds1685_rtc_ops = {
+ .ioctl = ds1685_ioctl,
+ .proc = ds1685_rtc_proc,
+ .read_time = ds1685_rtc_read_time,
+ .set_time = ds1685_rtc_set_time,
+ .read_alarm = ds1685_rtc_read_alarm,
+ .set_alarm = ds1685_rtc_set_alarm,
+ .irq_set_state = ds1685_rtc_periodic_irq_enable,
+ .alarm_irq_enable = ds1685_rtc_alarm_irq_enable,
+ .update_irq_enable = ds1685_rtc_update_irq_enable,
+};
+
+#ifdef CONFIG_SYSFS
+static ssize_t
+ds1685_nvram_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ ssize_t count;
+ unsigned int data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ ds1685_rtc_switch_to_bank0;
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0;
+ count++, size--)
+ if (count < NVRAM_SZ_TIME)
+ *buf++ = readb(®s->time.nvram1 + pos++);
+ else
+ *buf++ = readb(®s->bank0.nvram2 + pos++);
+
+ if (size > 0) {
+ ds1685_rtc_switch_to_bank1;
+
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ;
+ count++, size--) {
+ writeb((pos - NVRAM_TOTAL_SZ_BANK0),
+ ®s->bank1.ext_nvram_addr);
+ *buf++ = readb(®s->bank1.ext_nvram_dport);
+ pos++;
+ }
+
+ ds1685_rtc_switch_to_bank0;
+ }
+ spin_unlock_irqrestore(&pdata->lock, flags);
+ return count;
+}
+
+static ssize_t
+ds1685_nvram_write(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ ssize_t count;
+ unsigned int data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ ds1685_rtc_switch_to_bank0;
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0;
+ count++, size--)
+ if (count < NVRAM_SZ_TIME)
+ writeb(*buf++, ®s->time.nvram1 + pos++);
+ else
+ writeb(*buf++, ®s->bank0.nvram2 + pos++);
+
+ if (size > 0) {
+ ds1685_rtc_switch_to_bank1;
+
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ;
+ count++, size--) {
+ writeb((pos - NVRAM_TOTAL_SZ_BANK0),
+ ®s->bank1.ext_nvram_addr);
+ writeb(*buf++, ®s->bank1.ext_nvram_dport);
+ pos++;
+ }
+
+ ds1685_rtc_switch_to_bank0;
+ }
+ spin_unlock_irqrestore(&pdata->lock, flags);
+
+ return count;
+}
+
+static struct bin_attribute ds1685_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .read = ds1685_nvram_read,
+ .write = ds1685_nvram_write,
+ .size = NVRAM_TOTAL_SZ
+};
+
+static ssize_t
+ds1685_sysfs_show_battery(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned long flags;
+
+ ds1685_rtc_begin_ctrl_access;
+ data = readb(®s->time.ctrld);
+ ds1685_rtc_end_ctrl_access;
+
+ return sprintf(buf, "%s\n",
+ (data & RTC_CTRL_D_VRT) ? "ok" : "exhausted or n/a");
+}
+
+static DEVICE_ATTR(battery, S_IRUGO, ds1685_sysfs_show_battery, NULL);
+
+static ssize_t
+ds1685_sysfs_show_auxbatt(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ unsigned int data;
+ unsigned long flags;
+
+ ds1685_rtc_begin_ctrl_access;
+ data = readb(®s->bank1.ctrl4a);
+ ds1685_rtc_end_ctrl_access;
+
+ return sprintf(buf, "%s\n",
+ (data & RTC_CTRL_4A_VRT2) ? "ok" : "exhausted or n/a");
+}
+
+static DEVICE_ATTR(auxbatt, S_IRUGO, ds1685_sysfs_show_auxbatt, NULL);
+
+static ssize_t
+ds1685_sysfs_show_serial(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *pdata = platform_get_drvdata(pdev);
+ struct ds1685_rtc_regs __iomem *regs = pdata->regs;
+ u8 ssn[8];
+ unsigned int data;
+ unsigned long flags;
+
+ ds1685_rtc_begin_ctrl_access;
+ ds1685_rtc_get_ssn;
+ ds1685_rtc_end_ctrl_access;
+
+ return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ssn[0], ssn[1], ssn[2], ssn[3],
+ ssn[4], ssn[5], ssn[6], ssn[7]);
+
+ return 0;
+}
+
+static DEVICE_ATTR(serial, S_IRUGO, ds1685_sysfs_show_serial, NULL);
+
+static int
+ds1685_sysfs_register(struct device *dev)
+{
+ int err;
+
+ err = sysfs_create_bin_file(&dev->kobj, &ds1685_nvram_attr);
+ if (err)
+ return err;
+
+ err = device_create_file(dev, &dev_attr_battery);
+ if (err) {
+ device_remove_file(dev, &dev_attr_battery);
+ goto out;
+ }
+
+ err = device_create_file(dev, &dev_attr_auxbatt);
+ if (err) {
+ device_remove_file(dev, &dev_attr_auxbatt);
+ goto out;
+ }
+
+ err = device_create_file(dev, &dev_attr_serial);
+ if (err) {
+ device_remove_file(dev, &dev_attr_serial);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ sysfs_remove_bin_file(&dev->kobj, &ds1685_nvram_attr);
+ return err;
+}
+
+static int
+ds1685_sysfs_unregister(struct device *dev)
+{
+ sysfs_remove_bin_file(&dev->kobj, &ds1685_nvram_attr);
+ device_remove_file(dev, &dev_attr_battery);
+ device_remove_file(dev, &dev_attr_auxbatt);
+ device_remove_file(dev, &dev_attr_serial);
+
+ return 0;
+}
+#endif /* CONFIG_SYSFS */
+
+static int __devinit
+ds1685_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = NULL;
+ struct device *dev = NULL;
+ struct resource *res = NULL;
+ struct ds1685_priv *pdata = NULL;
+ struct ds1685_rtc_regs __iomem *regs = NULL;
+ int ret = 0;
+ unsigned int data, ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b;
+ unsigned long flags;
+
+ /* 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(®s->time.ctrla);
+ if (!(ctrla & RTC_CTRL_A_DV1)) {
+ dev_warn(&pdev->dev,
+ "oscillator stop detected - enabled!\n");
+ ctrla |= RTC_CTRL_A_DV1;
+ }
+ ctrla &= ~RTC_CTRL_A_DV2;
+ writeb(ctrla, ®s->time.ctrla);
+
+ /* Prefer BCD mode (DM = 0). */
+ ctrlb = readb(®s->time.ctrlb);
+ if (ctrlb & RTC_CTRL_B_DM) {
+ dev_info(&pdev->dev, "Setting data mode to BCD\n");
+ ctrlb &= ~RTC_CTRL_B_DM;
+ writeb(ctrlb, ®s->time.ctrlb);
+ }
+
+ /* Check the batteries. There can be a main and/or an aux battery. */
+ ctrld = readb(®s->time.ctrld);
+ if (!(ctrld & RTC_CTRL_D_VRT))
+ dev_warn(&pdev->dev,
+ "Main battery is exhausted or not available.\n");
+ ctrl4a = readb(®s->bank1.ctrl4a);
+ if (!(ctrl4a & RTC_CTRL_4A_VRT2))
+ dev_warn(&pdev->dev,
+ "Aux battery is exhausted or not available.\n");
+
+ /* Setup the interrupt handler. */
+ if (pdata->irq > 0) {
+ /* Read Ctrl B and clear PIE/AIE/UIE. */
+ ctrlb = readb(®s->time.ctrlb);
+ ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
+ writeb(ctrlb, ®s->time.ctrlb);
+
+ /* Reading Ctrl C auto-clears PF/AF/UF. */
+ ctrlc = readb(®s->time.ctrlc);
+
+ /* Read Ctrl 4B and clear RIE/WIE/KSE. */
+ ctrl4b = readb(®s->bank1.ctrl4b);
+ ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
+ writeb(ctrl4b, ®s->bank1.ctrl4b);
+
+ /* Manually clear RF/WF/KF in Ctrl 4A. */
+ ctrl4a = readb(®s->bank1.ctrl4a);
+ ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
+ writeb(ctrl4a, ®s->bank1.ctrl4a);
+
+ /* Request an IRQ. */
+ ret = request_irq(pdata->irq, ds1685_rtc_irq_handler,
+ IRQF_SHARED, pdev->name, pdev);
+
+ /* Check to see if something came back. */
+ if (unlikely(ret)) {
+ dev_warn(&pdev->dev, "RTC interrupt not available\n");
+ pdata->irq = 0;
+ }
+ }
+
+ /* Setup complete. */
+ ds1685_rtc_end_ctrl_access;
+
+ /* Register the device as an RTC. */
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &ds1685_rtc_ops, THIS_MODULE);
+
+ /* 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(®s->time.ctrlb);
+ ctrlb &= ~(RTC_CTRL_B_PIE & RTC_CTRL_B_AIE & RTC_CTRL_B_UIE);
+ writeb(ctrlb, ®s->time.ctrlb);
+
+ /* Reading Ctrl C auto-clears PF/AF/UF. */
+ ctrlc = readb(®s->time.ctrlc);
+
+ /* Read Ctrl 4B and clear RIE/WIE/KSE. */
+ ctrl4b = readb(®s->bank1.ctrl4b);
+ ctrl4b &= ~(RTC_CTRL_4B_RIE & RTC_CTRL_4B_WIE & RTC_CTRL_4B_KSE);
+ writeb(ctrl4b, ®s->bank1.ctrl4b);
+
+ /* Manually clear RF/WF/KF in Ctrl 4A. */
+ ctrl4a = readb(®s->bank1.ctrl4a);
+ ctrl4a &= ~(RTC_CTRL_4A_RF & RTC_CTRL_4A_WF & RTC_CTRL_4A_KF);
+ writeb(ctrl4a, ®s->bank1.ctrl4a);
+
+ /* 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@xxxxxxxxxxxxxxxxxxx>, "
+ "Joshua Kinard <kumba@xxxxxxxxxx>");
+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@xxxxxxxxxxxxxxxxxxx>
+ * Copyright (C) 2010 Joshua Kinard <kumba@xxxxxxxxxx>
+ *
+ * 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(®s->time.ctrlb) | RTC_CTRL_B_SET; \
+ writeb(data, ®s->time.ctrlb)
+
+#define ds1685_rtc_clear_set_bit \
+ data = readb(®s->time.ctrlb) & ~(RTC_CTRL_B_SET); \
+ writeb(data, ®s->time.ctrlb)
+
+
+/*
+ * These two macros switch between bank0 and bank1. Bank0 provides access
+ * to the standard RTC capabilities originally defined with the DS1286 RTC.
+ * Bank1 provides access to extended capabilities, including extended
+ * control registers, silicon serial number, century counter, aux battery
+ * capabilities, wake-up/kick-start features and additional amounts of nvram.
+ */
+#define ds1685_rtc_switch_to_bank0 \
+ data = readb(®s->time.ctrla) & ~(RTC_CTRL_A_DV0); \
+ writeb(data, ®s->time.ctrla)
+
+#define ds1685_rtc_switch_to_bank1 \
+ data = readb(®s->time.ctrla) | RTC_CTRL_A_DV0; \
+ writeb(data, ®s->time.ctrla)
+
+
+/*
+ * This begins the RTC data access, such as reading/writing clock/alarm
+ * registers. It performs several steps in a common block of code that is
+ * used quite frequently:
+ *
+ * - Sets a spinlock on the IRQ.
+ * - Sets the SET bit in Control Register B.
+ * - Reads Control Register A.
+ * - Checks the UIP bit in Control Register A. If UIP is active,
+ * a delay is forced and a check is run to see if RTC access was
+ * locked out. The loop runs until UIP is not set.
+ * - A switch to bank1 occurs. This allows access to all the relevant
+ * time data, since the time registers are available regardless of
+ * which bank is currently selected.
+ */
+#define ds1685_rtc_begin_data_access \
+ spin_lock_irqsave(&pdata->lock, flags); \
+ ds1685_rtc_set_set_bit; \
+ data = readb(®s->time.ctrla); \
+ while (data & RTC_CTRL_A_UIP) { \
+ udelay(10); \
+ if (jiffies > start + DS1685_MAGIC) { \
+ dev_err(dev, "Access lockout!\n"); \
+ return 1; \
+ } \
+ data = readb(®s->time.ctrla); \
+ } \
+ ds1685_rtc_switch_to_bank1
+
+/*
+ * This ends the RTC data access:
+ * - It switches back to bank0.
+ * - It clears the SET bit in Control Register B.
+ * - It unsets the spinlock on the IRQ.
+ */
+#define ds1685_rtc_end_data_access \
+ ds1685_rtc_switch_to_bank0; \
+ ds1685_rtc_clear_set_bit; \
+ spin_unlock_irqrestore(&pdata->lock, flags)
+
+
+/*
+ * This begins the RTC access to the control registers only. Such
+ * accesses need far less handling, just a spinlock and a switch to
+ * bank1.
+ */
+#define ds1685_rtc_begin_ctrl_access \
+ spin_lock_irqsave(&pdata->lock, flags); \
+ ds1685_rtc_switch_to_bank1
+
+/*
+ * This ends the RTC ctrl access:
+ * - It switches back to bank0.
+ * - It unsets the spinlock on the IRQ.
+ */
+#define ds1685_rtc_end_ctrl_access \
+ ds1685_rtc_switch_to_bank0; \
+ spin_unlock_irqrestore(&pdata->lock, flags)
+
+
+/*
+ * This fetches the Silicon Serial Number, a unique ID specific to every
+ * DS1685/1687.
+ *
+ * This number starts at 0x40, and is 8-bytes long, ending at 0x47.
+ * The first byte is the model #, the next six bytes are the serial
+ * number digits, and the final byte is a CRC check byte. Together,
+ * they form the SSN of the RTC.
+ */
+#define ds1685_rtc_get_ssn \
+ ssn[0] = readb(®s->bank1.model); \
+ ssn[1] = readb(®s->bank1.ssn1); \
+ ssn[2] = readb(®s->bank1.ssn2); \
+ ssn[3] = readb(®s->bank1.ssn3); \
+ ssn[4] = readb(®s->bank1.ssn4); \
+ ssn[5] = readb(®s->bank1.ssn5); \
+ ssn[6] = readb(®s->bank1.ssn6); \
+ ssn[7] = readb(®s->bank1.crc)
+
+#endif /* _LINUX_RTC_DS1685_H_ */