diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 71 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-bq4802.c | 230 | ||||
-rw-r--r-- | drivers/rtc/rtc-cmos.c | 127 | ||||
-rw-r--r-- | drivers/rtc/rtc-dev.c | 12 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1286.c | 410 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 308 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1374.c | 21 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1511.c | 13 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1553.c | 12 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1672.c | 114 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds3234.c | 290 | ||||
-rw-r--r-- | drivers/rtc/rtc-m41t80.c | 43 | ||||
-rw-r--r-- | drivers/rtc/rtc-m48t35.c | 235 | ||||
-rw-r--r-- | drivers/rtc/rtc-m48t59.c | 68 | ||||
-rw-r--r-- | drivers/rtc/rtc-max6900.c | 223 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 58 | ||||
-rw-r--r-- | drivers/rtc/rtc-pl030.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-pl031.c | 14 | ||||
-rw-r--r-- | drivers/rtc/rtc-rs5c372.c | 228 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 7 | ||||
-rw-r--r-- | drivers/rtc/rtc-starfire.c | 120 | ||||
-rw-r--r-- | drivers/rtc/rtc-stk17ta8.c | 12 | ||||
-rw-r--r-- | drivers/rtc/rtc-sun4v.c | 153 |
25 files changed, 2276 insertions, 514 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9a9755c..f660ef3 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -220,22 +220,22 @@ config RTC_DRV_PCF8583 will be called rtc-pcf8583. config RTC_DRV_M41T80 - tristate "ST M41T80/81/82/83/84/85/87" + tristate "ST M41T65/M41T80/81/82/83/84/85/87" 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. + If you say Y here you will get support for the ST M41T60 + and M41T80 RTC chips series. Currently, the following chips are + supported: M41T65, 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" + bool "ST M41T65/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. + watchdog timer in the ST M41T60 and M41T80 RTC chips series. config RTC_DRV_TWL92330 boolean "TI TWL92330/Menelaus" @@ -319,6 +319,15 @@ config RTC_DRV_RS5C348 This driver can also be built as a module. If so, the module will be called rtc-rs5c348. +config RTC_DRV_DS3234 + tristate "Maxim/Dallas DS3234" + help + If you say yes here you get support for the + Maxim/Dallas DS3234 SPI RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds3234. + endif # SPI_MASTER comment "Platform RTC drivers" @@ -329,7 +338,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" - depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS + depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64 default y if X86 help Say "yes" here to get direct support for the real time clock @@ -352,6 +361,11 @@ config RTC_DRV_DS1216 help If you say yes here you get support for the Dallas DS1216 RTC chips. +config RTC_DRV_DS1286 + tristate "Dallas DS1286" + help + If you say yes here you get support for the Dallas DS1286 RTC chips. + config RTC_DRV_DS1302 tristate "Dallas DS1302" depends on SH_SECUREEDGE5410 @@ -405,15 +419,36 @@ 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_M48T35 + tristate "ST M48T35" + help + If you say Y here you will get support for the + ST M48T35 RTC chip. + + This driver can also be built as a module, if so, the module + will be called "rtc-m48t35". + config RTC_DRV_M48T59 - tristate "ST M48T59" + tristate "ST M48T59/M48T08/M48T02" help If you say Y here you will get support for the - ST M48T59 RTC chip. + ST M48T59 RTC chip and compatible ST M48T08 and M48T02. + + These chips are usually found in Sun SPARC and UltraSPARC + workstations. This driver can also be built as a module, if so, the module will be called "rtc-m48t59". +config RTC_DRV_BQ4802 + tristate "TI BQ4802" + help + If you say Y here you will get support for the TI + BQ4802 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq4802. + config RTC_DRV_V3020 tristate "EM Microelectronic V3020" help @@ -577,10 +612,24 @@ config RTC_DRV_RS5C313 config RTC_DRV_PPC tristate "PowerPC machine dependent RTC support" - depends on PPC_MERGE + depends on PPC help The PowerPC kernel has machine-specific functions for accessing the RTC. This exposes that functionality through the generic RTC class. +config RTC_DRV_SUN4V + bool "SUN4V Hypervisor RTC" + depends on SPARC64 + help + If you say Y here you will get support for the Hypervisor + based RTC on SUN4V systems. + +config RTC_DRV_STARFIRE + bool "Starfire RTC" + depends on SPARC64 + help + If you say Y here you will get support for the RTC found on + Starfire systems. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 18622ef..d05928b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o +obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o @@ -31,13 +32,18 @@ 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_DS1742) += rtc-ds1742.o +obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o +obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o +obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o +obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index 4e888cc..3708261 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -29,10 +29,10 @@ #include <linux/completion.h> #include <asm/uaccess.h> + #include <mach/at91_rtc.h> -#define AT91_RTC_FREQ 1 #define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */ static DECLARE_COMPLETION(at91_rtc_updated); @@ -228,8 +228,6 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq) (imr & AT91_RTC_ACKUPD) ? "yes" : "no"); seq_printf(seq, "periodic_IRQ\t: %s\n", (imr & AT91_RTC_SECEV) ? "yes" : "no"); - seq_printf(seq, "periodic_freq\t: %ld\n", - (unsigned long) AT91_RTC_FREQ); return 0; } diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c new file mode 100644 index 0000000..189a018 --- /dev/null +++ b/drivers/rtc/rtc-bq4802.c @@ -0,0 +1,230 @@ +/* rtc-bq4802.c: TI BQ4802 RTC driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("TI BQ4802 RTC driver"); +MODULE_LICENSE("GPL"); + +struct bq4802 { + void __iomem *regs; + unsigned long ioport; + struct rtc_device *rtc; + spinlock_t lock; + struct resource *r; + u8 (*read)(struct bq4802 *, int); + void (*write)(struct bq4802 *, int, u8); +}; + +static u8 bq4802_read_io(struct bq4802 *p, int off) +{ + return inb(p->ioport + off); +} + +static void bq4802_write_io(struct bq4802 *p, int off, u8 val) +{ + outb(val, p->ioport + off); +} + +static u8 bq4802_read_mem(struct bq4802 *p, int off) +{ + return readb(p->regs + off); +} + +static void bq4802_write_mem(struct bq4802 *p, int off, u8 val) +{ + writeb(val, p->regs + off); +} + +static int bq4802_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bq4802 *p = platform_get_drvdata(pdev); + unsigned long flags; + unsigned int century; + u8 val; + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0xe, val | 0x08); + + tm->tm_sec = p->read(p, 0x00); + tm->tm_min = p->read(p, 0x02); + tm->tm_hour = p->read(p, 0x04); + tm->tm_mday = p->read(p, 0x06); + tm->tm_mon = p->read(p, 0x09); + tm->tm_year = p->read(p, 0x0a); + tm->tm_wday = p->read(p, 0x08); + century = p->read(p, 0x0f); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + BCD_TO_BIN(tm->tm_year); + BCD_TO_BIN(tm->tm_wday); + BCD_TO_BIN(century); + + tm->tm_year += (century * 100); + tm->tm_year -= 1900; + + tm->tm_mon--; + + return 0; +} + +static int bq4802_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bq4802 *p = platform_get_drvdata(pdev); + u8 sec, min, hrs, day, mon, yrs, century, val; + unsigned long flags; + unsigned int year; + + year = tm->tm_year + 1900; + century = year / 100; + yrs = year % 100; + + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(yrs); + BIN_TO_BCD(century); + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0x0e, val | 0x08); + + p->write(p, 0x00, sec); + p->write(p, 0x02, min); + p->write(p, 0x04, hrs); + p->write(p, 0x06, day); + p->write(p, 0x09, mon); + p->write(p, 0x0a, yrs); + p->write(p, 0x0f, century); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + return 0; +} + +static const struct rtc_class_ops bq4802_ops = { + .read_time = bq4802_read_time, + .set_time = bq4802_set_time, +}; + +static int __devinit bq4802_probe(struct platform_device *pdev) +{ + struct bq4802 *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + + if (!p) + goto out; + + spin_lock_init(&p->lock); + + p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!p->r) { + p->r = platform_get_resource(pdev, IORESOURCE_IO, 0); + err = -EINVAL; + if (!p->r) + goto out_free; + } + if (p->r->flags & IORESOURCE_IO) { + p->ioport = p->r->start; + p->read = bq4802_read_io; + p->write = bq4802_write_io; + } else if (p->r->flags & IORESOURCE_MEM) { + p->regs = ioremap(p->r->start, resource_size(p->r)); + p->read = bq4802_read_mem; + p->write = bq4802_write_mem; + } else { + err = -EINVAL; + goto out_free; + } + + p->rtc = rtc_device_register("bq4802", &pdev->dev, + &bq4802_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + err = PTR_ERR(p->rtc); + goto out_iounmap; + } + + platform_set_drvdata(pdev, p); + err = 0; +out: + return err; + +out_iounmap: + if (p->r->flags & IORESOURCE_MEM) + iounmap(p->regs); +out_free: + kfree(p); + goto out; +} + +static int __devexit bq4802_remove(struct platform_device *pdev) +{ + struct bq4802 *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + if (p->r->flags & IORESOURCE_MEM) + iounmap(p->regs); + + platform_set_drvdata(pdev, NULL); + + kfree(p); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-bq4802"); + +static struct platform_driver bq4802_driver = { + .driver = { + .name = "rtc-bq4802", + .owner = THIS_MODULE, + }, + .probe = bq4802_probe, + .remove = __devexit_p(bq4802_remove), +}; + +static int __init bq4802_init(void) +{ + return platform_driver_register(&bq4802_driver); +} + +static void __exit bq4802_exit(void) +{ + platform_driver_unregister(&bq4802_driver); +} + +module_init(bq4802_init); +module_exit(bq4802_exit); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b184367..963ad0b 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -636,7 +636,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) */ #if defined(CONFIG_ATARI) address_space = 64; -#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__sparc__) address_space = 128; #else #warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes. @@ -699,7 +699,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) /* FIXME teach the alarm code how to handle binary mode; * <asm-generic/rtc.h> doesn't know 12-hour mode either. */ - if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) { + if (is_valid_irq(rtc_irq) && + (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) { dev_dbg(dev, "only 24-hr BCD mode supported\n"); retval = -ENXIO; goto cleanup1; @@ -912,6 +913,92 @@ static inline int cmos_poweroff(struct device *dev) * predate even PNPBIOS should set up platform_bus devices. */ +#ifdef CONFIG_ACPI + +#include <linux/acpi.h> + +#ifdef CONFIG_PM +static u32 rtc_handler(void *context) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); + return ACPI_INTERRUPT_HANDLED; +} + +static inline void rtc_wake_setup(void) +{ + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); + /* + * After the RTC handler is installed, the Fixed_RTC event should + * be disabled. Only when the RTC alarm is set will it be enabled. + */ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); +} + +static void rtc_wake_on(struct device *dev) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_enable_event(ACPI_EVENT_RTC, 0); +} + +static void rtc_wake_off(struct device *dev) +{ + acpi_disable_event(ACPI_EVENT_RTC, 0); +} +#else +#define rtc_wake_setup() do{}while(0) +#define rtc_wake_on NULL +#define rtc_wake_off NULL +#endif + +/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find + * its device node and pass extra config data. This helps its driver use + * capabilities that the now-obsolete mc146818 didn't have, and informs it + * that this board's RTC is wakeup-capable (per ACPI spec). + */ +static struct cmos_rtc_board_info acpi_rtc_info; + +static void __devinit +cmos_wake_setup(struct device *dev) +{ + if (acpi_disabled) + return; + + rtc_wake_setup(); + acpi_rtc_info.wake_on = rtc_wake_on; + acpi_rtc_info.wake_off = rtc_wake_off; + + /* workaround bug in some ACPI tables */ + if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) { + dev_dbg(dev, "bogus FADT month_alarm (%d)\n", + acpi_gbl_FADT.month_alarm); + acpi_gbl_FADT.month_alarm = 0; + } + + acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm; + acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm; + acpi_rtc_info.rtc_century = acpi_gbl_FADT.century; + + /* NOTE: S4_RTC_WAKE is NOT currently useful to Linux */ + if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE) + dev_info(dev, "RTC can wake from S4\n"); + + dev->platform_data = &acpi_rtc_info; + + /* RTC always wakes from S1/S2/S3, and often S4/STD */ + device_init_wakeup(dev, 1); +} + +#else + +static void __devinit +cmos_wake_setup(struct device *dev) +{ +} + +#endif + #ifdef CONFIG_PNP #include <linux/pnp.h> @@ -919,6 +1006,8 @@ static inline int cmos_poweroff(struct device *dev) static int __devinit cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) { + cmos_wake_setup(&pnp->dev); + if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0)) /* Some machines contain a PNP entry for the RTC, but * don't define the IRQ. It should always be safe to @@ -996,6 +1085,7 @@ static struct pnp_driver cmos_pnp_driver = { static int __init cmos_platform_probe(struct platform_device *pdev) { + cmos_wake_setup(&pdev->dev); return cmos_do_probe(&pdev->dev, platform_get_resource(pdev, IORESOURCE_IO, 0), platform_get_irq(pdev, 0)); @@ -1030,29 +1120,32 @@ static struct platform_driver cmos_platform_driver = { static int __init cmos_init(void) { + int retval = 0; + #ifdef CONFIG_PNP - if (pnp_platform_devices) - return pnp_register_driver(&cmos_pnp_driver); - else - return platform_driver_probe(&cmos_platform_driver, - cmos_platform_probe); -#else - return platform_driver_probe(&cmos_platform_driver, - cmos_platform_probe); -#endif /* CONFIG_PNP */ + pnp_register_driver(&cmos_pnp_driver); +#endif + + if (!cmos_rtc.dev) + retval = platform_driver_probe(&cmos_platform_driver, + cmos_platform_probe); + + if (retval == 0) + return 0; + +#ifdef CONFIG_PNP + pnp_unregister_driver(&cmos_pnp_driver); +#endif + return retval; } module_init(cmos_init); static void __exit cmos_exit(void) { #ifdef CONFIG_PNP - if (pnp_platform_devices) - pnp_unregister_driver(&cmos_pnp_driver); - else - platform_driver_unregister(&cmos_platform_driver); -#else + pnp_unregister_driver(&cmos_pnp_driver); +#endif platform_driver_unregister(&cmos_platform_driver); -#endif /* CONFIG_PNP */ } module_exit(cmos_exit); diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 52e2743..079e9ed 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -432,9 +432,15 @@ static int rtc_dev_release(struct inode *inode, struct file *file) { struct rtc_device *rtc = file->private_data; -#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL - clear_uie(rtc); -#endif + /* We shut down the repeating IRQs that userspace enabled, + * since nothing is listening to them. + * - Update (UIE) ... currently only managed through ioctls + * - Periodic (PIE) ... also used through rtc_*() interface calls + * + * Leave the alarm alone; it may be set to trigger a system wakeup + * later, or be used by kernel code, and is a one-shot event anyway. + */ + rtc_dev_ioctl(file, RTC_UIE_OFF, 0); rtc_irq_set_state(rtc, NULL, 0); if (rtc->ops->release) diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c new file mode 100644 index 0000000..4fcb16b --- /dev/null +++ b/drivers/rtc/rtc-ds1286.c @@ -0,0 +1,410 @@ +/* + * DS1286 Real Time Clock interface for Linux + * + * Copyright (C) 1998, 1999, 2000 Ralf Baechle + * Copyright (C) 2008 Thomas Bogendoerfer + * + * Based on code written by Paul Gortmaker. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/bcd.h> +#include <linux/ds1286.h> +#include <linux/io.h> + +#define DRV_VERSION "1.0" + +struct ds1286_priv { + struct rtc_device *rtc; + u32 __iomem *rtcregs; + size_t size; + unsigned long baseaddr; + spinlock_t lock; +}; + +static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg) +{ + return __raw_readl(&priv->rtcregs[reg]) & 0xff; +} + +static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg) +{ + __raw_writel(data, &priv->rtcregs[reg]); +} + +#ifdef CONFIG_RTC_INTF_DEV + +static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + unsigned char val; + + switch (cmd) { + case RTC_AIE_OFF: + /* Mask alarm int. enab. bit */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val |= RTC_TDM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + case RTC_AIE_ON: + /* Allow alarm interrupts. */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val &= ~RTC_TDM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + case RTC_WIE_OFF: + /* Mask watchdog int. enab. bit */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val |= RTC_WAM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + case RTC_WIE_ON: + /* Allow watchdog interrupts. */ + spin_lock_irqsave(&priv->lock, flags); + val = ds1286_rtc_read(priv, RTC_CMD); + val &= ~RTC_WAM; + ds1286_rtc_write(priv, val, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +#else +#define ds1286_ioctl NULL +#endif + +#ifdef CONFIG_PROC_FS + +static int ds1286_proc(struct device *dev, struct seq_file *seq) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char month, cmd, amode; + const char *s; + + month = ds1286_rtc_read(priv, RTC_MONTH); + seq_printf(seq, + "oscillator\t: %s\n" + "square_wave\t: %s\n", + (month & RTC_EOSC) ? "disabled" : "enabled", + (month & RTC_ESQW) ? "disabled" : "enabled"); + + amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) | + ((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) | + ((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7); + switch (amode) { + case 7: + s = "each minute"; + break; + case 3: + s = "minutes match"; + break; + case 1: + s = "hours and minutes match"; + break; + case 0: + s = "days, hours and minutes match"; + break; + default: + s = "invalid"; + break; + } + seq_printf(seq, "alarm_mode\t: %s\n", s); + + cmd = ds1286_rtc_read(priv, RTC_CMD); + seq_printf(seq, + "alarm_enable\t: %s\n" + "wdog_alarm\t: %s\n" + "alarm_mask\t: %s\n" + "wdog_alarm_mask\t: %s\n" + "interrupt_mode\t: %s\n" + "INTB_mode\t: %s_active\n" + "interrupt_pins\t: %s\n", + (cmd & RTC_TDF) ? "yes" : "no", + (cmd & RTC_WAF) ? "yes" : "no", + (cmd & RTC_TDM) ? "disabled" : "enabled", + (cmd & RTC_WAM) ? "disabled" : "enabled", + (cmd & RTC_PU_LVL) ? "pulse" : "level", + (cmd & RTC_IBH_LO) ? "low" : "high", + (cmd & RTC_IPSW) ? "unswapped" : "swapped"); + return 0; +} + +#else +#define ds1286_proc NULL +#endif + +static int ds1286_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char save_control; + unsigned long flags; + unsigned long uip_watchdog = jiffies; + + /* + * read RTC once any update in progress is done. The update + * can take just over 2ms. We wait 10 to 20ms. There is no need to + * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. + * If you need to know *exactly* when a second has started, enable + * periodic update complete interrupts, (via ioctl) and then + * immediately read /dev/rtc which will block until you get the IRQ. + * Once the read clears, read the RTC time (again via ioctl). Easy. + */ + + if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE) + while (time_before(jiffies, uip_watchdog + 2*HZ/100)) + barrier(); + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irqsave(&priv->lock, flags); + save_control = ds1286_rtc_read(priv, RTC_CMD); + ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); + + tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS); + tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES); + tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f; + tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE); + tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f; + tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR); + + ds1286_rtc_write(priv, save_control, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if (tm->tm_year < 45) + tm->tm_year += 30; + tm->tm_year += 40; + if (tm->tm_year < 70) + tm->tm_year += 100; + + tm->tm_mon--; + + return rtc_valid_tm(tm); +} + +static int ds1286_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char mon, day, hrs, min, sec; + unsigned char save_control; + unsigned int yrs; + unsigned long flags; + + yrs = tm->tm_year + 1900; + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + if (yrs < 1970) + return -EINVAL; + + yrs -= 1940; + if (yrs > 255) /* They are unsigned */ + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + + spin_lock_irqsave(&priv->lock, flags); + save_control = ds1286_rtc_read(priv, RTC_CMD); + ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD); + + ds1286_rtc_write(priv, yrs, RTC_YEAR); + ds1286_rtc_write(priv, mon, RTC_MONTH); + ds1286_rtc_write(priv, day, RTC_DATE); + ds1286_rtc_write(priv, hrs, RTC_HOURS); + ds1286_rtc_write(priv, min, RTC_MINUTES); + ds1286_rtc_write(priv, sec, RTC_SECONDS); + ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND); + + ds1286_rtc_write(priv, save_control, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char cmd; + unsigned long flags; + + /* + * Only the values that we read from the RTC are set. That + * means only tm_wday, tm_hour, tm_min. + */ + spin_lock_irqsave(&priv->lock, flags); + alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f; + alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f; + alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07; + cmd = ds1286_rtc_read(priv, RTC_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + alm->time.tm_min = bcd2bin(alm->time.tm_min); + alm->time.tm_hour = bcd2bin(alm->time.tm_hour); + alm->time.tm_sec = 0; + return 0; +} + +static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct ds1286_priv *priv = dev_get_drvdata(dev); + unsigned char hrs, min, sec; + + hrs = alm->time.tm_hour; + min = alm->time.tm_min; + sec = alm->time.tm_sec; + + if (hrs >= 24) + hrs = 0xff; + + if (min >= 60) + min = 0xff; + + if (sec != 0) + return -EINVAL; + + min = bin2bcd(min); + hrs = bin2bcd(hrs); + + spin_lock(&priv->lock); + ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM); + ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM); + spin_unlock(&priv->lock); + + return 0; +} + +static const struct rtc_class_ops ds1286_ops = { + .ioctl = ds1286_ioctl, + .proc = ds1286_proc, + .read_time = ds1286_read_time, + .set_time = ds1286_set_time, + .read_alarm = ds1286_read_alarm, + .set_alarm = ds1286_set_alarm, +}; + +static int __devinit ds1286_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + struct ds1286_priv *priv; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + priv = kzalloc(sizeof(struct ds1286_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->rtcregs = ioremap(priv->baseaddr, priv->size); + if (!priv->rtcregs) { + ret = -ENOMEM; + goto out; + } + spin_lock_init(&priv->lock); + rtc = rtc_device_register("ds1286", &pdev->dev, + &ds1286_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto out; + } + priv->rtc = rtc; + platform_set_drvdata(pdev, priv); + return 0; + +out: + if (priv->rtc) + rtc_device_unregister(priv->rtc); + if (priv->rtcregs) + iounmap(priv->rtcregs); + if (priv->baseaddr) + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return ret; +} + +static int __devexit ds1286_remove(struct platform_device *pdev) +{ + struct ds1286_priv *priv = platform_get_drvdata(pdev); + + rtc_device_unregister(priv->rtc); + iounmap(priv->rtcregs); + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return 0; +} + +static struct platform_driver ds1286_platform_driver = { + .driver = { + .name = "rtc-ds1286", + .owner = THIS_MODULE, + }, + .probe = ds1286_probe, + .remove = __devexit_p(ds1286_remove), +}; + +static int __init ds1286_init(void) +{ + return platform_driver_register(&ds1286_platform_driver); +} + +static void __exit ds1286_exit(void) +{ + platform_driver_unregister(&ds1286_platform_driver); +} + +MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); +MODULE_DESCRIPTION("DS1286 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:rtc-ds1286"); + +module_init(ds1286_init); +module_exit(ds1286_exit); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index bbf97e6..4fcf073 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -23,10 +23,6 @@ * to have set the chip up as a clock (turning on the oscillator and * setting the date and time), Linux can ignore the non-clock features. * That's a natural job for a factory or repair bench. - * - * 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 { ds_1307, @@ -67,6 +63,7 @@ enum ds_type { # define DS1307_BIT_RS0 0x01 #define DS1337_REG_CONTROL 0x0e # define DS1337_BIT_nEOSC 0x80 +# define DS1339_BIT_BBSQI 0x20 # define DS1337_BIT_RS2 0x10 # define DS1337_BIT_RS1 0x08 # define DS1337_BIT_INTCN 0x04 @@ -83,19 +80,22 @@ enum ds_type { # define DS1337_BIT_OSF 0x80 # define DS1337_BIT_A2I 0x02 # define DS1337_BIT_A1I 0x01 +#define DS1339_REG_ALARM1_SECS 0x07 #define DS1339_REG_TRICKLE 0x10 struct ds1307 { u8 reg_addr; - bool has_nvram; - u8 regs[8]; + u8 regs[11]; enum ds_type type; + unsigned long flags; +#define HAS_NVRAM 0 /* bit 0 == sysfs file active */ +#define HAS_ALARM 1 /* bit 1 == irq claimed */ struct i2c_msg msg[2]; struct i2c_client *client; - struct i2c_client dev; struct rtc_device *rtc; + struct work_struct work; }; struct chip_desc { @@ -132,12 +132,79 @@ static const struct i2c_device_id ds1307_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds1307_id); +/*----------------------------------------------------------------------*/ + +/* + * The IRQ logic includes a "real" handler running in IRQ context just + * long enough to schedule this workqueue entry. We need a task context + * to talk to the RTC, since I2C I/O calls require that; and disable the + * IRQ until we clear its status on the chip, so that this handler can + * work with any type of triggering (not just falling edge). + * + * The ds1337 and ds1339 both have two alarms, but we only use the first + * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm + * signal; ds1339 chips have only one alarm signal. + */ +static void ds1307_work(struct work_struct *work) +{ + struct ds1307 *ds1307; + struct i2c_client *client; + struct mutex *lock; + int stat, control; + + ds1307 = container_of(work, struct ds1307, work); + client = ds1307->client; + lock = &ds1307->rtc->ops_lock; + + mutex_lock(lock); + stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS); + if (stat < 0) + goto out; + + if (stat & DS1337_BIT_A1I) { + stat &= ~DS1337_BIT_A1I; + i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat); + + control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); + if (control < 0) + goto out; + + control &= ~DS1337_BIT_A1IE; + i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control); + + /* rtc_update_irq() assumes that it is called + * from IRQ-disabled context. + */ + local_irq_disable(); + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + local_irq_enable(); + } + +out: + if (test_bit(HAS_ALARM, &ds1307->flags)) + enable_irq(client->irq); + mutex_unlock(lock); +} + +static irqreturn_t ds1307_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct ds1307 *ds1307 = i2c_get_clientdata(client); + + disable_irq_nosync(irq); + schedule_work(&ds1307->work); + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + static int ds1307_get_time(struct device *dev, struct rtc_time *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp; /* read the RTC date and time registers all at once */ + ds1307->reg_addr = 0; ds1307->msg[1].flags = I2C_M_RD; ds1307->msg[1].len = 7; @@ -231,9 +298,186 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) return 0; } +static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* read all ALARM1, ALARM2, and status registers at once */ + ds1307->reg_addr = DS1339_REG_ALARM1_SECS; + ds1307->msg[1].flags = I2C_M_RD; + ds1307->msg[1].len = 9; + + ret = i2c_transfer(to_i2c_adapter(client->dev.parent), + ds1307->msg, 2); + if (ret != 2) { + dev_err(dev, "%s error %d\n", "alarm read", ret); + return -EIO; + } + + dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n", + "alarm read", + ds1307->regs[0], ds1307->regs[1], + ds1307->regs[2], ds1307->regs[3], + ds1307->regs[4], ds1307->regs[5], + ds1307->regs[6], ds1307->regs[7], + ds1307->regs[8]); + + /* report alarm time (ALARM1); assume 24 hour and day-of-month modes, + * and that all four fields are checked matches + */ + t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); + t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f); + t->time.tm_mon = -1; + t->time.tm_year = -1; + t->time.tm_wday = -1; + t->time.tm_yday = -1; + t->time.tm_isdst = -1; + + /* ... and status */ + t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE); + t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I); + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm read", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + return 0; +} + +static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + unsigned char *buf = ds1307->regs; + u8 control, status; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + dev_dbg(dev, "%s secs=%d, mins=%d, " + "hours=%d, mday=%d, enabled=%d, pending=%d\n", + "alarm set", t->time.tm_sec, t->time.tm_min, + t->time.tm_hour, t->time.tm_mday, + t->enabled, t->pending); + + /* read current status of both alarms and the chip */ + ds1307->reg_addr = DS1339_REG_ALARM1_SECS; + ds1307->msg[1].flags = I2C_M_RD; + ds1307->msg[1].len = 9; + + ret = i2c_transfer(to_i2c_adapter(client->dev.parent), + ds1307->msg, 2); + if (ret != 2) { + dev_err(dev, "%s error %d\n", "alarm write", ret); + return -EIO; + } + control = ds1307->regs[7]; + status = ds1307->regs[8]; + + dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n", + "alarm set (old status)", + ds1307->regs[0], ds1307->regs[1], + ds1307->regs[2], ds1307->regs[3], + ds1307->regs[4], ds1307->regs[5], + ds1307->regs[6], control, status); + + /* set ALARM1, using 24 hour and day-of-month modes */ + *buf++ = DS1339_REG_ALARM1_SECS; /* first register addr */ + buf[0] = bin2bcd(t->time.tm_sec); + buf[1] = bin2bcd(t->time.tm_min); + buf[2] = bin2bcd(t->time.tm_hour); + buf[3] = bin2bcd(t->time.tm_mday); + + /* set ALARM2 to non-garbage */ + buf[4] = 0; + buf[5] = 0; + buf[6] = 0; + + /* optionally enable ALARM1 */ + buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + if (t->enabled) { + dev_dbg(dev, "alarm IRQ armed\n"); + buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + } + buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + + ds1307->msg[1].flags = 0; + ds1307->msg[1].len = 10; + + ret = i2c_transfer(to_i2c_adapter(client->dev.parent), + &ds1307->msg[1], 1); + if (ret != 1) { + dev_err(dev, "can't set alarm time\n"); + return -EIO; + } + + return 0; +} + +static int ds1307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + int ret; + + switch (cmd) { + case RTC_AIE_OFF: + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -ENOTTY; + + ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); + if (ret < 0) + return ret; + + ret &= ~DS1337_BIT_A1IE; + + ret = i2c_smbus_write_byte_data(client, + DS1337_REG_CONTROL, ret); + if (ret < 0) + return ret; + + break; + + case RTC_AIE_ON: + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -ENOTTY; + + ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL); + if (ret < 0) + return ret; + + ret |= DS1337_BIT_A1IE; + + ret = i2c_smbus_write_byte_data(client, + DS1337_REG_CONTROL, ret); + if (ret < 0) + return ret; + + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + static const struct rtc_class_ops ds13xx_rtc_ops = { .read_time = ds1307_get_time, .set_time = ds1307_set_time, + .read_alarm = ds1307_read_alarm, + .set_alarm = ds1307_set_alarm, + .ioctl = ds1307_ioctl, }; /*----------------------------------------------------------------------*/ @@ -327,6 +571,7 @@ static int __devinit ds1307_probe(struct i2c_client *client, int tmp; const struct chip_desc *chip = &chips[id->driver_data]; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int want_irq = false; if (!i2c_check_functionality(adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) @@ -353,6 +598,12 @@ static int __devinit ds1307_probe(struct i2c_client *client, switch (ds1307->type) { case ds_1337: case ds_1339: + /* has IRQ? */ + if (ds1307->client->irq > 0 && chip->alarm) { + INIT_WORK(&ds1307->work, ds1307_work); + want_irq = true; + } + ds1307->reg_addr = DS1337_REG_CONTROL; ds1307->msg[1].len = 2; @@ -369,8 +620,20 @@ static int __devinit ds1307_probe(struct i2c_client *client, /* 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); + ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + + /* Using IRQ? Disable the square wave and both alarms. + * For ds1339, be sure alarms can trigger when we're + * running on Vbackup (BBSQI); we assume ds1337 will + * ignore that bit + */ + if (want_irq) { + ds1307->regs[0] |= DS1337_BIT_INTCN | DS1339_BIT_BBSQI; + ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); + } + + i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, + ds1307->regs[0]); /* oscillator fault? clear flag, and warn */ if (ds1307->regs[1] & DS1337_BIT_OSF) { @@ -495,10 +758,22 @@ read_rtc: goto exit_free; } + if (want_irq) { + err = request_irq(client->irq, ds1307_irq, 0, + ds1307->rtc->name, client); + if (err) { + dev_err(&client->dev, + "unable to request IRQ!\n"); + goto exit_irq; + } + set_bit(HAS_ALARM, &ds1307->flags); + dev_dbg(&client->dev, "got IRQ %d\n", client->irq); + } + if (chip->nvram56) { err = sysfs_create_bin_file(&client->dev.kobj, &nvram); if (err == 0) { - ds1307->has_nvram = true; + set_bit(HAS_NVRAM, &ds1307->flags); dev_info(&client->dev, "56 bytes nvram\n"); } } @@ -512,7 +787,9 @@ exit_bad: ds1307->regs[2], ds1307->regs[3], ds1307->regs[4], ds1307->regs[5], ds1307->regs[6]); - +exit_irq: + if (ds1307->rtc) + rtc_device_unregister(ds1307->rtc); exit_free: kfree(ds1307); return err; @@ -520,9 +797,14 @@ exit_free: static int __devexit ds1307_remove(struct i2c_client *client) { - struct ds1307 *ds1307 = i2c_get_clientdata(client); + struct ds1307 *ds1307 = i2c_get_clientdata(client); + + if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) { + free_irq(client->irq, client); + cancel_work_sync(&ds1307->work); + } - if (ds1307->has_nvram) + if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) sysfs_remove_bin_file(&client->dev.kobj, &nvram); rtc_device_unregister(ds1307->rtc); diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index a150418..a5b0fc0 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -429,12 +429,33 @@ static int __devexit ds1374_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM +static int ds1374_suspend(struct i2c_client *client, pm_message_t state) +{ + if (client->irq >= 0 && device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + return 0; +} + +static int ds1374_resume(struct i2c_client *client) +{ + if (client->irq >= 0 && device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + return 0; +} +#else +#define ds1374_suspend NULL +#define ds1374_resume NULL +#endif + static struct i2c_driver ds1374_driver = { .driver = { .name = "rtc-ds1374", .owner = THIS_MODULE, }, .probe = ds1374_probe, + .suspend = ds1374_suspend, + .resume = ds1374_resume, .remove = __devexit_p(ds1374_remove), .id_table = ds1374_id, }; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 0f0d27d..86981d3 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -379,18 +379,6 @@ ds1511_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } - static void -ds1511_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - if (pdata->irq >= 0) { - pdata->irqen = 0; - ds1511_rtc_update_alarm(pdata); - } -} - static int ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { @@ -428,7 +416,6 @@ static const struct rtc_class_ops ds1511_rtc_ops = { .set_time = ds1511_rtc_set_time, .read_alarm = ds1511_rtc_read_alarm, .set_alarm = ds1511_rtc_set_alarm, - .release = ds1511_rtc_release, .ioctl = ds1511_rtc_ioctl, }; diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index a19f114..4ef5928 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -207,17 +207,6 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void ds1553_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - if (pdata->irq >= 0) { - pdata->irqen = 0; - ds1553_rtc_update_alarm(pdata); - } -} - static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { @@ -254,7 +243,6 @@ static const struct rtc_class_ops ds1553_rtc_ops = { .set_time = ds1553_rtc_set_time, .read_alarm = ds1553_rtc_read_alarm, .set_alarm = ds1553_rtc_set_alarm, - .release = ds1553_rtc_release, .ioctl = ds1553_rtc_ioctl, }; diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 6fa4556..341d7a5 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -9,17 +9,10 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> #include <linux/i2c.h> #include <linux/rtc.h> -#define DRV_VERSION "0.3" - -/* Addresses to scan: none. This chip cannot be detected. */ -static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD; +#define DRV_VERSION "0.4" /* Registers */ @@ -29,8 +22,7 @@ I2C_CLIENT_INSMOD; #define DS1672_REG_CONTROL_EOSC 0x80 -/* Prototypes */ -static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind); +static struct i2c_driver ds1672_driver; /* * In the routines that deal directly with the ds1672 hardware, we use @@ -44,8 +36,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned char buf[4]; struct i2c_msg msgs[] = { - { client->addr, 0, 1, &addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 4, buf }, /* read date */ + {client->addr, 0, 1, &addr}, /* setup read ptr */ + {client->addr, I2C_M_RD, 4, buf}, /* read date */ }; /* read date registers */ @@ -80,7 +72,7 @@ static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs) buf[2] = (secs & 0x0000FF00) >> 8; buf[3] = (secs & 0x00FF0000) >> 16; buf[4] = (secs & 0xFF000000) >> 24; - buf[5] = 0; /* set control reg to enable counting */ + buf[5] = 0; /* set control reg to enable counting */ xfer = i2c_master_send(client, buf, 6); if (xfer != 6) { @@ -127,8 +119,8 @@ static int ds1672_get_control(struct i2c_client *client, u8 *status) unsigned char addr = DS1672_REG_CONTROL; struct i2c_msg msgs[] = { - { client->addr, 0, 1, &addr }, /* setup read ptr */ - { client->addr, I2C_M_RD, 1, status }, /* read control */ + {client->addr, 0, 1, &addr}, /* setup read ptr */ + {client->addr, I2C_M_RD, 1, status}, /* read control */ }; /* read control register */ @@ -141,7 +133,8 @@ static int ds1672_get_control(struct i2c_client *client, u8 *status) } /* following are the sysfs callback functions */ -static ssize_t show_control(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_control(struct device *dev, struct device_attribute *attr, + char *buf) { struct i2c_client *client = to_i2c_client(dev); u8 control; @@ -152,85 +145,46 @@ static ssize_t show_control(struct device *dev, struct device_attribute *attr, c return err; return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC) - ? "disabled" : "enabled"); + ? "disabled" : "enabled"); } + static DEVICE_ATTR(control, S_IRUGO, show_control, NULL); static const struct rtc_class_ops ds1672_rtc_ops = { - .read_time = ds1672_rtc_read_time, - .set_time = ds1672_rtc_set_time, - .set_mmss = ds1672_rtc_set_mmss, + .read_time = ds1672_rtc_read_time, + .set_time = ds1672_rtc_set_time, + .set_mmss = ds1672_rtc_set_mmss, }; -static int ds1672_attach(struct i2c_adapter *adapter) +static int ds1672_remove(struct i2c_client *client) { - return i2c_probe(adapter, &addr_data, ds1672_probe); -} - -static int ds1672_detach(struct i2c_client *client) -{ - int err; struct rtc_device *rtc = i2c_get_clientdata(client); - if (rtc) + if (rtc) rtc_device_unregister(rtc); - if ((err = i2c_detach_client(client))) - return err; - - kfree(client); - return 0; } -static struct i2c_driver ds1672_driver = { - .driver = { - .name = "ds1672", - }, - .id = I2C_DRIVERID_DS1672, - .attach_adapter = &ds1672_attach, - .detach_client = &ds1672_detach, -}; - -static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) +static int ds1672_probe(struct i2c_client *client, + const struct i2c_device_id *id) { int err = 0; u8 control; - struct i2c_client *client; struct rtc_device *rtc; - dev_dbg(&adapter->dev, "%s\n", __func__); + dev_dbg(&client->dev, "%s\n", __func__); - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { - err = -ENODEV; - goto exit; - } - - if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - /* I2C client */ - client->addr = address; - client->driver = &ds1672_driver; - client->adapter = adapter; - - strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE); - - /* Inform the i2c layer */ - if ((err = i2c_attach_client(client))) - goto exit_kfree; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); rtc = rtc_device_register(ds1672_driver.driver.name, &client->dev, - &ds1672_rtc_ops, THIS_MODULE); + &ds1672_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - err = PTR_ERR(rtc); - goto exit_detach; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); i2c_set_clientdata(client, rtc); @@ -241,7 +195,7 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) if (control & DS1672_REG_CONTROL_EOSC) dev_warn(&client->dev, "Oscillator not enabled. " - "Set time to enable.\n"); + "Set time to enable.\n"); /* Register sysfs hooks */ err = device_create_file(&client->dev, &dev_attr_control); @@ -250,19 +204,19 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) return 0; -exit_devreg: + exit_devreg: rtc_device_unregister(rtc); - -exit_detach: - i2c_detach_client(client); - -exit_kfree: - kfree(client); - -exit: return err; } +static struct i2c_driver ds1672_driver = { + .driver = { + .name = "rtc-ds1672", + }, + .probe = &ds1672_probe, + .remove = &ds1672_remove, +}; + static int __init ds1672_init(void) { return i2c_add_driver(&ds1672_driver); diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c new file mode 100644 index 0000000..37d131d --- /dev/null +++ b/drivers/rtc/rtc-ds3234.c @@ -0,0 +1,290 @@ +/* drivers/rtc/rtc-ds3234.c + * + * Driver for Dallas Semiconductor (DS3234) SPI RTC with Integrated Crystal + * and SRAM. + * + * Copyright (C) 2008 MIMOMax Wireless Ltd. + * + * 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. + * + * Changelog: + * + * 07-May-2008: Dennis Aberilla <denzzzhome@yahoo.com> + * - Created based on the max6902 code. Only implements the + * date/time keeping functions; no SRAM yet. + */ + +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +#define DS3234_REG_SECONDS 0x00 +#define DS3234_REG_MINUTES 0x01 +#define DS3234_REG_HOURS 0x02 +#define DS3234_REG_DAY 0x03 +#define DS3234_REG_DATE 0x04 +#define DS3234_REG_MONTH 0x05 +#define DS3234_REG_YEAR 0x06 +#define DS3234_REG_CENTURY (1 << 7) /* Bit 7 of the Month register */ + +#define DS3234_REG_CONTROL 0x0E +#define DS3234_REG_CONT_STAT 0x0F + +#undef DS3234_DEBUG + +struct ds3234 { + struct rtc_device *rtc; + u8 buf[8]; /* Burst read: addr + 7 regs */ + u8 tx_buf[2]; + u8 rx_buf[2]; +}; + +static void ds3234_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* MSB must be '1' to indicate write */ + buf[0] = address | 0x80; + buf[1] = data; + + spi_write(spi, buf, 2); +} + +static int ds3234_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds3234 *chip = dev_get_drvdata(dev); + struct spi_message message; + struct spi_transfer xfer; + int status; + + if (!data) + return -EINVAL; + + /* Build our spi message */ + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + + /* Address + dummy tx byte */ + xfer.len = 2; + xfer.tx_buf = chip->tx_buf; + xfer.rx_buf = chip->rx_buf; + + chip->tx_buf[0] = address; + chip->tx_buf[1] = 0xff; + + spi_message_add_tail(&xfer, &message); + + /* do the i/o */ + status = spi_sync(spi, &message); + if (status == 0) + status = message.status; + else + return status; + + *data = chip->rx_buf[1]; + + return status; +} + +static int ds3234_get_datetime(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds3234 *chip = dev_get_drvdata(dev); + struct spi_message message; + struct spi_transfer xfer; + int status; + + /* build the message */ + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + xfer.len = 1 + 7; /* Addr + 7 registers */ + xfer.tx_buf = chip->buf; + xfer.rx_buf = chip->buf; + chip->buf[0] = 0x00; /* Start address */ + spi_message_add_tail(&xfer, &message); + + /* do the i/o */ + status = spi_sync(spi, &message); + if (status == 0) + status = message.status; + else + return status; + + /* Seconds, Minutes, Hours, Day, Date, Month, Year */ + dt->tm_sec = bcd2bin(chip->buf[1]); + dt->tm_min = bcd2bin(chip->buf[2]); + dt->tm_hour = bcd2bin(chip->buf[3] & 0x3f); + dt->tm_wday = bcd2bin(chip->buf[4]) - 1; /* 0 = Sun */ + dt->tm_mday = bcd2bin(chip->buf[5]); + dt->tm_mon = bcd2bin(chip->buf[6] & 0x1f) - 1; /* 0 = Jan */ + dt->tm_year = bcd2bin(chip->buf[7] & 0xff) + 100; /* Assume 20YY */ + +#ifdef DS3234_DEBUG + dev_dbg(dev, "\n%s : Read RTC values\n", __func__); + dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); + dev_dbg(dev, "tm_min : %i\n", dt->tm_min); + dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); + dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); + dev_dbg(dev, "tm_year: %i\n", dt->tm_year); +#endif + + return 0; +} + +static int ds3234_set_datetime(struct device *dev, struct rtc_time *dt) +{ +#ifdef DS3234_DEBUG + dev_dbg(dev, "\n%s : Setting RTC values\n", __func__); + dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec); + dev_dbg(dev, "tm_min : %i\n", dt->tm_min); + dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); + dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); + dev_dbg(dev, "tm_year: %i\n", dt->tm_year); +#endif + + ds3234_set_reg(dev, DS3234_REG_SECONDS, bin2bcd(dt->tm_sec)); + ds3234_set_reg(dev, DS3234_REG_MINUTES, bin2bcd(dt->tm_min)); + ds3234_set_reg(dev, DS3234_REG_HOURS, bin2bcd(dt->tm_hour) & 0x3f); + + /* 0 = Sun */ + ds3234_set_reg(dev, DS3234_REG_DAY, bin2bcd(dt->tm_wday + 1)); + ds3234_set_reg(dev, DS3234_REG_DATE, bin2bcd(dt->tm_mday)); + + /* 0 = Jan */ + ds3234_set_reg(dev, DS3234_REG_MONTH, bin2bcd(dt->tm_mon + 1)); + + /* Assume 20YY although we just want to make sure not to go negative. */ + if (dt->tm_year > 100) + dt->tm_year -= 100; + + ds3234_set_reg(dev, DS3234_REG_YEAR, bin2bcd(dt->tm_year)); + + return 0; +} + +static int ds3234_read_time(struct device *dev, struct rtc_time *tm) +{ + return ds3234_get_datetime(dev, tm); +} + +static int ds3234_set_time(struct device *dev, struct rtc_time *tm) +{ + return ds3234_set_datetime(dev, tm); +} + +static const struct rtc_class_ops ds3234_rtc_ops = { + .read_time = ds3234_read_time, + .set_time = ds3234_set_time, +}; + +static int ds3234_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + struct ds3234 *chip; + int res; + + rtc = rtc_device_register("ds3234", + &spi->dev, &ds3234_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + chip = kzalloc(sizeof(struct ds3234), GFP_KERNEL); + if (!chip) { + rtc_device_unregister(rtc); + return -ENOMEM; + } + chip->rtc = rtc; + dev_set_drvdata(&spi->dev, chip); + + res = ds3234_get_reg(&spi->dev, DS3234_REG_SECONDS, &tmp); + if (res) { + rtc_device_unregister(rtc); + return res; + } + + /* Control settings + * + * CONTROL_REG + * BIT 7 6 5 4 3 2 1 0 + * EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE + * + * 0 0 0 1 1 1 0 0 + * + * CONTROL_STAT_REG + * BIT 7 6 5 4 3 2 1 0 + * OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F + * + * 1 0 0 0 1 0 0 0 + */ + ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp); + ds3234_set_reg(&spi->dev, DS3234_REG_CONTROL, tmp & 0x1c); + + ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp); + ds3234_set_reg(&spi->dev, DS3234_REG_CONT_STAT, tmp & 0x88); + + /* Print our settings */ + ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp); + dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp); + + ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp); + dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp); + + return 0; +} + +static int __exit ds3234_remove(struct spi_device *spi) +{ + struct ds3234 *chip = platform_get_drvdata(spi); + struct rtc_device *rtc = chip->rtc; + + if (rtc) + rtc_device_unregister(rtc); + + kfree(chip); + + return 0; +} + +static struct spi_driver ds3234_driver = { + .driver = { + .name = "ds3234", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ds3234_probe, + .remove = __devexit_p(ds3234_remove), +}; + +static __init int ds3234_init(void) +{ + printk(KERN_INFO "DS3234 SPI RTC Driver\n"); + return spi_register_driver(&ds3234_driver); +} +module_init(ds3234_init); + +static __exit void ds3234_exit(void) +{ + spi_unregister_driver(&ds3234_driver); +} +module_exit(ds3234_exit); + +MODULE_DESCRIPTION("DS3234 SPI RTC driver"); +MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 24bc168..470fb2d 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -56,21 +56,27 @@ #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_WATCHDOG_RB2 (1 << 7) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB1 (1 << 1) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB0 (1 << 0) /* RB: Watchdog resolution */ -#define M41T80_FEATURE_HT (1 << 0) -#define M41T80_FEATURE_BL (1 << 1) +#define M41T80_FEATURE_HT (1 << 0) /* Halt feature */ +#define M41T80_FEATURE_BL (1 << 1) /* Battery low indicator */ +#define M41T80_FEATURE_SQ (1 << 2) /* Squarewave feature */ +#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */ #define DRV_VERSION "0.05" static const struct i2c_device_id m41t80_id[] = { - { "m41t80", 0 }, - { "m41t81", M41T80_FEATURE_HT }, - { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, - { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, - { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, - { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, - { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, - { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL }, + { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD }, + { "m41t80", M41T80_FEATURE_SQ }, + { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, + { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, { } }; MODULE_DEVICE_TABLE(i2c, m41t80_id); @@ -386,8 +392,12 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); int val; + if (!(clientdata->features & M41T80_FEATURE_SQ)) + return -EINVAL; + val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); if (val < 0) return -EIO; @@ -408,9 +418,13 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); int almon, sqw; int val = simple_strtoul(buf, NULL, 0); + if (!(clientdata->features & M41T80_FEATURE_SQ)) + return -EINVAL; + if (val) { if (!is_power_of_2(val)) return -EINVAL; @@ -499,6 +513,8 @@ static void wdt_ping(void) .buf = i2c_data, }, }; + struct m41t80_data *clientdata = i2c_get_clientdata(save_client); + i2c_data[0] = 0x09; /* watchdog register */ if (wdt_margin > 31) @@ -509,6 +525,13 @@ static void wdt_ping(void) */ i2c_data[1] = wdt_margin<<2 | 0x82; + /* + * M41T65 has three bits for watchdog resolution. Don't set bit 7, as + * that would be an invalid resolution. + */ + if (clientdata->features & M41T80_FEATURE_WD) + i2c_data[1] &= ~M41T80_WATCHDOG_RB2; + i2c_transfer(save_client->adapter, msgs1, 1); } diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c new file mode 100644 index 0000000..0b21975 --- /dev/null +++ b/drivers/rtc/rtc-m48t35.c @@ -0,0 +1,235 @@ +/* + * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip + * + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * + * Copyright (C) 2008 Thomas Bogendoerfer + * + * Based on code written by Paul Gortmaker. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> +#include <linux/bcd.h> +#include <linux/io.h> + +#define DRV_VERSION "1.0" + +struct m48t35_rtc { + u8 pad[0x7ff8]; /* starts at 0x7ff8 */ + u8 control; + u8 sec; + u8 min; + u8 hour; + u8 day; + u8 date; + u8 month; + u8 year; +}; + +#define M48T35_RTC_SET 0x80 +#define M48T35_RTC_READ 0x40 + +struct m48t35_priv { + struct rtc_device *rtc; + struct m48t35_rtc __iomem *reg; + size_t size; + unsigned long baseaddr; + spinlock_t lock; +}; + +static int m48t35_read_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t35_priv *priv = dev_get_drvdata(dev); + u8 control; + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irq(&priv->lock); + control = readb(&priv->reg->control); + writeb(control | M48T35_RTC_READ, &priv->reg->control); + tm->tm_sec = readb(&priv->reg->sec); + tm->tm_min = readb(&priv->reg->min); + tm->tm_hour = readb(&priv->reg->hour); + tm->tm_mday = readb(&priv->reg->date); + tm->tm_mon = readb(&priv->reg->month); + tm->tm_year = readb(&priv->reg->year); + writeb(control, &priv->reg->control); + spin_unlock_irq(&priv->lock); + + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + tm->tm_year += 70; + if (tm->tm_year <= 69) + tm->tm_year += 100; + + tm->tm_mon--; + return rtc_valid_tm(tm); +} + +static int m48t35_set_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t35_priv *priv = dev_get_drvdata(dev); + unsigned char mon, day, hrs, min, sec; + unsigned int yrs; + u8 control; + + yrs = tm->tm_year + 1900; + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + if (yrs < 1970) + return -EINVAL; + + yrs -= 1970; + if (yrs > 255) /* They are unsigned */ + return -EINVAL; + + if (yrs > 169) + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + + spin_lock_irq(&priv->lock); + control = readb(&priv->reg->control); + writeb(control | M48T35_RTC_SET, &priv->reg->control); + writeb(yrs, &priv->reg->year); + writeb(mon, &priv->reg->month); + writeb(day, &priv->reg->date); + writeb(hrs, &priv->reg->hour); + writeb(min, &priv->reg->min); + writeb(sec, &priv->reg->sec); + writeb(control, &priv->reg->control); + spin_unlock_irq(&priv->lock); + return 0; +} + +static const struct rtc_class_ops m48t35_ops = { + .read_time = m48t35_read_time, + .set_time = m48t35_set_time, +}; + +static int __devinit m48t35_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + struct resource *res; + struct m48t35_priv *priv; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + priv = kzalloc(sizeof(struct m48t35_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->size = res->end - res->start + 1; + /* + * kludge: remove the #ifndef after ioc3 resource + * conflicts are resolved + */ +#ifndef CONFIG_SGI_IP27 + if (!request_mem_region(res->start, priv->size, pdev->name)) { + ret = -EBUSY; + goto out; + } +#endif + priv->baseaddr = res->start; + priv->reg = ioremap(priv->baseaddr, priv->size); + if (!priv->reg) { + ret = -ENOMEM; + goto out; + } + spin_lock_init(&priv->lock); + rtc = rtc_device_register("m48t35", &pdev->dev, + &m48t35_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + ret = PTR_ERR(rtc); + goto out; + } + priv->rtc = rtc; + platform_set_drvdata(pdev, priv); + return 0; + +out: + if (priv->rtc) + rtc_device_unregister(priv->rtc); + if (priv->reg) + iounmap(priv->reg); + if (priv->baseaddr) + release_mem_region(priv->baseaddr, priv->size); + kfree(priv); + return ret; +} + +static int __devexit m48t35_remove(struct platform_device *pdev) +{ + struct m48t35_priv *priv = platform_get_drvdata(pdev); + + rtc_device_unregister(priv->rtc); + iounmap(priv->reg); +#ifndef CONFIG_SGI_IP27 + release_mem_region(priv->baseaddr, priv->size); +#endif + kfree(priv); + return 0; +} + +static struct platform_driver m48t35_platform_driver = { + .driver = { + .name = "rtc-m48t35", + .owner = THIS_MODULE, + }, + .probe = m48t35_probe, + .remove = __devexit_p(m48t35_remove), +}; + +static int __init m48t35_init(void) +{ + return platform_driver_register(&m48t35_platform_driver); +} + +static void __exit m48t35_exit(void) +{ + platform_driver_unregister(&m48t35_platform_driver); +} + +MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>"); +MODULE_DESCRIPTION("M48T35 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_ALIAS("platform:rtc-m48t35"); + +module_init(m48t35_init); +module_exit(m48t35_exit); diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 013e6c1..ce4eff6 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -24,8 +24,9 @@ #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_READ(reg) (pdata->read_byte(dev, pdata->offset + reg)) +#define M48T59_WRITE(val, reg) \ + (pdata->write_byte(dev, pdata->offset + reg, val)) #define M48T59_SET_BITS(mask, reg) \ M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) @@ -34,7 +35,6 @@ struct m48t59_private { void __iomem *ioaddr; - unsigned int size; /* iomem size */ int irq; struct rtc_device *rtc; spinlock_t lock; /* serialize the NVRAM and RTC access */ @@ -82,7 +82,8 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_MDAY)); val = M48T59_READ(M48T59_WDAY); - if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { + if ((pdata->type == M48T59RTC_TYPE_M48T59) && + (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { dev_dbg(dev, "Century bit is enabled\n"); tm->tm_year += 100; /* one century */ } @@ -126,7 +127,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) M48T59_WRITE((BIN2BCD(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); M48T59_WRITE(BIN2BCD(tm->tm_year % 100), M48T59_YEAR); - if (tm->tm_year/100) + if (pdata->type == M48T59RTC_TYPE_M48T59 && (tm->tm_year / 100)) val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); val |= (BIN2BCD(tm->tm_wday) & 0x07); M48T59_WRITE(val, M48T59_WDAY); @@ -310,6 +311,11 @@ static const struct rtc_class_ops m48t59_rtc_ops = { .proc = m48t59_rtc_proc, }; +static const struct rtc_class_ops m48t02_rtc_ops = { + .read_time = m48t59_rtc_read_time, + .set_time = m48t59_rtc_set_time, +}; + static ssize_t m48t59_nvram_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) @@ -321,7 +327,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); *buf++ = M48T59_READ(cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -341,7 +347,7 @@ static ssize_t m48t59_nvram_write(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); M48T59_WRITE(*buf++, cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -358,7 +364,6 @@ static struct bin_attribute m48t59_nvram_attr = { }, .read = m48t59_nvram_read, .write = m48t59_nvram_write, - .size = M48T59_NVRAM_SIZE, }; static int __devinit m48t59_rtc_probe(struct platform_device *pdev) @@ -367,6 +372,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) struct m48t59_private *m48t59 = NULL; struct resource *res; int ret = -ENOMEM; + char *name; + const struct rtc_class_ops *ops; /* This chip could be memory-mapped or I/O-mapped */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -391,6 +398,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) /* Ensure we only kmalloc platform data once */ pdev->dev.platform_data = pdata; } + if (!pdata->type) + pdata->type = M48T59RTC_TYPE_M48T59; /* Try to use the generic memory read/write ops */ if (!pdata->write_byte) @@ -403,10 +412,14 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59) return -ENOMEM; - m48t59->size = res->end - res->start + 1; - m48t59->ioaddr = ioremap(res->start, m48t59->size); - if (!m48t59->ioaddr) - goto out; + m48t59->ioaddr = pdata->ioaddr; + + if (!m48t59->ioaddr) { + /* ioaddr not mapped externally */ + m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); + if (!m48t59->ioaddr) + goto out; + } /* Try to get irq number. We also can work in * the mode without IRQ. @@ -421,14 +434,36 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (ret) goto out; } + switch (pdata->type) { + case M48T59RTC_TYPE_M48T59: + name = "m48t59"; + ops = &m48t59_rtc_ops; + pdata->offset = 0x1ff0; + break; + case M48T59RTC_TYPE_M48T02: + name = "m48t02"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x7f0; + break; + case M48T59RTC_TYPE_M48T08: + name = "m48t08"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x1ff0; + break; + default: + dev_err(&pdev->dev, "Unknown RTC type\n"); + ret = -ENODEV; + goto out; + } - m48t59->rtc = rtc_device_register("m48t59", &pdev->dev, - &m48t59_rtc_ops, THIS_MODULE); + m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE); if (IS_ERR(m48t59->rtc)) { ret = PTR_ERR(m48t59->rtc); goto out; } + m48t59_nvram_attr.size = pdata->offset; + ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (ret) goto out; @@ -452,11 +487,12 @@ out: static int __devexit m48t59_rtc_remove(struct platform_device *pdev) { struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (!IS_ERR(m48t59->rtc)) rtc_device_unregister(m48t59->rtc); - if (m48t59->ioaddr) + if (m48t59->ioaddr && !pdata->ioaddr) iounmap(m48t59->ioaddr); if (m48t59->irq != NO_IRQ) free_irq(m48t59->irq, &pdev->dev); @@ -491,5 +527,5 @@ module_init(m48t59_rtc_init); module_exit(m48t59_rtc_exit); MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>"); -MODULE_DESCRIPTION("M48T59 RTC driver"); +MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index ded3c0a..12c9cd2 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -17,19 +17,18 @@ #include <linux/rtc.h> #include <linux/delay.h> -#define DRV_NAME "max6900" -#define DRV_VERSION "0.1" +#define DRV_VERSION "0.2" /* * register indices */ -#define MAX6900_REG_SC 0 /* seconds 00-59 */ -#define MAX6900_REG_MN 1 /* minutes 00-59 */ -#define MAX6900_REG_HR 2 /* hours 00-23 */ -#define MAX6900_REG_DT 3 /* day of month 00-31 */ -#define MAX6900_REG_MO 4 /* month 01-12 */ -#define MAX6900_REG_DW 5 /* day of week 1-7 */ -#define MAX6900_REG_YR 6 /* year 00-99 */ +#define MAX6900_REG_SC 0 /* seconds 00-59 */ +#define MAX6900_REG_MN 1 /* minutes 00-59 */ +#define MAX6900_REG_HR 2 /* hours 00-23 */ +#define MAX6900_REG_DT 3 /* day of month 00-31 */ +#define MAX6900_REG_MO 4 /* month 01-12 */ +#define MAX6900_REG_DW 5 /* day of week 1-7 */ +#define MAX6900_REG_YR 6 /* year 00-99 */ #define MAX6900_REG_CT 7 /* control */ /* register 8 is undocumented */ #define MAX6900_REG_CENTURY 9 /* century */ @@ -39,7 +38,6 @@ #define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */ - /* * register read/write commands */ @@ -52,16 +50,7 @@ #define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */ -#define MAX6900_I2C_ADDR 0xa0 - -static const unsigned short normal_i2c[] = { - MAX6900_I2C_ADDR >> 1, - I2C_CLIENT_END -}; - -I2C_CLIENT_INSMOD; /* defines addr_data */ - -static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind); +static struct i2c_driver max6900_driver; static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf) { @@ -69,36 +58,35 @@ static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf) u8 reg_century_read[1] = { MAX6900_REG_CENTURY_READ }; struct i2c_msg msgs[4] = { { - .addr = client->addr, - .flags = 0, /* write */ - .len = sizeof(reg_burst_read), - .buf = reg_burst_read - }, + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(reg_burst_read), + .buf = reg_burst_read} + , { - .addr = client->addr, - .flags = I2C_M_RD, - .len = MAX6900_BURST_LEN, - .buf = buf - }, + .addr = client->addr, + .flags = I2C_M_RD, + .len = MAX6900_BURST_LEN, + .buf = buf} + , { - .addr = client->addr, - .flags = 0, /* write */ - .len = sizeof(reg_century_read), - .buf = reg_century_read - }, + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(reg_century_read), + .buf = reg_century_read} + , { - .addr = client->addr, - .flags = I2C_M_RD, - .len = sizeof(buf[MAX6900_REG_CENTURY]), - .buf = &buf[MAX6900_REG_CENTURY] - } + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(buf[MAX6900_REG_CENTURY]), + .buf = &buf[MAX6900_REG_CENTURY] + } }; int rc; rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (rc != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, "%s: register read failed\n", - __func__); + dev_err(&client->dev, "%s: register read failed\n", __func__); return -EIO; } return 0; @@ -109,20 +97,18 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf) u8 i2c_century_buf[1 + 1] = { MAX6900_REG_CENTURY_WRITE }; struct i2c_msg century_msgs[1] = { { - .addr = client->addr, - .flags = 0, /* write */ - .len = sizeof(i2c_century_buf), - .buf = i2c_century_buf - } + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(i2c_century_buf), + .buf = i2c_century_buf} }; u8 i2c_burst_buf[MAX6900_BURST_LEN + 1] = { MAX6900_REG_BURST_WRITE }; struct i2c_msg burst_msgs[1] = { { - .addr = client->addr, - .flags = 0, /* write */ - .len = sizeof(i2c_burst_buf), - .buf = i2c_burst_buf - } + .addr = client->addr, + .flags = 0, /* write */ + .len = sizeof(i2c_burst_buf), + .buf = i2c_burst_buf} }; int rc; @@ -133,10 +119,12 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf) * bit as part of the burst write. */ i2c_century_buf[1] = buf[MAX6900_REG_CENTURY]; + rc = i2c_transfer(client->adapter, century_msgs, ARRAY_SIZE(century_msgs)); if (rc != ARRAY_SIZE(century_msgs)) goto write_failed; + msleep(MAX6900_IDLE_TIME_AFTER_WRITE); memcpy(&i2c_burst_buf[1], buf, MAX6900_BURST_LEN); @@ -148,45 +136,11 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf) return 0; -write_failed: - dev_err(&client->dev, "%s: register write failed\n", - __func__); + write_failed: + dev_err(&client->dev, "%s: register write failed\n", __func__); return -EIO; } -static int max6900_i2c_validate_client(struct i2c_client *client) -{ - u8 regs[MAX6900_REG_LEN]; - u8 zero_mask[] = { - 0x80, /* seconds */ - 0x80, /* minutes */ - 0x40, /* hours */ - 0xc0, /* day of month */ - 0xe0, /* month */ - 0xf8, /* day of week */ - 0x00, /* year */ - 0x7f, /* control */ - }; - int i; - int rc; - int reserved; - - reserved = i2c_smbus_read_byte_data(client, MAX6900_REG_RESERVED_READ); - if (reserved != 0x07) - return -ENODEV; - - rc = max6900_i2c_read_regs(client, regs); - if (rc < 0) - return rc; - - for (i = 0; i < ARRAY_SIZE(zero_mask); ++i) { - if (regs[i] & zero_mask[i]) - return -ENODEV; - } - - return 0; -} - static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) { int rc; @@ -202,7 +156,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) tm->tm_mday = BCD2BIN(regs[MAX6900_REG_DT]); tm->tm_mon = BCD2BIN(regs[MAX6900_REG_MO]) - 1; tm->tm_year = BCD2BIN(regs[MAX6900_REG_YR]) + - BCD2BIN(regs[MAX6900_REG_CENTURY]) * 100 - 1900; + BCD2BIN(regs[MAX6900_REG_CENTURY]) * 100 - 1900; tm->tm_wday = BCD2BIN(regs[MAX6900_REG_DW]); return 0; @@ -211,7 +165,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm) static int max6900_i2c_clear_write_protect(struct i2c_client *client) { int rc; - rc = i2c_smbus_write_byte_data (client, MAX6900_REG_CONTROL_WRITE, 0); + rc = i2c_smbus_write_byte_data(client, MAX6900_REG_CONTROL_WRITE, 0); if (rc < 0) { dev_err(&client->dev, "%s: control register write failed\n", __func__); @@ -220,8 +174,8 @@ static int max6900_i2c_clear_write_protect(struct i2c_client *client) return 0; } -static int max6900_i2c_set_time(struct i2c_client *client, - struct rtc_time const *tm) +static int +max6900_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm) { u8 regs[MAX6900_REG_LEN]; int rc; @@ -258,89 +212,49 @@ static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm) return max6900_i2c_set_time(to_i2c_client(dev), tm); } -static int max6900_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_probe(adapter, &addr_data, max6900_probe); -} - -static int max6900_detach_client(struct i2c_client *client) +static int max6900_remove(struct i2c_client *client) { - struct rtc_device *const rtc = i2c_get_clientdata(client); + struct rtc_device *rtc = i2c_get_clientdata(client); if (rtc) rtc_device_unregister(rtc); - return i2c_detach_client(client); + return 0; } -static struct i2c_driver max6900_driver = { - .driver = { - .name = DRV_NAME, - }, - .id = I2C_DRIVERID_MAX6900, - .attach_adapter = max6900_attach_adapter, - .detach_client = max6900_detach_client, -}; - static const struct rtc_class_ops max6900_rtc_ops = { - .read_time = max6900_rtc_read_time, - .set_time = max6900_rtc_set_time, + .read_time = max6900_rtc_read_time, + .set_time = max6900_rtc_set_time, }; -static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind) +static int +max6900_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int rc = 0; - struct i2c_client *client = NULL; - struct rtc_device *rtc = NULL; - - if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { - rc = -ENODEV; - goto failout; - } - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == NULL) { - rc = -ENOMEM; - goto failout; - } - - client->addr = addr; - client->adapter = adapter; - client->driver = &max6900_driver; - strlcpy(client->name, DRV_NAME, I2C_NAME_SIZE); - - if (kind < 0) { - rc = max6900_i2c_validate_client(client); - if (rc < 0) - goto failout; - } + struct rtc_device *rtc; - rc = i2c_attach_client(client); - if (rc < 0) - goto failout; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; - dev_info(&client->dev, - "chip found, driver version " DRV_VERSION "\n"); + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); rtc = rtc_device_register(max6900_driver.driver.name, - &client->dev, - &max6900_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - rc = PTR_ERR(rtc); - goto failout_detach; - } + &client->dev, &max6900_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); i2c_set_clientdata(client, rtc); return 0; - -failout_detach: - i2c_detach_client(client); -failout: - kfree(client); - return rc; } +static struct i2c_driver max6900_driver = { + .driver = { + .name = "rtc-max6900", + }, + .probe = max6900_probe, + .remove = max6900_remove, +}; + static int __init max6900_init(void) { return i2c_add_driver(&max6900_driver); @@ -352,6 +266,7 @@ static void __exit max6900_exit(void) } MODULE_DESCRIPTION("Maxim MAX6900 RTC driver"); +MODULE_AUTHOR("Dale Farnsworth <dale@farnsworth.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 748a502..a829f20 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -179,58 +179,6 @@ struct pcf8563_limit unsigned char max; }; -static int pcf8563_validate_client(struct i2c_client *client) -{ - int i; - - static const struct pcf8563_limit pattern[] = { - /* register, mask, min, max */ - { PCF8563_REG_SC, 0x7F, 0, 59 }, - { PCF8563_REG_MN, 0x7F, 0, 59 }, - { PCF8563_REG_HR, 0x3F, 0, 23 }, - { PCF8563_REG_DM, 0x3F, 0, 31 }, - { PCF8563_REG_MO, 0x1F, 0, 12 }, - }; - - /* check limits (only registers with bcd values) */ - for (i = 0; i < ARRAY_SIZE(pattern); i++) { - int xfer; - unsigned char value; - unsigned char buf = pattern[i].reg; - - struct i2c_msg msgs[] = { - { client->addr, 0, 1, &buf }, - { client->addr, I2C_M_RD, 1, &buf }, - }; - - xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - - if (xfer != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, - "%s: could not read register 0x%02X\n", - __func__, pattern[i].reg); - - return -EIO; - } - - value = BCD2BIN(buf & pattern[i].mask); - - if (value > pattern[i].max || - value < pattern[i].min) { - dev_dbg(&client->dev, - "%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, " - "max=%d, value=%d, raw=0x%02X\n", - __func__, i, pattern[i].reg, pattern[i].mask, - pattern[i].min, pattern[i].max, - value, buf); - - return -ENODEV; - } - } - - return 0; -} - static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) { return pcf8563_get_datetime(to_i2c_client(dev), tm); @@ -262,12 +210,6 @@ static int pcf8563_probe(struct i2c_client *client, if (!pcf8563) return -ENOMEM; - /* Verify the chip is really an PCF8563 */ - if (pcf8563_validate_client(client) < 0) { - err = -ENODEV; - goto exit_kfree; - } - dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); pcf8563->rtc = rtc_device_register(pcf8563_driver.driver.name, diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index 8448eeb..8261535 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -34,15 +34,6 @@ static irqreturn_t pl030_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int pl030_open(struct device *dev) -{ - return 0; -} - -static void pl030_release(struct device *dev) -{ -} - static int pl030_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { return -ENOIOCTLCMD; @@ -104,8 +95,6 @@ static int pl030_set_time(struct device *dev, struct rtc_time *tm) } static const struct rtc_class_ops pl030_ops = { - .open = pl030_open, - .release = pl030_release, .ioctl = pl030_ioctl, .read_time = pl030_read_time, .set_time = pl030_set_time, diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 08b4610..333eec6 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -45,18 +45,6 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int pl031_open(struct device *dev) -{ - /* - * We request IRQ in pl031_probe, so nothing to do here... - */ - return 0; -} - -static void pl031_release(struct device *dev) -{ -} - static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct pl031_local *ldata = dev_get_drvdata(dev); @@ -118,8 +106,6 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) } static const struct rtc_class_ops pl031_ops = { - .open = pl031_open, - .release = pl031_release, .ioctl = pl031_ioctl, .read_time = pl031_read_time, .set_time = pl031_set_time, diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 56caf6b..8b56195 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -1,8 +1,9 @@ /* - * An I2C driver for Ricoh RS5C372 and RV5C38[67] RTCs + * An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs * * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> * Copyright (C) 2006 Tower Technologies + * Copyright (C) 2008 Paul Mundt * * 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 @@ -13,7 +14,7 @@ #include <linux/rtc.h> #include <linux/bcd.h> -#define DRV_VERSION "0.5" +#define DRV_VERSION "0.6" /* @@ -51,7 +52,8 @@ # define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */ #define RS5C_REG_CTRL2 15 # define RS5C372_CTRL2_24 (1 << 5) -# define RS5C_CTRL2_XSTP (1 << 4) +# define R2025_CTRL2_XST (1 << 5) +# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */ # define RS5C_CTRL2_CTFG (1 << 2) # define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */ # define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */ @@ -63,6 +65,7 @@ enum rtc_type { rtc_undef = 0, + rtc_r2025sd, rtc_rs5c372a, rtc_rs5c372b, rtc_rv5c386, @@ -70,6 +73,7 @@ enum rtc_type { }; static const struct i2c_device_id rs5c372_id[] = { + { "r2025sd", rtc_r2025sd }, { "rs5c372a", rtc_rs5c372a }, { "rs5c372b", rtc_rs5c372b }, { "rv5c386", rtc_rv5c386 }, @@ -89,6 +93,7 @@ struct rs5c372 { enum rtc_type type; unsigned time24:1; unsigned has_irq:1; + unsigned smbus:1; char buf[17]; char *regs; }; @@ -106,10 +111,25 @@ static int rs5c_get_regs(struct rs5c372 *rs5c) * * The first method doesn't work with the iop3xx adapter driver, on at * least 80219 chips; this works around that bug. + * + * The third method on the other hand doesn't work for the SMBus-only + * configurations, so we use the the first method there, stripping off + * the extra register in the process. */ - if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { - dev_warn(&client->dev, "can't read registers\n"); - return -EIO; + if (rs5c->smbus) { + int addr = RS5C_ADDR(RS5C372_REG_SECS); + int size = sizeof(rs5c->buf) - 1; + + if (i2c_smbus_read_i2c_block_data(client, addr, size, + rs5c->buf + 1) != size) { + dev_warn(&client->dev, "can't read registers\n"); + return -EIO; + } + } else { + if ((i2c_transfer(client->adapter, msgs, 1)) != 1) { + dev_warn(&client->dev, "can't read registers\n"); + return -EIO; + } } dev_dbg(&client->dev, @@ -187,6 +207,7 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) { struct rs5c372 *rs5c = i2c_get_clientdata(client); unsigned char buf[8]; + int addr; dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d " "mday=%d, mon=%d, year=%d, wday=%d\n", @@ -194,16 +215,16 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm) tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); - buf[0] = RS5C_ADDR(RS5C372_REG_SECS); - buf[1] = BIN2BCD(tm->tm_sec); - buf[2] = BIN2BCD(tm->tm_min); - buf[3] = rs5c_hr2reg(rs5c, tm->tm_hour); - buf[4] = BIN2BCD(tm->tm_wday); - buf[5] = BIN2BCD(tm->tm_mday); - buf[6] = BIN2BCD(tm->tm_mon + 1); - buf[7] = BIN2BCD(tm->tm_year - 100); + addr = RS5C_ADDR(RS5C372_REG_SECS); + buf[0] = BIN2BCD(tm->tm_sec); + buf[1] = BIN2BCD(tm->tm_min); + buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour); + buf[3] = BIN2BCD(tm->tm_wday); + buf[4] = BIN2BCD(tm->tm_mday); + buf[5] = BIN2BCD(tm->tm_mon + 1); + buf[6] = BIN2BCD(tm->tm_year - 100); - if ((i2c_master_send(client, buf, 8)) != 8) { + if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) { dev_err(&client->dev, "%s: write error\n", __func__); return -EIO; } @@ -266,16 +287,16 @@ rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); - unsigned char buf[2]; - int status; + unsigned char buf; + int status, addr; - buf[1] = rs5c->regs[RS5C_REG_CTRL1]; + buf = rs5c->regs[RS5C_REG_CTRL1]; switch (cmd) { case RTC_UIE_OFF: case RTC_UIE_ON: /* some 327a modes use a different IRQ pin for 1Hz irqs */ if (rs5c->type == rtc_rs5c372a - && (buf[1] & RS5C372A_CTRL1_SL1)) + && (buf & RS5C372A_CTRL1_SL1)) return -ENOIOCTLCMD; case RTC_AIE_OFF: case RTC_AIE_ON: @@ -293,28 +314,30 @@ rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) if (status < 0) return status; - buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); + addr = RS5C_ADDR(RS5C_REG_CTRL1); switch (cmd) { case RTC_AIE_OFF: /* alarm off */ - buf[1] &= ~RS5C_CTRL1_AALE; + buf &= ~RS5C_CTRL1_AALE; break; case RTC_AIE_ON: /* alarm on */ - buf[1] |= RS5C_CTRL1_AALE; + buf |= RS5C_CTRL1_AALE; break; case RTC_UIE_OFF: /* update off */ - buf[1] &= ~RS5C_CTRL1_CT_MASK; + buf &= ~RS5C_CTRL1_CT_MASK; break; case RTC_UIE_ON: /* update on */ - buf[1] &= ~RS5C_CTRL1_CT_MASK; - buf[1] |= RS5C_CTRL1_CT4; + buf &= ~RS5C_CTRL1_CT_MASK; + buf |= RS5C_CTRL1_CT4; break; } - if ((i2c_master_send(client, buf, 2)) != 2) { + + if (i2c_smbus_write_byte_data(client, addr, buf) < 0) { printk(KERN_WARNING "%s: can't update alarm\n", rs5c->rtc->name); status = -EIO; } else - rs5c->regs[RS5C_REG_CTRL1] = buf[1]; + rs5c->regs[RS5C_REG_CTRL1] = buf; + return status; } @@ -364,8 +387,8 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct rs5c372 *rs5c = i2c_get_clientdata(client); - int status; - unsigned char buf[4]; + int status, addr, i; + unsigned char buf[3]; /* only handle up to 24 hours in the future, like RTC_ALM_SET */ if (t->time.tm_mday != -1 @@ -380,33 +403,36 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) if (status < 0) return status; if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) { - buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); - buf[1] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; - if (i2c_master_send(client, buf, 2) != 2) { + addr = RS5C_ADDR(RS5C_REG_CTRL1); + buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; + if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) { pr_debug("%s: can't disable alarm\n", rs5c->rtc->name); return -EIO; } - rs5c->regs[RS5C_REG_CTRL1] = buf[1]; + rs5c->regs[RS5C_REG_CTRL1] = buf[0]; } /* set alarm */ - buf[0] = RS5C_ADDR(RS5C_REG_ALARM_A_MIN); - buf[1] = BIN2BCD(t->time.tm_min); - buf[2] = rs5c_hr2reg(rs5c, t->time.tm_hour); - buf[3] = 0x7f; /* any/all days */ - if ((i2c_master_send(client, buf, 4)) != 4) { - pr_debug("%s: can't set alarm time\n", rs5c->rtc->name); - return -EIO; + buf[0] = BIN2BCD(t->time.tm_min); + buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); + buf[2] = 0x7f; /* any/all days */ + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); + if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) { + pr_debug("%s: can't set alarm time\n", rs5c->rtc->name); + return -EIO; + } } /* ... and maybe enable its irq */ if (t->enabled) { - buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); - buf[1] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; - if ((i2c_master_send(client, buf, 2)) != 2) + addr = RS5C_ADDR(RS5C_REG_CTRL1); + buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; + if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) printk(KERN_WARNING "%s: can't enable alarm\n", rs5c->rtc->name); - rs5c->regs[RS5C_REG_CTRL1] = buf[1]; + rs5c->regs[RS5C_REG_CTRL1] = buf[0]; } return 0; @@ -503,18 +529,81 @@ static void rs5c_sysfs_unregister(struct device *dev) static struct i2c_driver rs5c372_driver; +static int rs5c_oscillator_setup(struct rs5c372 *rs5c372) +{ + unsigned char buf[2]; + int addr, i, ret = 0; + + if (rs5c372->type == rtc_r2025sd) { + if (!(rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST)) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] &= ~R2025_CTRL2_XST; + } else { + if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP)) + return ret; + rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; + } + + addr = RS5C_ADDR(RS5C_REG_CTRL1); + buf[0] = rs5c372->regs[RS5C_REG_CTRL1]; + buf[1] = rs5c372->regs[RS5C_REG_CTRL2]; + + /* use 24hr mode */ + switch (rs5c372->type) { + case rtc_rs5c372a: + case rtc_rs5c372b: + buf[1] |= RS5C372_CTRL2_24; + rs5c372->time24 = 1; + break; + case rtc_r2025sd: + case rtc_rv5c386: + case rtc_rv5c387a: + buf[0] |= RV5C387_CTRL1_24; + rs5c372->time24 = 1; + break; + default: + /* impossible */ + break; + } + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_CTRL1 + i); + ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]); + if (unlikely(ret < 0)) + return ret; + } + + rs5c372->regs[RS5C_REG_CTRL1] = buf[0]; + rs5c372->regs[RS5C_REG_CTRL2] = buf[1]; + + return 0; +} + static int rs5c372_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err = 0; + int smbus_mode = 0; struct rs5c372 *rs5c372; struct rtc_time tm; dev_dbg(&client->dev, "%s\n", __func__); - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - err = -ENODEV; - goto exit; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) { + /* + * If we don't have any master mode adapter, try breaking + * it down in to the barest of capabilities. + */ + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + smbus_mode = 1; + else { + /* Still no good, give up */ + err = -ENODEV; + goto exit; + } } if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) { @@ -528,6 +617,7 @@ static int rs5c372_probe(struct i2c_client *client, /* we read registers 0x0f then 0x00-0x0f; skip the first one */ rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; err = rs5c_get_regs(rs5c372); if (err < 0) @@ -543,6 +633,7 @@ static int rs5c372_probe(struct i2c_client *client, if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24) rs5c372->time24 = 1; break; + case rtc_r2025sd: case rtc_rv5c386: case rtc_rv5c387a: if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24) @@ -558,39 +649,14 @@ static int rs5c372_probe(struct i2c_client *client, /* if the oscillator lost power and no other software (like * the bootloader) set it up, do it here. + * + * The R2025S/D does this a little differently than the other + * parts, so we special case that.. */ - if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP) { - unsigned char buf[3]; - - rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP; - - buf[0] = RS5C_ADDR(RS5C_REG_CTRL1); - buf[1] = rs5c372->regs[RS5C_REG_CTRL1]; - buf[2] = rs5c372->regs[RS5C_REG_CTRL2]; - - /* use 24hr mode */ - switch (rs5c372->type) { - case rtc_rs5c372a: - case rtc_rs5c372b: - buf[2] |= RS5C372_CTRL2_24; - rs5c372->time24 = 1; - break; - case rtc_rv5c386: - case rtc_rv5c387a: - buf[1] |= RV5C387_CTRL1_24; - rs5c372->time24 = 1; - break; - default: - /* impossible */ - break; - } - - if ((i2c_master_send(client, buf, 3)) != 3) { - dev_err(&client->dev, "setup error\n"); - goto exit_kfree; - } - rs5c372->regs[RS5C_REG_CTRL1] = buf[1]; - rs5c372->regs[RS5C_REG_CTRL2] = buf[2]; + err = rs5c_oscillator_setup(rs5c372); + if (unlikely(err < 0)) { + dev_err(&client->dev, "setup error\n"); + goto exit_kfree; } if (rs5c372_get_datetime(client, &tm) < 0) @@ -598,6 +664,7 @@ static int rs5c372_probe(struct i2c_client *client, dev_info(&client->dev, "%s found, %s, driver version " DRV_VERSION "\n", ({ char *s; switch (rs5c372->type) { + case rtc_r2025sd: s = "r2025sd"; break; case rtc_rs5c372a: s = "rs5c372a"; break; case rtc_rs5c372b: s = "rs5c372b"; break; case rtc_rv5c386: s = "rv5c386"; break; @@ -667,7 +734,8 @@ module_exit(rs5c372_exit); MODULE_AUTHOR( "Pavel Mironchik <pmironchik@optifacio.net>, " - "Alessandro Zummo <a.zummo@towertech.it>"); + "Alessandro Zummo <a.zummo@towertech.it>, " + "Paul Mundt <lethal@linux-sh.org>"); MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 1f88e9e..fcead4c 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -257,12 +257,6 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) spin_unlock_irq(&rtc->lock); } -static void sh_rtc_release(struct device *dev) -{ - sh_rtc_setpie(dev, 0); - sh_rtc_setaie(dev, 0); -} - static int sh_rtc_proc(struct device *dev, struct seq_file *seq) { struct sh_rtc *rtc = dev_get_drvdata(dev); @@ -559,7 +553,6 @@ static int sh_rtc_irq_set_freq(struct device *dev, int freq) } static struct rtc_class_ops sh_rtc_ops = { - .release = sh_rtc_release, .ioctl = sh_rtc_ioctl, .read_time = sh_rtc_read_time, .set_time = sh_rtc_set_time, diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c new file mode 100644 index 0000000..7ccb0dd --- /dev/null +++ b/drivers/rtc/rtc-starfire.c @@ -0,0 +1,120 @@ +/* rtc-starfire.c: Starfire platform RTC driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +#include <asm/oplib.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("Starfire RTC driver"); +MODULE_LICENSE("GPL"); + +struct starfire_rtc { + struct rtc_device *rtc; + spinlock_t lock; +}; + +static u32 starfire_get_time(void) +{ + static char obp_gettod[32]; + static u32 unix_tod; + + sprintf(obp_gettod, "h# %08x unix-gettod", + (unsigned int) (long) &unix_tod); + prom_feval(obp_gettod); + + return unix_tod; +} + +static int starfire_read_time(struct device *dev, struct rtc_time *tm) +{ + struct starfire_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + + spin_lock_irqsave(&p->lock, flags); + secs = starfire_get_time(); + spin_unlock_irqrestore(&p->lock, flags); + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int starfire_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long secs; + int err; + + err = rtc_tm_to_time(tm, &secs); + if (err) + return err; + + /* Do nothing, time is set using the service processor + * console on this platform. + */ + return 0; +} + +static const struct rtc_class_ops starfire_rtc_ops = { + .read_time = starfire_read_time, + .set_time = starfire_set_time, +}; + +static int __devinit starfire_rtc_probe(struct platform_device *pdev) +{ + struct starfire_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + spin_lock_init(&p->lock); + + p->rtc = rtc_device_register("starfire", &pdev->dev, + &starfire_rtc_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + int err = PTR_ERR(p->rtc); + kfree(p); + return err; + } + platform_set_drvdata(pdev, p); + return 0; +} + +static int __devexit starfire_rtc_remove(struct platform_device *pdev) +{ + struct starfire_rtc *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + kfree(p); + + return 0; +} + +static struct platform_driver starfire_rtc_driver = { + .driver = { + .name = "rtc-starfire", + .owner = THIS_MODULE, + }, + .probe = starfire_rtc_probe, + .remove = __devexit_p(starfire_rtc_remove), +}; + +static int __init starfire_rtc_init(void) +{ + return platform_driver_register(&starfire_rtc_driver); +} + +static void __exit starfire_rtc_exit(void) +{ + platform_driver_unregister(&starfire_rtc_driver); +} + +module_init(starfire_rtc_init); +module_exit(starfire_rtc_exit); diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index 31d3c8c..9a7e920 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -215,17 +215,6 @@ static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void stk17ta8_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - - if (pdata->irq >= 0) { - pdata->irqen = 0; - stk17ta8_rtc_update_alarm(pdata); - } -} - static int stk17ta8_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { @@ -254,7 +243,6 @@ static const struct rtc_class_ops stk17ta8_rtc_ops = { .set_time = stk17ta8_rtc_set_time, .read_alarm = stk17ta8_rtc_read_alarm, .set_alarm = stk17ta8_rtc_set_alarm, - .release = stk17ta8_rtc_release, .ioctl = stk17ta8_rtc_ioctl, }; diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c new file mode 100644 index 0000000..2012ccb --- /dev/null +++ b/drivers/rtc/rtc-sun4v.c @@ -0,0 +1,153 @@ +/* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +#include <asm/hypervisor.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("SUN4V RTC driver"); +MODULE_LICENSE("GPL"); + +struct sun4v_rtc { + struct rtc_device *rtc; + spinlock_t lock; +}; + +static unsigned long hypervisor_get_time(void) +{ + unsigned long ret, time; + int retries = 10000; + +retry: + ret = sun4v_tod_get(&time); + if (ret == HV_EOK) + return time; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); + return 0; + } + printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); + return 0; +} + +static int sun4v_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sun4v_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + + spin_lock_irqsave(&p->lock, flags); + secs = hypervisor_get_time(); + spin_unlock_irqrestore(&p->lock, flags); + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int hypervisor_set_time(unsigned long secs) +{ + unsigned long ret; + int retries = 10000; + +retry: + ret = sun4v_tod_set(secs); + if (ret == HV_EOK) + return 0; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); + return -EAGAIN; + } + printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); + return -EOPNOTSUPP; +} + +static int sun4v_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sun4v_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + int err; + + err = rtc_tm_to_time(tm, &secs); + if (err) + return err; + + spin_lock_irqsave(&p->lock, flags); + err = hypervisor_set_time(secs); + spin_unlock_irqrestore(&p->lock, flags); + + return err; +} + +static const struct rtc_class_ops sun4v_rtc_ops = { + .read_time = sun4v_read_time, + .set_time = sun4v_set_time, +}; + +static int __devinit sun4v_rtc_probe(struct platform_device *pdev) +{ + struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + spin_lock_init(&p->lock); + + p->rtc = rtc_device_register("sun4v", &pdev->dev, + &sun4v_rtc_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + int err = PTR_ERR(p->rtc); + kfree(p); + return err; + } + platform_set_drvdata(pdev, p); + return 0; +} + +static int __devexit sun4v_rtc_remove(struct platform_device *pdev) +{ + struct sun4v_rtc *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + kfree(p); + + return 0; +} + +static struct platform_driver sun4v_rtc_driver = { + .driver = { + .name = "rtc-sun4v", + .owner = THIS_MODULE, + }, + .probe = sun4v_rtc_probe, + .remove = __devexit_p(sun4v_rtc_remove), +}; + +static int __init sun4v_rtc_init(void) +{ + return platform_driver_register(&sun4v_rtc_driver); +} + +static void __exit sun4v_rtc_exit(void) +{ + platform_driver_unregister(&sun4v_rtc_driver); +} + +module_init(sun4v_rtc_init); +module_exit(sun4v_rtc_exit); |