diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 66 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-at32ap700x.c | 317 | ||||
-rw-r--r-- | drivers/rtc/rtc-dev.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1216.c | 226 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 300 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1553.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1742.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t80.c | 917 | ||||
-rw-r--r-- | drivers/rtc/rtc-m48t59.c | 491 | ||||
-rw-r--r-- | drivers/rtc/rtc-rs5c372.c | 95 | ||||
-rw-r--r-- | drivers/rtc/rtc-vr41xx.c | 186 | ||||
-rw-r--r-- | drivers/rtc/rtc-x1205.c | 5 |
13 files changed, 2324 insertions, 307 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4e4c10a7fd3..cea401feb0f 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -10,7 +10,6 @@ config RTC_LIB config RTC_CLASS tristate "RTC class" - depends on EXPERIMENTAL default n select RTC_LIB help @@ -119,7 +118,7 @@ config RTC_DRV_TEST will be called rtc-test. comment "I2C RTC drivers" - depends on RTC_CLASS + depends on RTC_CLASS && I2C config RTC_DRV_DS1307 tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00" @@ -160,11 +159,11 @@ config RTC_DRV_MAX6900 will be called rtc-max6900. config RTC_DRV_RS5C372 - tristate "Ricoh RS5C372A/B" + tristate "Ricoh RS5C372A/B, RV5C386, RV5C387A" depends on RTC_CLASS && I2C help If you say yes here you get support for the - Ricoh RS5C372A and RS5C372B RTC chips. + Ricoh RS5C372A, RS5C372B, RV5C386, and RV5C387A RTC chips. This driver can also be built as a module. If so, the module will be called rtc-rs5c372. @@ -213,12 +212,40 @@ config RTC_DRV_PCF8583 This driver can also be built as a module. If so, the module will be called rtc-pcf8583. +config RTC_DRV_M41T80 + tristate "ST M41T80 series RTC" + depends on RTC_CLASS && I2C + help + If you say Y here you will get support for the + ST M41T80 RTC chips series. Currently following chips are + supported: M41T80, M41T81, M41T82, M41T83, M41ST84, M41ST85 + and M41ST87. + + This driver can also be built as a module. If so, the module + will be called rtc-m41t80. + +config RTC_DRV_M41T80_WDT + bool "ST M41T80 series RTC watchdog timer" + depends on RTC_DRV_M41T80 + help + If you say Y here you will get support for the + watchdog timer in ST M41T80 RTC chips series. + +config RTC_DRV_TWL92330 + boolean "TI TWL92330/Menelaus" + depends on RTC_CLASS && I2C && MENELAUS + help + If you say yes here you get support for the RTC on the + TWL92330 "Menelaus" power mangement chip, used with OMAP2 + platforms. The support is integrated with the rest of + the Menelaus driver; it's not separate module. + comment "SPI RTC drivers" - depends on RTC_CLASS + depends on RTC_CLASS && SPI_MASTER config RTC_DRV_RS5C348 tristate "Ricoh RS5C348A/B" - depends on RTC_CLASS && SPI + depends on RTC_CLASS && SPI_MASTER help If you say yes here you get support for the Ricoh RS5C348A and RS5C348B RTC chips. @@ -228,7 +255,7 @@ config RTC_DRV_RS5C348 config RTC_DRV_MAX6902 tristate "Maxim 6902" - depends on RTC_CLASS && SPI + depends on RTC_CLASS && SPI_MASTER help If you say yes here you will get support for the Maxim MAX6902 SPI RTC chip. @@ -246,7 +273,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ - || M32R || ATARI || POWERPC || MIPS) + || M32R || ATARI || PPC || MIPS) help Say "yes" here to get direct support for the real time clock found in every PC or ACPI-based system, and some other boards. @@ -262,6 +289,12 @@ config RTC_DRV_CMOS This driver can also be built as a module. If so, the module will be called rtc-cmos. +config RTC_DRV_DS1216 + tristate "Dallas DS1216" + depends on RTC_CLASS && SNI_RM + help + If you say yes here you get support for the Dallas DS1216 RTC chips. + config RTC_DRV_DS1553 tristate "Dallas DS1553" depends on RTC_CLASS @@ -292,6 +325,16 @@ config RTC_DRV_M48T86 This driver can also be built as a module. If so, the module will be called rtc-m48t86. +config RTC_DRV_M48T59 + tristate "ST M48T59" + depends on RTC_CLASS + help + If you say Y here you will get support for the + ST M48T59 RTC chip. + + This driver can also be built as a module, if so, the module + will be called "rtc-m48t59". + config RTC_DRV_V3020 tristate "EM Microelectronic V3020" depends on RTC_CLASS @@ -379,6 +422,13 @@ config RTC_DRV_PL031 To compile this driver as a module, choose M here: the module will be called rtc-pl031. +config RTC_DRV_AT32AP700X + tristate "AT32AP700X series RTC" + depends on RTC_CLASS && PLATFORM_AT32AP + help + Driver for the internal RTC (Realtime Clock) on Atmel AVR32 + AT32AP700x family processors. + config RTC_DRV_AT91RM9200 tristate "AT91RM9200" depends on RTC_CLASS && ARCH_AT91RM9200 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index a1afbc23607..3109af9a165 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o +obj-$(CONFIG_RTC_DRV_AT32AP700X) += rtc-at32ap700x.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o @@ -28,6 +29,7 @@ obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o +obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o @@ -41,3 +43,5 @@ obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o +obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o +obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c new file mode 100644 index 00000000000..2999214ca53 --- /dev/null +++ b/drivers/rtc/rtc-at32ap700x.c @@ -0,0 +1,317 @@ +/* + * An RTC driver for the AVR32 AT32AP700x processor series. + * + * Copyright (C) 2007 Atmel Corporation + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/io.h> + +/* + * This is a bare-bones RTC. It runs during most system sleep states, but has + * no battery backup and gets reset during system restart. It must be + * initialized from an external clock (network, I2C, etc) before it can be of + * much use. + * + * The alarm functionality is limited by the hardware, not supporting + * periodic interrupts. + */ + +#define RTC_CTRL 0x00 +#define RTC_CTRL_EN 0 +#define RTC_CTRL_PCLR 1 +#define RTC_CTRL_TOPEN 2 +#define RTC_CTRL_PSEL 8 + +#define RTC_VAL 0x04 + +#define RTC_TOP 0x08 + +#define RTC_IER 0x10 +#define RTC_IER_TOPI 0 + +#define RTC_IDR 0x14 +#define RTC_IDR_TOPI 0 + +#define RTC_IMR 0x18 +#define RTC_IMR_TOPI 0 + +#define RTC_ISR 0x1c +#define RTC_ISR_TOPI 0 + +#define RTC_ICR 0x20 +#define RTC_ICR_TOPI 0 + +#define RTC_BIT(name) (1 << RTC_##name) +#define RTC_BF(name, value) ((value) << RTC_##name) + +#define rtc_readl(dev, reg) \ + __raw_readl((dev)->regs + RTC_##reg) +#define rtc_writel(dev, reg, value) \ + __raw_writel((value), (dev)->regs + RTC_##reg) + +struct rtc_at32ap700x { + struct rtc_device *rtc; + void __iomem *regs; + unsigned long alarm_time; + unsigned long irq; + /* Protect against concurrent register access. */ + spinlock_t lock; +}; + +static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); + unsigned long now; + + now = rtc_readl(rtc, VAL); + rtc_time_to_tm(now, tm); + + return 0; +} + +static int at32_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); + unsigned long now; + int ret; + + ret = rtc_tm_to_time(tm, &now); + if (ret == 0) + rtc_writel(rtc, VAL, now); + + return ret; +} + +static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); + + rtc_time_to_tm(rtc->alarm_time, &alrm->time); + alrm->pending = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0; + + return 0; +} + +static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); + unsigned long rtc_unix_time; + unsigned long alarm_unix_time; + int ret; + + rtc_unix_time = rtc_readl(rtc, VAL); + + ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time); + if (ret) + return ret; + + if (alarm_unix_time < rtc_unix_time) + return -EINVAL; + + spin_lock_irq(&rtc->lock); + rtc->alarm_time = alarm_unix_time; + rtc_writel(rtc, TOP, rtc->alarm_time); + if (alrm->pending) + rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) + | RTC_BIT(CTRL_TOPEN)); + else + rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) + & ~RTC_BIT(CTRL_TOPEN)); + spin_unlock_irq(&rtc->lock); + + return ret; +} + +static int at32_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct rtc_at32ap700x *rtc = dev_get_drvdata(dev); + int ret = 0; + + spin_lock_irq(&rtc->lock); + + switch (cmd) { + case RTC_AIE_ON: + if (rtc_readl(rtc, VAL) > rtc->alarm_time) { + ret = -EINVAL; + break; + } + rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) + | RTC_BIT(CTRL_TOPEN)); + rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); + rtc_writel(rtc, IER, RTC_BIT(IER_TOPI)); + break; + case RTC_AIE_OFF: + rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) + & ~RTC_BIT(CTRL_TOPEN)); + rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); + rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + spin_unlock_irq(&rtc->lock); + + return ret; +} + +static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id) +{ + struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id; + unsigned long isr = rtc_readl(rtc, ISR); + unsigned long events = 0; + int ret = IRQ_NONE; + + spin_lock(&rtc->lock); + + if (isr & RTC_BIT(ISR_TOPI)) { + rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI)); + rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); + rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL) + & ~RTC_BIT(CTRL_TOPEN)); + rtc_writel(rtc, VAL, rtc->alarm_time); + events = RTC_AF | RTC_IRQF; + rtc_update_irq(rtc->rtc, 1, events); + ret = IRQ_HANDLED; + } + + spin_unlock(&rtc->lock); + + return ret; +} + +static struct rtc_class_ops at32_rtc_ops = { + .ioctl = at32_rtc_ioctl, + .read_time = at32_rtc_readtime, + .set_time = at32_rtc_settime, + .read_alarm = at32_rtc_readalarm, + .set_alarm = at32_rtc_setalarm, +}; + +static int __init at32_rtc_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct rtc_at32ap700x *rtc; + int irq = -1; + int ret; + + rtc = kzalloc(sizeof(struct rtc_at32ap700x), GFP_KERNEL); + if (!rtc) { + dev_dbg(&pdev->dev, "out of memory\n"); + return -ENOMEM; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no mmio resource defined\n"); + ret = -ENXIO; + goto out; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + ret = -ENXIO; + goto out; + } + + ret = request_irq(irq, at32_rtc_interrupt, IRQF_SHARED, "rtc", rtc); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out; + } + + rtc->irq = irq; + rtc->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!rtc->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto out_free_irq; + } + spin_lock_init(&rtc->lock); + + /* + * Maybe init RTC: count from zero at 1 Hz, disable wrap irq. + * + * Do not reset VAL register, as it can hold an old time + * from last JTAG reset. + */ + if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) { + rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR)); + rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI)); + rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe) + | RTC_BIT(CTRL_EN)); + } + + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &at32_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + dev_dbg(&pdev->dev, "could not register rtc device\n"); + ret = PTR_ERR(rtc->rtc); + goto out_iounmap; + } + + platform_set_drvdata(pdev, rtc); + + dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n", + (unsigned long)rtc->regs, rtc->irq); + + return 0; + +out_iounmap: + iounmap(rtc->regs); +out_free_irq: + free_irq(irq, rtc); +out: + kfree(rtc); + return ret; +} + +static int __exit at32_rtc_remove(struct platform_device *pdev) +{ + struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev); + + free_irq(rtc->irq, rtc); + iounmap(rtc->regs); + rtc_device_unregister(rtc->rtc); + kfree(rtc); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_ALIAS("at32ap700x_rtc"); + +static struct platform_driver at32_rtc_driver = { + .remove = __exit_p(at32_rtc_remove), + .driver = { + .name = "at32ap700x_rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init at32_rtc_init(void) +{ + return platform_driver_probe(&at32_rtc_driver, at32_rtc_probe); +} +module_init(at32_rtc_init); + +static void __exit at32_rtc_exit(void) +{ + platform_driver_unregister(&at32_rtc_driver); +} +module_exit(at32_rtc_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index f4e5f0040ff..304535942de 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -341,6 +341,8 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file, case RTC_IRQP_READ: if (ops->irq_set_freq) err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); + else + err = -ENOTTY; break; case RTC_IRQP_SET: diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c new file mode 100644 index 00000000000..83efb88f8f2 --- /dev/null +++ b/drivers/rtc/rtc-ds1216.c @@ -0,0 +1,226 @@ +/* + * Dallas DS1216 RTC driver + * + * Copyright (c) 2007 Thomas Bogendoerfer + * + */ + +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/bcd.h> + +#define DRV_VERSION "0.1" + +struct ds1216_regs { + u8 tsec; + u8 sec; + u8 min; + u8 hour; + u8 wday; + u8 mday; + u8 month; + u8 year; +}; + +#define DS1216_HOUR_1224 (1 << 7) +#define DS1216_HOUR_AMPM (1 << 5) + +struct ds1216_priv { + struct rtc_device *rtc; + void __iomem *ioaddr; + size_t size; + unsigned long baseaddr; +}; + +static const u8 magic[] = { + 0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c +}; + +/* + * Read the 64 bit we'd like to have - It a series + * of 64 bits showing up in the LSB of the base register. + * + */ +static void ds1216_read(u8 __iomem *ioaddr, u8 *buf) +{ + unsigned char c; + int i, j; + + for (i = 0; i < 8; i++) { + c = 0; + for (j = 0; j < 8; j++) + c |= (readb(ioaddr) & 0x1) << j; + buf[i] = c; + } +} + +static void ds1216_write(u8 __iomem *ioaddr, const u8 *buf) +{ + unsigned char c; + int i, j; + + for (i = 0; i < 8; i++) { + c = buf[i]; + for (j = 0; j < 8; j++) { + writeb(c, ioaddr); + c = c >> 1; + } + } +} + +static void ds1216_switch_ds_to_clock(u8 __iomem *ioaddr) +{ + /* Reset magic pointer */ + readb(ioaddr); + /* Write 64 bit magic to DS1216 */ + ds1216_write(ioaddr, magic); +} + +static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ds1216_priv *priv = platform_get_drvdata(pdev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + tm->tm_sec = BCD2BIN(regs.sec); + tm->tm_min = BCD2BIN(regs.min); + if (regs.hour & DS1216_HOUR_1224) { + /* AM/PM mode */ + tm->tm_hour = BCD2BIN(regs.hour & 0x1f); + if (regs.hour & DS1216_HOUR_AMPM) + tm->tm_hour += 12; + } else + tm->tm_hour = BCD2BIN(regs.hour & 0x3f); + tm->tm_wday = (regs.wday & 7) - 1; + tm->tm_mday = BCD2BIN(regs.mday & 0x3f); + tm->tm_mon = BCD2BIN(regs.month & 0x1f); + tm->tm_year = BCD2BIN(regs.year); + if (tm->tm_year < 70) + tm->tm_year += 100; + return 0; +} + +static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ds1216_priv *priv = platform_get_drvdata(pdev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + regs.tsec = 0; /* clear 0.1 and 0.01 seconds */ + regs.sec = BIN2BCD(tm->tm_sec); + regs.min = BIN2BCD(tm->tm_min); + regs.hour &= DS1216_HOUR_1224; + if (regs.hour && tm->tm_hour > 12) { + regs.hour |= DS1216_HOUR_AMPM; + tm->tm_hour -= 12; + } + regs.hour |= BIN2BCD(tm->tm_hour); + regs.wday &= ~7; + regs.wday |= tm->tm_wday; + regs.mday = BIN2BCD(tm->tm_mday); + regs.month = BIN2BCD(tm->tm_mon); + regs.year = BIN2BCD(tm->tm_year % 100); + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_write(priv->ioaddr, (u8 *)®s); + return 0; +} + +static const struct rtc_class_ops ds1216_rtc_ops = { + .read_time = ds1216_rtc_read_time, + .set_time = ds1216_rtc_set_time, +}; + +static int __devinit ds1216_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + struct ds1216_priv *priv; + int ret = 0; + u8 dummy[8]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + priv = kzalloc(sizeof *priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->size = res->end - res->start + 1; + if (!request_mem_region(res->start, priv->size, pdev->name)) { + ret = -EBUSY; + goto out; + } + priv->baseaddr = res->start; + priv->ioaddr = ioremap(priv->baseaddr, priv->size); + if (!priv->ioaddr) { + ret = -ENOMEM; + goto out; + } + rtc = rtc_device_register("ds1216", &pdev->dev, + &ds1216_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto out; + } + priv->rtc = rtc; + platform_set_drvdata(pdev, priv); + + /* dummy read to get clock into a known state */ + ds1216_read(priv->ioaddr, dummy); + return 0; + +out: + if (priv->rtc) + rtc_device_unregister(priv->rtc); + if (priv->ioaddr) + iounmap(priv->ioaddr); + if (priv->baseaddr) + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return ret; +} + +static int __devexit ds1216_rtc_remove(struct platform_device *pdev) +{ + struct ds1216_priv *priv = platform_get_drvdata(pdev); + + rtc_device_unregister(priv->rtc); + iounmap(priv->ioaddr); + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return 0; +} + +static struct platform_driver ds1216_rtc_platform_driver = { + .driver = { + .name = "rtc-ds1216", + .owner = THIS_MODULE, + }, + .probe = ds1216_rtc_probe, + .remove = __devexit_p(ds1216_rtc_remove), +}; + +static int __init ds1216_rtc_init(void) +{ + return platform_driver_register(&ds1216_rtc_platform_driver); +} + +static void __exit ds1216_rtc_exit(void) +{ + platform_driver_unregister(&ds1216_rtc_platform_driver); +} + +MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); +MODULE_DESCRIPTION("DS1216 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(ds1216_rtc_init); +module_exit(ds1216_rtc_exit); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 3f0f7b8fa81..5158a625671 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -24,29 +24,29 @@ * setting the date and time), Linux can ignore the non-clock features. * That's a natural job for a factory or repair bench. * - * If the I2C "force" mechanism is used, we assume the chip is a ds1337. - * (Much better would be board-specific tables of I2C devices, along with - * the platform_data drivers would use to sort such issues out.) + * This is currently a simple no-alarms driver. If your board has the + * alarm irq wired up on a ds1337 or ds1339, and you want to use that, + * then look at the rtc-rs5c372 driver for code to steal... */ enum ds_type { - unknown = 0, - ds_1307, /* or ds1338, ... */ - ds_1337, /* or ds1339, ... */ - ds_1340, /* or st m41t00, ... */ + ds_1307, + ds_1337, + ds_1338, + ds_1339, + ds_1340, + m41t00, // rs5c372 too? different address... }; -static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - - /* RTC registers don't differ much, except for the century flag */ #define DS1307_REG_SECS 0x00 /* 00-59 */ # define DS1307_BIT_CH 0x80 +# define DS1340_BIT_nEOSC 0x80 #define DS1307_REG_MIN 0x01 /* 00-59 */ #define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ +# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */ +# define DS1307_BIT_PM 0x20 /* in REG_HOUR */ # define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */ # define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */ #define DS1307_REG_WDAY 0x03 /* 01-07 */ @@ -56,11 +56,12 @@ I2C_CLIENT_INSMOD; #define DS1307_REG_YEAR 0x06 /* 00-99 */ /* Other registers (control, status, alarms, trickle charge, NVRAM, etc) - * start at 7, and they differ a lot. Only control and status matter for RTC; - * be careful using them. + * start at 7, and they differ a LOT. Only control and status matter for + * basic RTC date and time functionality; be careful using them. */ -#define DS1307_REG_CONTROL 0x07 +#define DS1307_REG_CONTROL 0x07 /* or ds1338 */ # define DS1307_BIT_OUT 0x80 +# define DS1338_BIT_OSF 0x20 # define DS1307_BIT_SQWE 0x10 # define DS1307_BIT_RS1 0x02 # define DS1307_BIT_RS0 0x01 @@ -71,6 +72,13 @@ I2C_CLIENT_INSMOD; # define DS1337_BIT_INTCN 0x04 # define DS1337_BIT_A2IE 0x02 # define DS1337_BIT_A1IE 0x01 +#define DS1340_REG_CONTROL 0x07 +# define DS1340_BIT_OUT 0x80 +# define DS1340_BIT_FT 0x40 +# define DS1340_BIT_CALIB_SIGN 0x20 +# define DS1340_M_CALIBRATION 0x1f +#define DS1340_REG_FLAG 0x09 +# define DS1340_BIT_OSF 0x80 #define DS1337_REG_STATUS 0x0f # define DS1337_BIT_OSF 0x80 # define DS1337_BIT_A2I 0x02 @@ -84,21 +92,63 @@ struct ds1307 { u8 regs[8]; enum ds_type type; struct i2c_msg msg[2]; - struct i2c_client client; + struct i2c_client *client; + struct i2c_client dev; struct rtc_device *rtc; }; +struct chip_desc { + char name[9]; + unsigned nvram56:1; + unsigned alarm:1; + enum ds_type type; +}; + +static const struct chip_desc chips[] = { { + .name = "ds1307", + .type = ds_1307, + .nvram56 = 1, +}, { + .name = "ds1337", + .type = ds_1337, + .alarm = 1, +}, { + .name = "ds1338", + .type = ds_1338, + .nvram56 = 1, +}, { + .name = "ds1339", + .type = ds_1339, + .alarm = 1, +}, { + .name = "ds1340", + .type = ds_1340, +}, { + .name = "m41t00", + .type = m41t00, +}, }; + +static inline const struct chip_desc *find_chip(const char *s) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(chips); i++) + if (strnicmp(s, chips[i].name, sizeof chips[i].name) == 0) + return &chips[i]; + return NULL; +} static int ds1307_get_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp; - /* read the RTC registers all at once */ + /* read the RTC date and time registers all at once */ ds1307->msg[1].flags = I2C_M_RD; ds1307->msg[1].len = 7; - tmp = i2c_transfer(ds1307->client.adapter, ds1307->msg, 2); + tmp = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent), + ds1307->msg, 2); if (tmp != 2) { dev_err(dev, "%s error %d\n", "read", tmp); return -EIO; @@ -129,7 +179,8 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) t->tm_hour, t->tm_mday, t->tm_mon, t->tm_year, t->tm_wday); - return 0; + /* initial clock setting can be undefined */ + return rtc_valid_tm(t); } static int ds1307_set_time(struct device *dev, struct rtc_time *t) @@ -157,11 +208,18 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) tmp = t->tm_year - 100; buf[DS1307_REG_YEAR] = BIN2BCD(tmp); - if (ds1307->type == ds_1337) + switch (ds1307->type) { + case ds_1337: + case ds_1339: buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; - else if (ds1307->type == ds_1340) + break; + case ds_1340: buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN | DS1340_BIT_CENTURY; + break; + default: + break; + } ds1307->msg[1].flags = 0; ds1307->msg[1].len = 8; @@ -170,7 +228,8 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) "write", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - result = i2c_transfer(ds1307->client.adapter, &ds1307->msg[1], 1); + result = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent), + &ds1307->msg[1], 1); if (result != 1) { dev_err(dev, "%s error %d\n", "write", tmp); return -EIO; @@ -185,25 +244,29 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { static struct i2c_driver ds1307_driver; -static int __devinit -ds1307_detect(struct i2c_adapter *adapter, int address, int kind) +static int __devinit ds1307_probe(struct i2c_client *client) { struct ds1307 *ds1307; int err = -ENODEV; - struct i2c_client *client; int tmp; - - if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; + const struct chip_desc *chip; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + chip = find_chip(client->name); + if (!chip) { + dev_err(&client->dev, "unknown chip type '%s'\n", + client->name); + return -ENODEV; } - client = &ds1307->client; - client->addr = address; - client->adapter = adapter; - client->driver = &ds1307_driver; - client->flags = 0; + if (!i2c_check_functionality(adapter, + I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) + return -ENOMEM; + ds1307->client = client; i2c_set_clientdata(client, ds1307); ds1307->msg[0].addr = client->addr; @@ -216,14 +279,16 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind) ds1307->msg[1].len = sizeof(ds1307->regs); ds1307->msg[1].buf = ds1307->regs; - /* HACK: "force" implies "needs ds1337-style-oscillator setup" */ - if (kind >= 0) { - ds1307->type = ds_1337; + ds1307->type = chip->type; + switch (ds1307->type) { + case ds_1337: + case ds_1339: ds1307->reg_addr = DS1337_REG_CONTROL; ds1307->msg[1].len = 2; - tmp = i2c_transfer(client->adapter, ds1307->msg, 2); + /* get registers that the "rtc" read below won't read... */ + tmp = i2c_transfer(adapter, ds1307->msg, 2); if (tmp != 2) { pr_debug("read error %d\n", tmp); err = -EIO; @@ -233,19 +298,26 @@ ds1307_detect(struct i2c_adapter *adapter, int address, int kind) ds1307->reg_addr = 0; ds1307->msg[1].len = sizeof(ds1307->regs); - /* oscillator is off; need to turn it on */ - if ((ds1307->regs[0] & DS1337_BIT_nEOSC) - || (ds1307->regs[1] & DS1337_BIT_OSF)) { - printk(KERN_ERR "no ds1337 oscillator code\n"); - goto exit_free; + /* oscillator off? turn it on, so clock can tick. */ + if (ds1307->regs[0] & DS1337_BIT_nEOSC) + i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, + ds1307->regs[0] & ~DS1337_BIT_nEOSC); + + /* oscillator fault? clear flag, and warn */ + if (ds1307->regs[1] & DS1337_BIT_OSF) { + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, + ds1307->regs[1] & ~DS1337_BIT_OSF); + dev_warn(&client->dev, "SET TIME!\n"); } - } else - ds1307->type = ds_1307; + break; + default: + break; + } read_rtc: /* read RTC registers */ - tmp = i2c_transfer(client->adapter, ds1307->msg, 2); + tmp = i2c_transfer(adapter, ds1307->msg, 2); if (tmp != 2) { pr_debug("read error %d\n", tmp); err = -EIO; @@ -257,72 +329,80 @@ read_rtc: * still a few values that are clearly out-of-range. */ tmp = ds1307->regs[DS1307_REG_SECS]; - if (tmp & DS1307_BIT_CH) { - if (ds1307->type && ds1307->type != ds_1307) { - pr_debug("not a ds1307?\n"); - goto exit_free; - } - ds1307->type = ds_1307; - - /* this partial initialization should work for ds1307, - * ds1338, ds1340, st m41t00, and more. + switch (ds1307->type) { + case ds_1340: + /* FIXME read register with DS1340_BIT_OSF, use that to + * trigger the "set time" warning (*after* restarting the + * oscillator!) instead of this weaker ds1307/m41t00 test. */ - dev_warn(&client->dev, "oscillator started; SET TIME!\n"); - i2c_smbus_write_byte_data(client, 0, 0); - goto read_rtc; + case ds_1307: + case m41t00: + /* clock halted? turn it on, so clock can tick. */ + if (tmp & DS1307_BIT_CH) { + i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); + dev_warn(&client->dev, "SET TIME!\n"); + goto read_rtc; + } + break; + case ds_1338: + /* clock halted? turn it on, so clock can tick. */ + if (tmp & DS1307_BIT_CH) + i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0); + + /* oscillator fault? clear flag, and warn */ + if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { + i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL, + ds1307->regs[DS1337_REG_CONTROL] + & ~DS1338_BIT_OSF); + dev_warn(&client->dev, "SET TIME!\n"); + goto read_rtc; + } + break; + case ds_1337: + case ds_1339: + break; } + + tmp = ds1307->regs[DS1307_REG_SECS]; tmp = BCD2BIN(tmp & 0x7f); if (tmp > 60) - goto exit_free; + goto exit_bad; tmp = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f); if (tmp > 60) - goto exit_free; + goto exit_bad; tmp = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f); if (tmp == 0 || tmp > 31) - goto exit_free; + goto exit_bad; tmp = BCD2BIN(ds1307->regs[DS1307_REG_MONTH] & 0x1f); if (tmp == 0 || tmp > 12) - goto exit_free; + goto exit_bad; - /* force into in 24 hour mode (most chips) or - * disable century bit (ds1340) - */ tmp = ds1307->regs[DS1307_REG_HOUR]; - if (tmp & (1 << 6)) { - if (tmp & (1 << 5)) - tmp = BCD2BIN(tmp & 0x1f) + 12; - else - tmp = BCD2BIN(tmp); - i2c_smbus_write_byte_data(client, - DS1307_REG_HOUR, - BIN2BCD(tmp)); - } - - /* FIXME chips like 1337 can generate alarm irqs too; those are - * worth exposing through the API (especially when the irq is - * wakeup-capable). - */ - switch (ds1307->type) { - case unknown: - strlcpy(client->name, "unknown", I2C_NAME_SIZE); - break; - case ds_1307: - strlcpy(client->name, "ds1307", I2C_NAME_SIZE); - break; - case ds_1337: - strlcpy(client->name, "ds1337", I2C_NAME_SIZE); - break; case ds_1340: - strlcpy(client->name, "ds1340", I2C_NAME_SIZE); + case m41t00: + /* NOTE: ignores century bits; fix before deploying + * systems that will run through year 2100. + */ break; - } + default: + if (!(tmp & DS1307_BIT_12HR)) + break; - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(client))) - goto exit_free; + /* Be sure we're in 24 hour mode. Multi-master systems + * take note... + */ + tmp = BCD2BIN(tmp & 0x1f); + if (tmp == 12) + tmp = 0; + if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + tmp += 12; + i2c_smbus_write_byte_data(client, + DS1307_REG_HOUR, + BIN2BCD(tmp)); + } ds1307->rtc = rtc_device_register(client->name, &client->dev, &ds13xx_rtc_ops, THIS_MODULE); @@ -330,46 +410,40 @@ read_rtc: err = PTR_ERR(ds1307->rtc); dev_err(&client->dev, "unable to register the class device\n"); - goto exit_detach; + goto exit_free; } return 0; -exit_detach: - i2c_detach_client(client); +exit_bad: + dev_dbg(&client->dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", + "bogus register", + ds1307->regs[0], ds1307->regs[1], + ds1307->regs[2], ds1307->regs[3], + ds1307->regs[4], ds1307->regs[5], + ds1307->regs[6]); + exit_free: kfree(ds1307); -exit: return err; } -static int __devinit -ds1307_attach_adapter(struct i2c_adapter *adapter) -{ - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) - return 0; - return i2c_probe(adapter, &addr_data, ds1307_detect); -} - -static int __devexit ds1307_detach_client(struct i2c_client *client) +static int __devexit ds1307_remove(struct i2c_client *client) { - int err; struct ds1307 *ds1307 = i2c_get_clientdata(client); rtc_device_unregister(ds1307->rtc); - if ((err = i2c_detach_client(client))) - return err; kfree(ds1307); return 0; } static struct i2c_driver ds1307_driver = { .driver = { - .name = "ds1307", + .name = "rtc-ds1307", .owner = THIS_MODULE, }, - .attach_adapter = ds1307_attach_adapter, - .detach_client = __devexit_p(ds1307_detach_client), + .probe = ds1307_probe, + .remove = __devexit_p(ds1307_remove), }; static int __init ds1307_init(void) diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index afa64c7fa2e..f98a83a11aa 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -258,8 +258,9 @@ static const struct rtc_class_ops ds1553_rtc_ops = { .ioctl = ds1553_rtc_ioctl, }; -static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1553_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)); @@ -272,8 +273,9 @@ static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf, return count; } -static ssize_t ds1553_nvram_write(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1553_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)); @@ -290,7 +292,6 @@ static struct bin_attribute ds1553_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUGO | S_IWUGO, - .owner = THIS_MODULE, }, .size = RTC_OFFSET, .read = ds1553_nvram_read, diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index d68288b389d..d1778ae8bca 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -127,8 +127,9 @@ static const struct rtc_class_ops ds1742_rtc_ops = { .set_time = ds1742_rtc_set_time, }; -static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1742_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)); @@ -141,8 +142,9 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf, return count; } -static ssize_t ds1742_nvram_write(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1742_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)); @@ -159,7 +161,6 @@ static struct bin_attribute ds1742_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUGO | S_IWUGO, - .owner = THIS_MODULE, }, .read = ds1742_nvram_read, .write = ds1742_nvram_write, diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c new file mode 100644 index 00000000000..80c4a846306 --- /dev/null +++ b/drivers/rtc/rtc-m41t80.c @@ -0,0 +1,917 @@ +/* + * I2C client/driver for the ST M41T80 family of i2c rtc chips. + * + * Author: Alexander Bigga <ab@mycable.de> + * + * Based on m41t00.c by Mark A. Greer <mgreer@mvista.com> + * + * 2006 (c) mycable GmbH + * + * 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/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/i2c.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#ifdef CONFIG_RTC_DRV_M41T80_WDT +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#endif + +#define M41T80_REG_SSEC 0 +#define M41T80_REG_SEC 1 +#define M41T80_REG_MIN 2 +#define M41T80_REG_HOUR 3 +#define M41T80_REG_WDAY 4 +#define M41T80_REG_DAY 5 +#define M41T80_REG_MON 6 +#define M41T80_REG_YEAR 7 +#define M41T80_REG_ALARM_MON 0xa +#define M41T80_REG_ALARM_DAY 0xb +#define M41T80_REG_ALARM_HOUR 0xc +#define M41T80_REG_ALARM_MIN 0xd +#define M41T80_REG_ALARM_SEC 0xe +#define M41T80_REG_FLAGS 0xf +#define M41T80_REG_SQW 0x13 + +#define M41T80_DATETIME_REG_SIZE (M41T80_REG_YEAR + 1) +#define M41T80_ALARM_REG_SIZE \ + (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON) + +#define M41T80_SEC_ST (1 << 7) /* ST: Stop Bit */ +#define M41T80_ALMON_AFE (1 << 7) /* AFE: AF Enable Bit */ +#define M41T80_ALMON_SQWE (1 << 6) /* SQWE: SQW Enable Bit */ +#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */ +#define M41T80_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */ +#define M41T80_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */ + +#define M41T80_FEATURE_HT (1 << 0) +#define M41T80_FEATURE_BL (1 << 1) + +#define DRV_VERSION "0.05" + +struct m41t80_chip_info { + const char *name; + u8 features; +}; + +static const struct m41t80_chip_info m41t80_chip_info_tbl[] = { + { + .name = "m41t80", + .features = 0, + }, + { + .name = "m41t81", + .features = M41T80_FEATURE_HT, + }, + { + .name = "m41t81s", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, + { + .name = "m41t82", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, + { + .name = "m41t83", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, + { + .name = "m41st84", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, + { + .name = "m41st85", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, + { + .name = "m41st87", + .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, + }, +}; + +struct m41t80_data { + const struct m41t80_chip_info *chip; + struct rtc_device *rtc; +}; + +static int m41t80_get_datetime(struct i2c_client *client, + struct rtc_time *tm) +{ + u8 buf[M41T80_DATETIME_REG_SIZE], dt_addr[1] = { M41T80_REG_SEC }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = dt_addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC, + .buf = buf + M41T80_REG_SEC, + }, + }; + + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + dev_err(&client->dev, "read error\n"); + return -EIO; + } + + tm->tm_sec = BCD2BIN(buf[M41T80_REG_SEC] & 0x7f); + tm->tm_min = BCD2BIN(buf[M41T80_REG_MIN] & 0x7f); + tm->tm_hour = BCD2BIN(buf[M41T80_REG_HOUR] & 0x3f); + tm->tm_mday = BCD2BIN(buf[M41T80_REG_DAY] & 0x3f); + tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07; + tm->tm_mon = BCD2BIN(buf[M41T80_REG_MON] & 0x1f) - 1; + + /* assume 20YY not 19YY, and ignore the Century Bit */ + tm->tm_year = BCD2BIN(buf[M41T80_REG_YEAR]) + 100; + return 0; +} + +/* Sets the given date and time to the real time clock. */ +static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + u8 wbuf[1 + M41T80_DATETIME_REG_SIZE]; + u8 *buf = &wbuf[1]; + u8 dt_addr[1] = { M41T80_REG_SEC }; + struct i2c_msg msgs_in[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = dt_addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC, + .buf = buf + M41T80_REG_SEC, + }, + }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1 + M41T80_DATETIME_REG_SIZE, + .buf = wbuf, + }, + }; + + /* Read current reg values into buf[1..7] */ + if (i2c_transfer(client->adapter, msgs_in, 2) < 0) { + dev_err(&client->dev, "read error\n"); + return -EIO; + } + + wbuf[0] = 0; /* offset into rtc's regs */ + /* Merge time-data and register flags into buf[0..7] */ + buf[M41T80_REG_SSEC] = 0; + buf[M41T80_REG_SEC] = + BIN2BCD(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f); + buf[M41T80_REG_MIN] = + BIN2BCD(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f); + buf[M41T80_REG_HOUR] = + BIN2BCD(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f) ; + buf[M41T80_REG_WDAY] = + (tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07); + buf[M41T80_REG_DAY] = + BIN2BCD(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f); + buf[M41T80_REG_MON] = + BIN2BCD(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f); + /* assume 20YY not 19YY */ + buf[M41T80_REG_YEAR] = BIN2BCD(tm->tm_year % 100); + + if (i2c_transfer(client->adapter, msgs, 1) != 1) { + dev_err(&client->dev, "write error\n"); + return -EIO; + } + return 0; +} + +#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE) +static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); + u8 reg; + + if (clientdata->chip->features & M41T80_FEATURE_BL) { + reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + seq_printf(seq, "battery\t\t: %s\n", + (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok"); + } + return 0; +} +#else +#define m41t80_rtc_proc NULL +#endif + +static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return m41t80_get_datetime(to_i2c_client(dev), tm); +} + +static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return m41t80_set_datetime(to_i2c_client(dev), tm); +} + +#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE) +static int +m41t80_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + + switch (cmd) { + case RTC_AIE_OFF: + case RTC_AIE_ON: + break; + default: + return -ENOIOCTLCMD; + } + + rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (rc < 0) + goto err; + switch (cmd) { + case RTC_AIE_OFF: + rc &= ~M41T80_ALMON_AFE; + break; + case RTC_AIE_ON: + rc |= M41T80_ALMON_AFE; + break; + } + if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, rc) < 0) + goto err; + return 0; +err: + return -EIO; +} +#else +#define m41t80_rtc_ioctl NULL +#endif + +static int m41t80_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 wbuf[1 + M41T80_ALARM_REG_SIZE]; + u8 *buf = &wbuf[1]; + u8 *reg = buf - M41T80_REG_ALARM_MON; + u8 dt_addr[1] = { M41T80_REG_ALARM_MON }; + struct i2c_msg msgs_in[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = dt_addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = M41T80_ALARM_REG_SIZE, + .buf = buf, + }, + }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1 + M41T80_ALARM_REG_SIZE, + .buf = wbuf, + }, + }; + + if (i2c_transfer(client->adapter, msgs_in, 2) < 0) { + dev_err(&client->dev, "read error\n"); + return -EIO; + } + reg[M41T80_REG_ALARM_MON] &= ~(0x1f | M41T80_ALMON_AFE); + reg[M41T80_REG_ALARM_DAY] = 0; + reg[M41T80_REG_ALARM_HOUR] &= ~(0x3f | 0x80); + reg[M41T80_REG_ALARM_MIN] = 0; + reg[M41T80_REG_ALARM_SEC] = 0; + + wbuf[0] = M41T80_REG_ALARM_MON; /* offset into rtc's regs */ + reg[M41T80_REG_ALARM_SEC] |= t->time.tm_sec >= 0 ? + BIN2BCD(t->time.tm_sec) : 0x80; + reg[M41T80_REG_ALARM_MIN] |= t->time.tm_min >= 0 ? + BIN2BCD(t->time.tm_min) : 0x80; + reg[M41T80_REG_ALARM_HOUR] |= t->time.tm_hour >= 0 ? + BIN2BCD(t->time.tm_hour) : 0x80; + reg[M41T80_REG_ALARM_DAY] |= t->time.tm_mday >= 0 ? + BIN2BCD(t->time.tm_mday) : 0x80; + if (t->time.tm_mon >= 0) + reg[M41T80_REG_ALARM_MON] |= BIN2BCD(t->time.tm_mon + 1); + else + reg[M41T80_REG_ALARM_DAY] |= 0x40; + + if (i2c_transfer(client->adapter, msgs, 1) != 1) { + dev_err(&client->dev, "write error\n"); + return -EIO; + } + + if (t->enabled) { + reg[M41T80_REG_ALARM_MON] |= M41T80_ALMON_AFE; + if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + reg[M41T80_REG_ALARM_MON]) < 0) { + dev_err(&client->dev, "write error\n"); + return -EIO; + } + } + return 0; +} + +static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 buf[M41T80_ALARM_REG_SIZE + 1]; /* all alarm regs and flags */ + u8 dt_addr[1] = { M41T80_REG_ALARM_MON }; + u8 *reg = buf - M41T80_REG_ALARM_MON; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = dt_addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = M41T80_ALARM_REG_SIZE + 1, + .buf = buf, + }, + }; + + if (i2c_transfer(client->adapter, msgs, 2) < 0) { + dev_err(&client->dev, "read error\n"); + return -EIO; + } + t->time.tm_sec = -1; + t->time.tm_min = -1; + t->time.tm_hour = -1; + t->time.tm_mday = -1; + t->time.tm_mon = -1; + if (!(reg[M41T80_REG_ALARM_SEC] & 0x80)) + t->time.tm_sec = BCD2BIN(reg[M41T80_REG_ALARM_SEC] & 0x7f); + if (!(reg[M41T80_REG_ALARM_MIN] & 0x80)) + t->time.tm_min = BCD2BIN(reg[M41T80_REG_ALARM_MIN] & 0x7f); + if (!(reg[M41T80_REG_ALARM_HOUR] & 0x80)) + t->time.tm_hour = BCD2BIN(reg[M41T80_REG_ALARM_HOUR] & 0x3f); + if (!(reg[M41T80_REG_ALARM_DAY] & 0x80)) + t->time.tm_mday = BCD2BIN(reg[M41T80_REG_ALARM_DAY] & 0x3f); + if (!(reg[M41T80_REG_ALARM_DAY] & 0x40)) + t->time.tm_mon = BCD2BIN(reg[M41T80_REG_ALARM_MON] & 0x1f) - 1; + t->time.tm_year = -1; + t->time.tm_wday = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE); + t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF); + return 0; +} + +static struct rtc_class_ops m41t80_rtc_ops = { + .read_time = m41t80_rtc_read_time, + .set_time = m41t80_rtc_set_time, + .read_alarm = m41t80_rtc_read_alarm, + .set_alarm = m41t80_rtc_set_alarm, + .proc = m41t80_rtc_proc, + .ioctl = m41t80_rtc_ioctl, +}; + +#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE) +static ssize_t m41t80_sysfs_show_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (val < 0) + return -EIO; + return sprintf(buf, "%#x\n", val); +} +static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL); + +static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val; + + val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + if (val < 0) + return -EIO; + val = (val >> 4) & 0xf; + switch (val) { + case 0: + break; + case 1: + val = 32768; + break; + default: + val = 32768 >> val; + } + return sprintf(buf, "%d\n", val); +} +static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int almon, sqw; + int val = simple_strtoul(buf, NULL, 0); + + if (val) { + if (!is_power_of_2(val)) + return -EINVAL; + val = ilog2(val); + if (val == 15) + val = 1; + else if (val < 14) + val = 15 - val; + else + return -EINVAL; + } + /* disable SQW, set SQW frequency & re-enable */ + almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (almon < 0) + return -EIO; + sqw = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + if (sqw < 0) + return -EIO; + sqw = (sqw & 0x0f) | (val << 4); + if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + almon & ~M41T80_ALMON_SQWE) < 0 || + i2c_smbus_write_byte_data(client, M41T80_REG_SQW, sqw) < 0) + return -EIO; + if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + almon | M41T80_ALMON_SQWE) < 0) + return -EIO; + return count; +} +static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR, + m41t80_sysfs_show_sqwfreq, m41t80_sysfs_set_sqwfreq); + +static struct attribute *attrs[] = { + &dev_attr_flags.attr, + &dev_attr_sqwfreq.attr, + NULL, +}; +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static int m41t80_sysfs_register(struct device *dev) +{ + return sysfs_create_group(&dev->kobj, &attr_group); +} +#else +static int m41t80_sysfs_register(struct device *dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_RTC_DRV_M41T80_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +static struct i2c_client *save_client; + +/* Default margin */ +#define WD_TIMO 60 /* 1..31 seconds */ + +static int wdt_margin = WD_TIMO; +module_param(wdt_margin, int, 0); +MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)"); + +static unsigned long wdt_is_open; +static int boot_flag; + +/** + * wdt_ping: + * + * Reload counter one with the watchdog timeout. We don't bother reloading + * the cascade counter. + */ +static void wdt_ping(void) +{ + unsigned char i2c_data[2]; + struct i2c_msg msgs1[1] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 2, + .buf = i2c_data, + }, + }; + i2c_data[0] = 0x09; /* watchdog register */ + + if (wdt_margin > 31) + i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */ + else + /* + * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02) + */ + i2c_data[1] = wdt_margin<<2 | 0x82; + + i2c_transfer(save_client->adapter, msgs1, 1); +} + +/** + * wdt_disable: + * + * disables watchdog. + */ +static void wdt_disable(void) +{ + unsigned char i2c_data[2], i2c_buf[0x10]; + struct i2c_msg msgs0[2] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 1, + .buf = i2c_data, + }, + { + .addr = save_client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = i2c_buf, + }, + }; + struct i2c_msg msgs1[1] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 2, + .buf = i2c_data, + }, + }; + + i2c_data[0] = 0x09; + i2c_transfer(save_client->adapter, msgs0, 2); + + i2c_data[0] = 0x09; + i2c_data[1] = 0x00; + i2c_transfer(save_client->adapter, msgs1, 1); +} + +/** + * wdt_write: + * @file: file handle to the watchdog + * @buf: buffer to write (unused as data does not matter here + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we we don't define content meaning. + */ +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* Can't seek (pwrite) on this device + if (ppos != &file->f_pos) + return -ESPIPE; + */ + if (count) { + wdt_ping(); + return 1; + } + return 0; +} + +static ssize_t wdt_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +/** + * wdt_ioctl: + * @inode: inode of the device + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. We only actually usefully support + * querying capabilities and current status. + */ +static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin, rv; + static struct watchdog_info ident = { + .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = "M41T80 WTD" + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(boot_flag, (int __user *)arg); + case WDIOC_KEEPALIVE: + wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + /* Arbitrary, can't find the card's limits */ + if (new_margin < 1 || new_margin > 124) + return -EINVAL; + wdt_margin = new_margin; + wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int __user *)arg); + + case WDIOC_SETOPTIONS: + if (copy_from_user(&rv, (int __user *)arg, sizeof(int))) + return -EFAULT; + + if (rv & WDIOS_DISABLECARD) { + printk(KERN_INFO + "rtc-m41t80: disable watchdog\n"); + wdt_disable(); + } + + if (rv & WDIOS_ENABLECARD) { + printk(KERN_INFO + "rtc-m41t80: enable watchdog\n"); + wdt_ping(); + } + + return -EINVAL; + } + return -ENOTTY; +} + +/** + * wdt_open: + * @inode: inode of device + * @file: file handle to device + * + */ +static int wdt_open(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { + if (test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* + * Activate + */ + wdt_is_open = 1; + return 0; + } + return -ENODEV; +} + +/** + * wdt_close: + * @inode: inode to board + * @file: file handle to board + * + */ +static int wdt_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) + clear_bit(0, &wdt_is_open); + return 0; +} + +/** + * notify_sys: + * @this: our notifier block + * @code: the event being reported + * @unused: unused + * + * Our notifier is called on system shutdowns. We want to turn the card + * off at reboot otherwise the machine will reboot again during memory + * test or worse yet during the following fsck. This would suck, in fact + * trust me - if it happens it does suck. + */ +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + /* Disable Watchdog */ + wdt_disable(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .read = wdt_read, + .ioctl = wdt_ioctl, + .write = wdt_write, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_dev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * The WDT card needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; +#endif /* CONFIG_RTC_DRV_M41T80_WDT */ + +/* + ***************************************************************************** + * + * Driver Interface + * + ***************************************************************************** + */ +static int m41t80_probe(struct i2c_client *client) +{ + int i, rc = 0; + struct rtc_device *rtc = NULL; + struct rtc_time tm; + const struct m41t80_chip_info *chip; + struct m41t80_data *clientdata = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C + | I2C_FUNC_SMBUS_BYTE_DATA)) { + rc = -ENODEV; + goto exit; + } + + dev_info(&client->dev, + "chip found, driver version " DRV_VERSION "\n"); + + chip = NULL; + for (i = 0; i < ARRAY_SIZE(m41t80_chip_info_tbl); i++) { + if (!strcmp(m41t80_chip_info_tbl[i].name, client->name)) { + chip = &m41t80_chip_info_tbl[i]; + break; + } + } + if (!chip) { + dev_err(&client->dev, "%s is not supported\n", client->name); + rc = -ENODEV; + goto exit; + } + + clientdata = kzalloc(sizeof(*clientdata), GFP_KERNEL); + if (!clientdata) { + rc = -ENOMEM; + goto exit; + } + + rtc = rtc_device_register(client->name, &client->dev, + &m41t80_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + rc = PTR_ERR(rtc); + rtc = NULL; + goto exit; + } + + clientdata->rtc = rtc; + clientdata->chip = chip; + i2c_set_clientdata(client, clientdata); + + /* Make sure HT (Halt Update) bit is cleared */ + rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); + if (rc < 0) + goto ht_err; + + if (rc & M41T80_ALHOUR_HT) { + if (chip->features & M41T80_FEATURE_HT) { + m41t80_get_datetime(client, &tm); + dev_info(&client->dev, "HT bit was set!\n"); + dev_info(&client->dev, + "Power Down at " + "%04i-%02i-%02i %02i:%02i:%02i\n", + tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec); + } + if (i2c_smbus_write_byte_data(client, + M41T80_REG_ALARM_HOUR, + rc & ~M41T80_ALHOUR_HT) < 0) + goto ht_err; + } + + /* Make sure ST (stop) bit is cleared */ + rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC); + if (rc < 0) + goto st_err; + + if (rc & M41T80_SEC_ST) { + if (i2c_smbus_write_byte_data(client, M41T80_REG_SEC, + rc & ~M41T80_SEC_ST) < 0) + goto st_err; + } + + rc = m41t80_sysfs_register(&client->dev); + if (rc) + goto exit; + +#ifdef CONFIG_RTC_DRV_M41T80_WDT + if (chip->features & M41T80_FEATURE_HT) { + rc = misc_register(&wdt_dev); + if (rc) + goto exit; + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + misc_deregister(&wdt_dev); + goto exit; + } + save_client = client; + } +#endif + return 0; + +st_err: + rc = -EIO; + dev_err(&client->dev, "Can't clear ST bit\n"); + goto exit; +ht_err: + rc = -EIO; + dev_err(&client->dev, "Can't clear HT bit\n"); + goto exit; + +exit: + if (rtc) + rtc_device_unregister(rtc); + kfree(clientdata); + return rc; +} + +static int m41t80_remove(struct i2c_client *client) +{ + struct m41t80_data *clientdata = i2c_get_clientdata(client); + struct rtc_device *rtc = clientdata->rtc; + +#ifdef CONFIG_RTC_DRV_M41T80_WDT + if (clientdata->chip->features & M41T80_FEATURE_HT) { + misc_deregister(&wdt_dev); + unregister_reboot_notifier(&wdt_notifier); + } +#endif + if (rtc) + rtc_device_unregister(rtc); + kfree(clientdata); + + return 0; +} + +static struct i2c_driver m41t80_driver = { + .driver = { + .name = "m41t80", + }, + .probe = m41t80_probe, + .remove = m41t80_remove, +}; + +static int __init m41t80_rtc_init(void) +{ + return i2c_add_driver(&m41t80_driver); +} + +static void __exit m41t80_rtc_exit(void) +{ + i2c_del_driver(&m41t80_driver); +} + +MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>"); +MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(m41t80_rtc_init); +module_exit(m41t80_rtc_exit); diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c new file mode 100644 index 00000000000..33b752350ab --- /dev/null +++ b/drivers/rtc/rtc-m48t59.c @@ -0,0 +1,491 @@ +/* + * ST M48T59 RTC driver + * + * Copyright (c) 2007 Wind River Systems, Inc. + * + * Author: Mark Zhan <rongkai.zhan@windriver.com> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/rtc/m48t59.h> +#include <linux/bcd.h> + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#define M48T59_READ(reg) pdata->read_byte(dev, reg) +#define M48T59_WRITE(val, reg) pdata->write_byte(dev, reg, val) + +#define M48T59_SET_BITS(mask, reg) \ + M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) +#define M48T59_CLEAR_BITS(mask, reg) \ + M48T59_WRITE((M48T59_READ(reg) & ~(mask)), (reg)) + +struct m48t59_private { + void __iomem *ioaddr; + unsigned int size; /* iomem size */ + unsigned int irq; + struct rtc_device *rtc; + spinlock_t lock; /* serialize the NVRAM and RTC access */ +}; + +/* + * This is the generic access method when the chip is memory-mapped + */ +static void +m48t59_mem_writeb(struct device *dev, u32 ofs, u8 val) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + + writeb(val, m48t59->ioaddr+ofs); +} + +static u8 +m48t59_mem_readb(struct device *dev, u32 ofs) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + + return readb(m48t59->ioaddr+ofs); +} + +/* + * NOTE: M48T59 only uses BCD mode + */ +static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the READ command */ + M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); + + tm->tm_year = BCD2BIN(M48T59_READ(M48T59_YEAR)); + /* tm_mon is 0-11 */ + tm->tm_mon = BCD2BIN(M48T59_READ(M48T59_MONTH)) - 1; + tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_MDAY)); + + val = M48T59_READ(M48T59_WDAY); + if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { + dev_dbg(dev, "Century bit is enabled\n"); + tm->tm_year += 100; /* one century */ + } + + tm->tm_wday = BCD2BIN(val & 0x07); + tm->tm_hour = BCD2BIN(M48T59_READ(M48T59_HOUR) & 0x3F); + tm->tm_min = BCD2BIN(M48T59_READ(M48T59_MIN) & 0x7F); + tm->tm_sec = BCD2BIN(M48T59_READ(M48T59_SEC) & 0x7F); + + /* Clear the READ bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} + +static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + unsigned long flags; + u8 val = 0; + + dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the WRITE command */ + M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + + M48T59_WRITE((BIN2BCD(tm->tm_sec) & 0x7F), M48T59_SEC); + M48T59_WRITE((BIN2BCD(tm->tm_min) & 0x7F), M48T59_MIN); + M48T59_WRITE((BIN2BCD(tm->tm_hour) & 0x3F), M48T59_HOUR); + M48T59_WRITE((BIN2BCD(tm->tm_mday) & 0x3F), M48T59_MDAY); + /* tm_mon is 0-11 */ + M48T59_WRITE((BIN2BCD(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); + M48T59_WRITE(BIN2BCD(tm->tm_year % 100), M48T59_YEAR); + + if (tm->tm_year/100) + val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); + val |= (BIN2BCD(tm->tm_wday) & 0x07); + M48T59_WRITE(val, M48T59_WDAY); + + /* Clear the WRITE bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + return 0; +} + +/* + * Read alarm time and date in RTC + */ +static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + unsigned long flags; + u8 val; + + /* If no irq, we don't support ALARM */ + if (m48t59->irq == NO_IRQ) + return -EIO; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the READ command */ + M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL); + + tm->tm_year = BCD2BIN(M48T59_READ(M48T59_YEAR)); + /* tm_mon is 0-11 */ + tm->tm_mon = BCD2BIN(M48T59_READ(M48T59_MONTH)) - 1; + + val = M48T59_READ(M48T59_WDAY); + if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) + tm->tm_year += 100; /* one century */ + + tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_ALARM_DATE)); + tm->tm_hour = BCD2BIN(M48T59_READ(M48T59_ALARM_HOUR)); + tm->tm_min = BCD2BIN(M48T59_READ(M48T59_ALARM_MIN)); + tm->tm_sec = BCD2BIN(M48T59_READ(M48T59_ALARM_SEC)); + + /* Clear the READ bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} + +/* + * Set alarm time and date in RTC + */ +static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + struct rtc_time *tm = &alrm->time; + u8 mday, hour, min, sec; + unsigned long flags; + + /* If no irq, we don't support ALARM */ + if (m48t59->irq == NO_IRQ) + return -EIO; + + /* + * 0xff means "always match" + */ + mday = tm->tm_mday; + mday = (mday >= 1 && mday <= 31) ? BIN2BCD(mday) : 0xff; + if (mday == 0xff) + mday = M48T59_READ(M48T59_MDAY); + + hour = tm->tm_hour; + hour = (hour < 24) ? BIN2BCD(hour) : 0x00; + + min = tm->tm_min; + min = (min < 60) ? BIN2BCD(min) : 0x00; + + sec = tm->tm_sec; + sec = (sec < 60) ? BIN2BCD(sec) : 0x00; + + spin_lock_irqsave(&m48t59->lock, flags); + /* Issue the WRITE command */ + M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + + M48T59_WRITE(mday, M48T59_ALARM_DATE); + M48T59_WRITE(hour, M48T59_ALARM_HOUR); + M48T59_WRITE(min, M48T59_ALARM_MIN); + M48T59_WRITE(sec, M48T59_ALARM_SEC); + + /* Clear the WRITE bit */ + M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL); + spin_unlock_irqrestore(&m48t59->lock, flags); + + dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + return 0; +} + +/* + * Handle commands from user-space + */ +static int m48t59_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&m48t59->lock, flags); + switch (cmd) { + case RTC_AIE_OFF: /* alarm interrupt off */ + M48T59_WRITE(0x00, M48T59_INTR); + break; + case RTC_AIE_ON: /* alarm interrupt on */ + M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + spin_unlock_irqrestore(&m48t59->lock, flags); + + return ret; +} + +static int m48t59_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + unsigned long flags; + u8 val; + + spin_lock_irqsave(&m48t59->lock, flags); + val = M48T59_READ(M48T59_FLAGS); + spin_unlock_irqrestore(&m48t59->lock, flags); + + seq_printf(seq, "battery\t\t: %s\n", + (val & M48T59_FLAGS_BF) ? "low" : "normal"); + return 0; +} + +/* + * IRQ handler for the RTC + */ +static irqreturn_t m48t59_rtc_interrupt(int irq, void *dev_id) +{ + struct device *dev = (struct device *)dev_id; + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + u8 event; + + spin_lock(&m48t59->lock); + event = M48T59_READ(M48T59_FLAGS); + spin_unlock(&m48t59->lock); + + if (event & M48T59_FLAGS_AF) { + rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF)); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static const struct rtc_class_ops m48t59_rtc_ops = { + .ioctl = m48t59_rtc_ioctl, + .read_time = m48t59_rtc_read_time, + .set_time = m48t59_rtc_set_time, + .read_alarm = m48t59_rtc_readalarm, + .set_alarm = m48t59_rtc_setalarm, + .proc = m48t59_rtc_proc, +}; + +static ssize_t m48t59_nvram_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + ssize_t cnt = 0; + unsigned long flags; + + for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + spin_lock_irqsave(&m48t59->lock, flags); + *buf++ = M48T59_READ(cnt); + spin_unlock_irqrestore(&m48t59->lock, flags); + } + + return cnt; +} + +static ssize_t m48t59_nvram_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + ssize_t cnt = 0; + unsigned long flags; + + for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + spin_lock_irqsave(&m48t59->lock, flags); + M48T59_WRITE(*buf++, cnt); + spin_unlock_irqrestore(&m48t59->lock, flags); + } + + return cnt; +} + +static struct bin_attribute m48t59_nvram_attr = { + .attr = { + .name = "nvram", + .mode = S_IRUGO | S_IWUGO, + .owner = THIS_MODULE, + }, + .read = m48t59_nvram_read, + .write = m48t59_nvram_write, +}; + +static int __devinit m48t59_rtc_probe(struct platform_device *pdev) +{ + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + struct m48t59_private *m48t59 = NULL; + struct resource *res; + int ret = -ENOMEM; + + /* This chip could be memory-mapped or I/O-mapped */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -EINVAL; + } + + if (res->flags & IORESOURCE_IO) { + /* If we are I/O-mapped, the platform should provide + * the operations accessing chip registers. + */ + if (!pdata || !pdata->write_byte || !pdata->read_byte) + return -EINVAL; + } else if (res->flags & IORESOURCE_MEM) { + /* we are memory-mapped */ + if (!pdata) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + /* Ensure we only kmalloc platform data once */ + pdev->dev.platform_data = pdata; + } + + /* Try to use the generic memory read/write ops */ + if (!pdata->write_byte) + pdata->write_byte = m48t59_mem_writeb; + if (!pdata->read_byte) + pdata->read_byte = m48t59_mem_readb; + } + + m48t59 = kzalloc(sizeof(*m48t59), GFP_KERNEL); + if (!m48t59) + return -ENOMEM; + + m48t59->size = res->end - res->start + 1; + m48t59->ioaddr = ioremap(res->start, m48t59->size); + if (!m48t59->ioaddr) + goto out; + + /* Try to get irq number. We also can work in + * the mode without IRQ. + */ + m48t59->irq = platform_get_irq(pdev, 0); + if (m48t59->irq < 0) + m48t59->irq = NO_IRQ; + + if (m48t59->irq != NO_IRQ) { + ret = request_irq(m48t59->irq, m48t59_rtc_interrupt, + IRQF_SHARED, "rtc-m48t59", &pdev->dev); + if (ret) + goto out; + } + + m48t59->rtc = rtc_device_register("m48t59", &pdev->dev, + &m48t59_rtc_ops, THIS_MODULE); + if (IS_ERR(m48t59->rtc)) { + ret = PTR_ERR(m48t59->rtc); + goto out; + } + + ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); + if (ret) + goto out; + + spin_lock_init(&m48t59->lock); + platform_set_drvdata(pdev, m48t59); + return 0; + +out: + if (!IS_ERR(m48t59->rtc)) + rtc_device_unregister(m48t59->rtc); + if (m48t59->irq != NO_IRQ) + free_irq(m48t59->irq, &pdev->dev); + if (m48t59->ioaddr) + iounmap(m48t59->ioaddr); + if (m48t59) + kfree(m48t59); + return ret; +} + +static int __devexit m48t59_rtc_remove(struct platform_device *pdev) +{ + struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + + sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); + if (!IS_ERR(m48t59->rtc)) + rtc_device_unregister(m48t59->rtc); + if (m48t59->ioaddr) + iounmap(m48t59->ioaddr); + if (m48t59->irq != NO_IRQ) + free_irq(m48t59->irq, &pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(m48t59); + return 0; +} + +static struct platform_driver m48t59_rtc_platdrv = { + .driver = { + .name = "rtc-m48t59", + .owner = THIS_MODULE, + }, + .probe = m48t59_rtc_probe, + .remove = __devexit_p(m48t59_rtc_remove), +}; + +static int __init m48t59_rtc_init(void) +{ + return platform_driver_register(&m48t59_rtc_platdrv); +} + +static void __exit m48t59_rtc_exit(void) +{ + platform_driver_unregister(&m48t59_rtc_platdrv); +} + +module_init(m48t59_rtc_init); +module_exit(m48t59_rtc_exit); + +MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>"); +MODULE_DESCRIPTION("M48T59 RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 09bbe575647..6b67b509792 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -13,13 +13,7 @@ #include <linux/rtc.h> #include <linux/bcd.h> -#define DRV_VERSION "0.4" - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD; +#define DRV_VERSION "0.5" /* @@ -88,9 +82,6 @@ struct rs5c372 { unsigned has_irq:1; char buf[17]; char *regs; - - /* on conversion to a "new style" i2c driver, this vanishes */ - struct i2c_client dev; }; static int rs5c_get_regs(struct rs5c372 *rs5c) @@ -483,25 +474,35 @@ static int rs5c_sysfs_register(struct device *dev) return err; } +static void rs5c_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_trim); + device_remove_file(dev, &dev_attr_osc); +} + #else static int rs5c_sysfs_register(struct device *dev) { return 0; } + +static void rs5c_sysfs_unregister(struct device *dev) +{ + /* nothing */ +} #endif /* SYSFS */ static struct i2c_driver rs5c372_driver; -static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) +static int rs5c372_probe(struct i2c_client *client) { int err = 0; - struct i2c_client *client; struct rs5c372 *rs5c372; struct rtc_time tm; - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + dev_dbg(&client->dev, "%s\n", __FUNCTION__); - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { err = -ENODEV; goto exit; } @@ -514,35 +515,22 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) /* we read registers 0x0f then 0x00-0x0f; skip the first one */ rs5c372->regs=&rs5c372->buf[1]; - /* On conversion to a "new style" i2c driver, we'll be handed - * the i2c_client (we won't create it) - */ - client = &rs5c372->dev; rs5c372->client = client; - - /* I2C client */ - client->addr = address; - client->driver = &rs5c372_driver; - client->adapter = adapter; - - strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE); - i2c_set_clientdata(client, rs5c372); - /* Inform the i2c layer */ - if ((err = i2c_attach_client(client))) - goto exit_kfree; - err = rs5c_get_regs(rs5c372); if (err < 0) - goto exit_detach; + goto exit_kfree; - /* For "new style" drivers, irq is in i2c_client and chip type - * info comes from i2c_client.dev.platform_data. Meanwhile: - * - * STICK BOARD-SPECIFIC SETUP CODE RIGHT HERE - */ - if (rs5c372->type == rtc_undef) { + if (strcmp(client->name, "rs5c372a") == 0) + rs5c372->type = rtc_rs5c372a; + else if (strcmp(client->name, "rs5c372b") == 0) + rs5c372->type = rtc_rs5c372b; + else if (strcmp(client->name, "rv5c386") == 0) + rs5c372->type = rtc_rv5c386; + else if (strcmp(client->name, "rv5c387a") == 0) + rs5c372->type = rtc_rv5c387a; + else { rs5c372->type = rtc_rs5c372b; dev_warn(&client->dev, "assuming rs5c372b\n"); } @@ -567,7 +555,7 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) break; default: dev_err(&client->dev, "unknown RTC type\n"); - goto exit_detach; + goto exit_kfree; } /* if the oscillator lost power and no other software (like @@ -601,7 +589,7 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) if ((i2c_master_send(client, buf, 3)) != 3) { dev_err(&client->dev, "setup error\n"); - goto exit_detach; + goto exit_kfree; } rs5c372->regs[RS5C_REG_CTRL1] = buf[1]; rs5c372->regs[RS5C_REG_CTRL2] = buf[2]; @@ -621,14 +609,14 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) rs5c372->time24 ? "24hr" : "am/pm" ); - /* FIXME when client->irq exists, use it to register alarm irq */ + /* REVISIT use client->irq to register alarm irq ... */ rs5c372->rtc = rtc_device_register(rs5c372_driver.driver.name, &client->dev, &rs5c372_rtc_ops, THIS_MODULE); if (IS_ERR(rs5c372->rtc)) { err = PTR_ERR(rs5c372->rtc); - goto exit_detach; + goto exit_kfree; } err = rs5c_sysfs_register(&client->dev); @@ -640,9 +628,6 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) exit_devreg: rtc_device_unregister(rs5c372->rtc); -exit_detach: - i2c_detach_client(client); - exit_kfree: kfree(rs5c372); @@ -650,24 +635,12 @@ exit: return err; } -static int rs5c372_attach(struct i2c_adapter *adapter) +static int rs5c372_remove(struct i2c_client *client) { - return i2c_probe(adapter, &addr_data, rs5c372_probe); -} - -static int rs5c372_detach(struct i2c_client *client) -{ - int err; struct rs5c372 *rs5c372 = i2c_get_clientdata(client); - if (rs5c372->rtc) - rtc_device_unregister(rs5c372->rtc); - - /* REVISIT properly destroy the sysfs files ... */ - - if ((err = i2c_detach_client(client))) - return err; - + rtc_device_unregister(rs5c372->rtc); + rs5c_sysfs_unregister(&client->dev); kfree(rs5c372); return 0; } @@ -676,8 +649,8 @@ static struct i2c_driver rs5c372_driver = { .driver = { .name = "rtc-rs5c372", }, - .attach_adapter = &rs5c372_attach, - .detach_client = &rs5c372_detach, + .probe = rs5c372_probe, + .remove = rs5c372_remove, }; static __init int rs5c372_init(void) diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index af7596ef29e..ce2f78de7a8 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/err.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/ioport.h> -#include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/rtc.h> @@ -30,25 +31,11 @@ #include <asm/div64.h> #include <asm/io.h> #include <asm/uaccess.h> -#include <asm/vr41xx/irq.h> MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>"); MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); MODULE_LICENSE("GPL"); -#define RTC1_TYPE1_START 0x0b0000c0UL -#define RTC1_TYPE1_END 0x0b0000dfUL -#define RTC2_TYPE1_START 0x0b0001c0UL -#define RTC2_TYPE1_END 0x0b0001dfUL - -#define RTC1_TYPE2_START 0x0f000100UL -#define RTC1_TYPE2_END 0x0f00011fUL -#define RTC2_TYPE2_START 0x0f000120UL -#define RTC2_TYPE2_END 0x0f00013fUL - -#define RTC1_SIZE 0x20 -#define RTC2_SIZE 0x20 - /* RTC 1 registers */ #define ETIMELREG 0x00 #define ETIMEMREG 0x02 @@ -98,13 +85,8 @@ static char rtc_name[] = "RTC"; static unsigned long periodic_frequency; static unsigned long periodic_count; static unsigned int alarm_enabled; - -struct resource rtc_resource[2] = { - { .name = rtc_name, - .flags = IORESOURCE_MEM, }, - { .name = rtc_name, - .flags = IORESOURCE_MEM, }, -}; +static int aie_irq = -1; +static int pie_irq = -1; static inline unsigned long read_elapsed_second(void) { @@ -150,8 +132,8 @@ static void vr41xx_rtc_release(struct device *dev) spin_unlock_irq(&rtc_lock); - disable_irq(ELAPSEDTIME_IRQ); - disable_irq(RTCLONG1_IRQ); + disable_irq(aie_irq); + disable_irq(pie_irq); } static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) @@ -209,14 +191,14 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) spin_lock_irq(&rtc_lock); if (alarm_enabled) - disable_irq(ELAPSEDTIME_IRQ); + disable_irq(aie_irq); rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15)); rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1)); rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); if (wkalrm->enabled) - enable_irq(ELAPSEDTIME_IRQ); + enable_irq(aie_irq); alarm_enabled = wkalrm->enabled; @@ -234,7 +216,7 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long spin_lock_irq(&rtc_lock); if (!alarm_enabled) { - enable_irq(ELAPSEDTIME_IRQ); + enable_irq(aie_irq); alarm_enabled = 1; } @@ -244,17 +226,17 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long spin_lock_irq(&rtc_lock); if (alarm_enabled) { - disable_irq(ELAPSEDTIME_IRQ); + disable_irq(aie_irq); alarm_enabled = 0; } spin_unlock_irq(&rtc_lock); break; case RTC_PIE_ON: - enable_irq(RTCLONG1_IRQ); + enable_irq(pie_irq); break; case RTC_PIE_OFF: - disable_irq(RTCLONG1_IRQ); + disable_irq(pie_irq); break; case RTC_IRQP_READ: return put_user(periodic_frequency, (unsigned long __user *)arg); @@ -331,31 +313,37 @@ static const struct rtc_class_ops vr41xx_rtc_ops = { static int __devinit rtc_probe(struct platform_device *pdev) { + struct resource *res; struct rtc_device *rtc; - unsigned int irq; int retval; - if (pdev->num_resources != 2) + if (pdev->num_resources != 4) return -EBUSY; - rtc1_base = ioremap(pdev->resource[0].start, RTC1_SIZE); - if (rtc1_base == NULL) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) return -EBUSY; - rtc2_base = ioremap(pdev->resource[1].start, RTC2_SIZE); - if (rtc2_base == NULL) { - iounmap(rtc1_base); - rtc1_base = NULL; + rtc1_base = ioremap(res->start, res->end - res->start + 1); + if (!rtc1_base) return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + retval = -EBUSY; + goto err_rtc1_iounmap; + } + + rtc2_base = ioremap(res->start, res->end - res->start + 1); + if (!rtc2_base) { + retval = -EBUSY; + goto err_rtc1_iounmap; } rtc = rtc_device_register(rtc_name, &pdev->dev, &vr41xx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { - iounmap(rtc1_base); - iounmap(rtc2_base); - rtc1_base = NULL; - rtc2_base = NULL; - return PTR_ERR(rtc); + retval = PTR_ERR(rtc); + goto err_iounmap_all; } spin_lock_irq(&rtc_lock); @@ -368,35 +356,50 @@ static int __devinit rtc_probe(struct platform_device *pdev) spin_unlock_irq(&rtc_lock); - irq = ELAPSEDTIME_IRQ; - retval = request_irq(irq, elapsedtime_interrupt, IRQF_DISABLED, - "elapsed_time", pdev); - if (retval == 0) { - irq = RTCLONG1_IRQ; - retval = request_irq(irq, rtclong1_interrupt, IRQF_DISABLED, - "rtclong1", pdev); + aie_irq = platform_get_irq(pdev, 0); + if (aie_irq < 0 || aie_irq >= NR_IRQS) { + retval = -EBUSY; + goto err_device_unregister; } - if (retval < 0) { - printk(KERN_ERR "rtc: IRQ%d is busy\n", irq); - rtc_device_unregister(rtc); - if (irq == RTCLONG1_IRQ) - free_irq(ELAPSEDTIME_IRQ, NULL); - iounmap(rtc1_base); - iounmap(rtc2_base); - rtc1_base = NULL; - rtc2_base = NULL; - return retval; - } + retval = request_irq(aie_irq, elapsedtime_interrupt, IRQF_DISABLED, + "elapsed_time", pdev); + if (retval < 0) + goto err_device_unregister; + + pie_irq = platform_get_irq(pdev, 1); + if (pie_irq < 0 || pie_irq >= NR_IRQS) + goto err_free_irq; + + retval = request_irq(pie_irq, rtclong1_interrupt, IRQF_DISABLED, + "rtclong1", pdev); + if (retval < 0) + goto err_free_irq; platform_set_drvdata(pdev, rtc); - disable_irq(ELAPSEDTIME_IRQ); - disable_irq(RTCLONG1_IRQ); + disable_irq(aie_irq); + disable_irq(pie_irq); printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n"); return 0; + +err_free_irq: + free_irq(aie_irq, pdev); + +err_device_unregister: + rtc_device_unregister(rtc); + +err_iounmap_all: + iounmap(rtc2_base); + rtc2_base = NULL; + +err_rtc1_iounmap: + iounmap(rtc1_base); + rtc1_base = NULL; + + return retval; } static int __devexit rtc_remove(struct platform_device *pdev) @@ -404,23 +407,21 @@ static int __devexit rtc_remove(struct platform_device *pdev) struct rtc_device *rtc; rtc = platform_get_drvdata(pdev); - if (rtc != NULL) + if (rtc) rtc_device_unregister(rtc); platform_set_drvdata(pdev, NULL); - free_irq(ELAPSEDTIME_IRQ, NULL); - free_irq(RTCLONG1_IRQ, NULL); - if (rtc1_base != NULL) + free_irq(aie_irq, pdev); + free_irq(pie_irq, pdev); + if (rtc1_base) iounmap(rtc1_base); - if (rtc2_base != NULL) + if (rtc2_base) iounmap(rtc2_base); return 0; } -static struct platform_device *rtc_platform_device; - static struct platform_driver rtc_platform_driver = { .probe = rtc_probe, .remove = __devexit_p(rtc_remove), @@ -432,55 +433,12 @@ static struct platform_driver rtc_platform_driver = { static int __init vr41xx_rtc_init(void) { - int retval; - - switch (current_cpu_data.cputype) { - case CPU_VR4111: - case CPU_VR4121: - rtc_resource[0].start = RTC1_TYPE1_START; - rtc_resource[0].end = RTC1_TYPE1_END; - rtc_resource[1].start = RTC2_TYPE1_START; - rtc_resource[1].end = RTC2_TYPE1_END; - break; - case CPU_VR4122: - case CPU_VR4131: - case CPU_VR4133: - rtc_resource[0].start = RTC1_TYPE2_START; - rtc_resource[0].end = RTC1_TYPE2_END; - rtc_resource[1].start = RTC2_TYPE2_START; - rtc_resource[1].end = RTC2_TYPE2_END; - break; - default: - return -ENODEV; - break; - } - - rtc_platform_device = platform_device_alloc("RTC", -1); - if (rtc_platform_device == NULL) - return -ENOMEM; - - retval = platform_device_add_resources(rtc_platform_device, - rtc_resource, ARRAY_SIZE(rtc_resource)); - - if (retval == 0) - retval = platform_device_add(rtc_platform_device); - - if (retval < 0) { - platform_device_put(rtc_platform_device); - return retval; - } - - retval = platform_driver_register(&rtc_platform_driver); - if (retval < 0) - platform_device_unregister(rtc_platform_device); - - return retval; + return platform_driver_register(&rtc_platform_driver); } static void __exit vr41xx_rtc_exit(void) { platform_driver_unregister(&rtc_platform_driver); - platform_device_unregister(rtc_platform_device); } module_init(vr41xx_rtc_init); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 513d1a611aa..b3fae357ca4 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -9,6 +9,9 @@ * * based on a lot of other RTC drivers. * + * Information and datasheet: + * http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html + * * 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. @@ -26,7 +29,7 @@ * Two bytes need to be written to read a single register, * while most other chips just require one and take the second * one as the data to be written. To prevent corrupting - * unknown chips, the user must explicitely set the probe parameter. + * unknown chips, the user must explicitly set the probe parameter. */ static unsigned short normal_i2c[] = { I2C_CLIENT_END }; |