From: Kumba <kumba@gentoo.org>
To: Linux MIPS List <linux-mips@linux-mips.org>
Subject: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
Date: Tue, 15 Feb 2011 06:39:15 -0500 [thread overview]
Message-ID: <4D5A65E3.1050707@gentoo.org> (raw)
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(®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@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(®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_ */
next reply other threads:[~2011-02-15 11:40 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-15 11:39 Kumba [this message]
2011-02-16 23:23 ` [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4D5A65E3.1050707@gentoo.org \
--to=kumba@gentoo.org \
--cc=linux-mips@linux-mips.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.