From: Lars-Peter Clausen <lars@metafoo.de>
To: Kumba <kumba@gentoo.org>
Cc: Linux MIPS List <linux-mips@linux-mips.org>, rtc-linux@googlegroups.com
Subject: Re: [PATCH 1/2]: Add support for Dallas/Maxim DS1685/1687 RTC
Date: Thu, 17 Feb 2011 00:23:18 +0100 [thread overview]
Message-ID: <4D5C5C66.6060205@metafoo.de> (raw)
In-Reply-To: <4D5A65E3.1050707@gentoo.org>
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(®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);
> + }
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(®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 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, ®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,
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(®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;
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(®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 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(®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);
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(®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 prev parent reply other threads:[~2011-02-16 23:23 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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=4D5C5C66.6060205@metafoo.de \
--to=lars@metafoo.de \
--cc=kumba@gentoo.org \
--cc=linux-mips@linux-mips.org \
--cc=rtc-linux@googlegroups.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox