diff options
Diffstat (limited to 'drivers/rtc')
34 files changed, 2044 insertions, 542 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 81450fbd8b12..81adbdbd5042 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -129,13 +129,14 @@ comment "I2C RTC drivers" if I2C config RTC_DRV_DS1307 - tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00" + tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" help If you say yes here you get support for various compatible RTC chips (often with battery backup) connected with I2C. This driver should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00, - and probably other chips. In some cases the RTC must already - have been initialized (by manufacturing or a bootloader). + EPSON RX-8025 and probably other chips. In some cases the RTC + must already have been initialized (by manufacturing or a + bootloader). The first seven registers on these chips hold an RTC, and other registers may add features such as NVRAM, a trickle charger for @@ -224,11 +225,11 @@ config RTC_DRV_PCF8583 will be called rtc-pcf8583. config RTC_DRV_M41T80 - tristate "ST M41T65/M41T80/81/82/83/84/85/87" + tristate "ST M41T62/65/M41T80/81/82/83/84/85/87" help 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, + supported: M41T62, M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84, M41ST85, and M41ST87. This driver can also be built as a module. If so, the module @@ -295,6 +296,15 @@ config RTC_DRV_RX8581 This driver can also be built as a module. If so the module will be called rtc-rx8581. +config RTC_DRV_RX8025 + tristate "Epson RX-8025SA/NB" + help + If you say yes here you get support for the Epson + RX-8025SA/NB RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rx8025. + endif # I2C comment "SPI RTC drivers" @@ -440,6 +450,16 @@ config RTC_DRV_DS1742 This driver can also be built as a module. If so, the module will be called rtc-ds1742. +config RTC_DRV_EFI + tristate "EFI RTC" + depends on IA64 + help + If you say yes here you will get support for the EFI + Real Time Clock. + + This driver can also be built as a module. If so, the module + will be called rtc-efi. + config RTC_DRV_STK17TA8 tristate "Simtek STK17TA8" depends on RTC_CLASS @@ -562,7 +582,7 @@ config RTC_DRV_SA1100 config RTC_DRV_SH tristate "SuperH On-Chip RTC" - depends on RTC_CLASS && SUPERH + depends on RTC_CLASS && SUPERH && HAVE_CLK help Say Y here to enable support for the on-chip RTC found in most SuperH processors. @@ -677,22 +697,16 @@ config RTC_DRV_RS5C313 help If you say yes here you get support for the Ricoh RS5C313 RTC chips. -config RTC_DRV_PARISC - tristate "PA-RISC firmware RTC support" - depends on PARISC +config RTC_DRV_GENERIC + tristate "Generic RTC support" + # Please consider writing a new RTC driver instead of using the generic + # RTC abstraction + depends on PARISC || M68K || PPC || SUPERH32 help - Say Y or M here to enable RTC support on PA-RISC systems using - firmware calls. If you do not know what you are doing, you should + Say Y or M here to enable RTC support on systems using the generic + RTC abstraction. If you do not know what you are doing, you should just say Y. -config RTC_DRV_PPC - tristate "PowerPC machine dependent RTC support" - 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_PXA tristate "PXA27x/PXA3xx" depends on ARCH_PXA @@ -736,4 +750,13 @@ config RTC_DRV_MV This driver can also be built as a module. If so, the module will be called rtc-mv. +config RTC_DRV_PS3 + tristate "PS3 RTC" + depends on PPC_PS3 + help + If you say yes here you will get support for the RTC on PS3. + + This driver can also be built as a module. If so, the module + will be called rtc-ps3. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0e697aa51caa..3c0f2b2ac927 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -36,6 +36,7 @@ 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_EFI) += rtc-efi.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 @@ -55,13 +56,13 @@ obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o -obj-$(CONFIG_RTC_DRV_PARISC) += rtc-parisc.o -obj-$(CONFIG_RTC_DRV_PPC) += rtc-ppc.o +obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o @@ -76,3 +77,4 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o +obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 4348c4b0d453..4cdb31a362ca 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -371,19 +371,21 @@ EXPORT_SYMBOL_GPL(rtc_update_irq_enable); * @rtc: the rtc device * @num: how many irqs are being reported (usually one) * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF - * Context: in_interrupt(), irqs blocked + * Context: any */ void rtc_update_irq(struct rtc_device *rtc, unsigned long num, unsigned long events) { - spin_lock(&rtc->irq_lock); + unsigned long flags; + + spin_lock_irqsave(&rtc->irq_lock, flags); rtc->irq_data = (rtc->irq_data + (num << 8)) | events; - spin_unlock(&rtc->irq_lock); + spin_unlock_irqrestore(&rtc->irq_lock, flags); - spin_lock(&rtc->irq_task_lock); + spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task) rtc->irq_task->func(rtc->irq_task->private_data); - spin_unlock(&rtc->irq_task_lock); + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); wake_up_interruptible(&rtc->irq_queue); kill_fasync(&rtc->async_queue, SIGIO, POLL_IN); diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index d5e4e637ddec..86c61f143515 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -351,7 +351,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) /* register irq handler after we know what name we'll use */ ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, IRQF_DISABLED | IRQF_SHARED, - rtc->rtcdev->dev.bus_id, rtc); + dev_name(&rtc->rtcdev->dev), rtc); if (ret) { dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS); rtc_device_unregister(rtc->rtcdev); @@ -366,7 +366,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) if (gpbr_readl(rtc) == 0) dev_warn(&pdev->dev, "%s: SET TIME!\n", - rtc->rtcdev->dev.bus_id); + dev_name(&rtc->rtcdev->dev)); return 0; diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index aafd3e6ebb0d..a118eb0f1e67 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -1,8 +1,8 @@ /* * Blackfin On-Chip Real Time Clock Driver - * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789] + * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x * - * Copyright 2004-2008 Analog Devices Inc. + * Copyright 2004-2009 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -363,7 +363,7 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) struct bfin_rtc *rtc; struct device *dev = &pdev->dev; int ret = 0; - unsigned long timeout; + unsigned long timeout = jiffies + HZ; dev_dbg_stamp(dev); @@ -374,32 +374,32 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); device_init_wakeup(dev, 1); + /* Register our RTC with the RTC framework */ + rtc->rtc_dev = rtc_device_register(pdev->name, dev, &bfin_rtc_ops, + THIS_MODULE); + if (unlikely(IS_ERR(rtc->rtc_dev))) { + ret = PTR_ERR(rtc->rtc_dev); + goto err; + } + /* Grab the IRQ and init the hardware */ ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, pdev->name, dev); if (unlikely(ret)) - goto err; + goto err_reg; /* sometimes the bootloader touched things, but the write complete was not * enabled, so let's just do a quick timeout here since the IRQ will not fire ... */ - timeout = jiffies + HZ; while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING) if (time_after(jiffies, timeout)) break; bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE); bfin_write_RTC_SWCNT(0); - /* Register our RTC with the RTC framework */ - rtc->rtc_dev = rtc_device_register(pdev->name, dev, &bfin_rtc_ops, THIS_MODULE); - if (unlikely(IS_ERR(rtc->rtc_dev))) { - ret = PTR_ERR(rtc->rtc_dev); - goto err_irq; - } - return 0; - err_irq: - free_irq(IRQ_RTC, dev); - err: +err_reg: + rtc_device_unregister(rtc->rtc_dev); +err: kfree(rtc); return ret; } diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b6d35f50e404..f7a4701bf863 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -797,17 +797,15 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) goto cleanup2; } - pr_info("%s: alarms up to one %s%s, %zd bytes nvram%s\n", - dev_name(&cmos_rtc.rtc->dev), - is_valid_irq(rtc_irq) - ? (cmos_rtc.mon_alrm - ? "year" - : (cmos_rtc.day_alrm - ? "month" : "day")) - : "no", - cmos_rtc.century ? ", y3k" : "", - nvram.size, - is_hpet_enabled() ? ", hpet irqs" : ""); + pr_info("%s: %s%s, %zd bytes nvram%s\n", + dev_name(&cmos_rtc.rtc->dev), + !is_valid_irq(rtc_irq) ? "no alarms" : + cmos_rtc.mon_alrm ? "alarms up to one year" : + cmos_rtc.day_alrm ? "alarms up to one month" : + "alarms up to one day", + cmos_rtc.century ? ", y3k" : "", + nvram.size, + is_hpet_enabled() ? ", hpet irqs" : ""); return 0; @@ -1176,23 +1174,34 @@ static struct platform_driver cmos_platform_driver = { } }; +#ifdef CONFIG_PNP +static bool pnp_driver_registered; +#endif +static bool platform_driver_registered; + static int __init cmos_init(void) { int retval = 0; #ifdef CONFIG_PNP - pnp_register_driver(&cmos_pnp_driver); + retval = pnp_register_driver(&cmos_pnp_driver); + if (retval == 0) + pnp_driver_registered = true; #endif - if (!cmos_rtc.dev) + if (!cmos_rtc.dev) { retval = platform_driver_probe(&cmos_platform_driver, cmos_platform_probe); + if (retval == 0) + platform_driver_registered = true; + } if (retval == 0) return 0; #ifdef CONFIG_PNP - pnp_unregister_driver(&cmos_pnp_driver); + if (pnp_driver_registered) + pnp_unregister_driver(&cmos_pnp_driver); #endif return retval; } @@ -1201,9 +1210,11 @@ module_init(cmos_init); static void __exit cmos_exit(void) { #ifdef CONFIG_PNP - pnp_unregister_driver(&cmos_pnp_driver); + if (pnp_driver_registered) + pnp_unregister_driver(&cmos_pnp_driver); #endif - platform_driver_unregister(&cmos_platform_driver); + if (platform_driver_registered) + platform_driver_unregister(&cmos_platform_driver); } module_exit(cmos_exit); diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 45152f4952d6..8a11de9552cd 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -60,8 +60,7 @@ static void rtc_uie_task(struct work_struct *work) err = rtc_read_time(rtc, &tm); - local_irq_disable(); - spin_lock(&rtc->irq_lock); + spin_lock_irq(&rtc->irq_lock); if (rtc->stop_uie_polling || err) { rtc->uie_task_active = 0; } else if (rtc->oldsecs != tm.tm_sec) { @@ -74,10 +73,9 @@ static void rtc_uie_task(struct work_struct *work) } else if (schedule_work(&rtc->uie_task) == 0) { rtc->uie_task_active = 0; } - spin_unlock(&rtc->irq_lock); + spin_unlock_irq(&rtc->irq_lock); if (num) rtc_update_irq(rtc, num, RTC_UF | RTC_IRQF); - local_irq_enable(); } static void rtc_uie_timer(unsigned long data) { diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index fc372df6534b..8f410e59d9f5 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -499,10 +499,7 @@ static void ds1305_work(struct work_struct *work) if (!test_bit(FLAG_EXITING, &ds1305->flags)) enable_irq(spi->irq); - /* rtc_update_irq() requires an IRQ-disabled context */ - local_irq_disable(); rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF); - local_irq_enable(); } /* diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7e5155e88ac7..47a93c022d91 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -3,6 +3,7 @@ * * Copyright (C) 2005 James Chapman (ds1337 core) * Copyright (C) 2006 David Brownell + * Copyright (C) 2009 Matthias Fuchs (rx8025 support) * * 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 @@ -30,7 +31,10 @@ enum ds_type { ds_1338, ds_1339, ds_1340, + ds_1388, + ds_3231, m41t00, + rx_8025, // rs5c372 too? different address... }; @@ -64,6 +68,7 @@ enum ds_type { #define DS1337_REG_CONTROL 0x0e # define DS1337_BIT_nEOSC 0x80 # define DS1339_BIT_BBSQI 0x20 +# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */ # define DS1337_BIT_RS2 0x10 # define DS1337_BIT_RS1 0x08 # define DS1337_BIT_INTCN 0x04 @@ -83,9 +88,16 @@ enum ds_type { #define DS1339_REG_ALARM1_SECS 0x07 #define DS1339_REG_TRICKLE 0x10 +#define RX8025_REG_CTRL1 0x0e +# define RX8025_BIT_2412 0x20 +#define RX8025_REG_CTRL2 0x0f +# define RX8025_BIT_PON 0x10 +# define RX8025_BIT_VDET 0x40 +# define RX8025_BIT_XST 0x20 struct ds1307 { + u8 offset; /* register's offset */ u8 regs[11]; enum ds_type type; unsigned long flags; @@ -94,6 +106,10 @@ struct ds1307 { struct i2c_client *client; struct rtc_device *rtc; struct work_struct work; + s32 (*read_block_data)(struct i2c_client *client, u8 command, + u8 length, u8 *values); + s32 (*write_block_data)(struct i2c_client *client, u8 command, + u8 length, const u8 *values); }; struct chip_desc { @@ -116,7 +132,12 @@ static const struct chip_desc chips[] = { }, [ds_1340] = { }, +[ds_3231] = { + .alarm = 1, +}, [m41t00] = { +}, +[rx_8025] = { }, }; static const struct i2c_device_id ds1307_id[] = { @@ -124,14 +145,90 @@ static const struct i2c_device_id ds1307_id[] = { { "ds1337", ds_1337 }, { "ds1338", ds_1338 }, { "ds1339", ds_1339 }, + { "ds1388", ds_1388 }, { "ds1340", ds_1340 }, + { "ds3231", ds_3231 }, { "m41t00", m41t00 }, + { "rx8025", rx_8025 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1307_id); /*----------------------------------------------------------------------*/ +#define BLOCK_DATA_MAX_TRIES 10 + +static s32 ds1307_read_block_data_once(struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + s32 i, data; + + for (i = 0; i < length; i++) { + data = i2c_smbus_read_byte_data(client, command + i); + if (data < 0) + return data; + values[i] = data; + } + return i; +} + +static s32 ds1307_read_block_data(struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + u8 oldvalues[I2C_SMBUS_BLOCK_MAX]; + s32 ret; + int tries = 0; + + dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length); + ret = ds1307_read_block_data_once(client, command, length, values); + if (ret < 0) + return ret; + do { + if (++tries > BLOCK_DATA_MAX_TRIES) { + dev_err(&client->dev, + "ds1307_read_block_data failed\n"); + return -EIO; + } + memcpy(oldvalues, values, length); + ret = ds1307_read_block_data_once(client, command, length, + values); + if (ret < 0) + return ret; + } while (memcmp(oldvalues, values, length)); + return length; +} + +static s32 ds1307_write_block_data(struct i2c_client *client, u8 command, + u8 length, const u8 *values) +{ + u8 currvalues[I2C_SMBUS_BLOCK_MAX]; + int tries = 0; + + dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length); + do { + s32 i, ret; + + if (++tries > BLOCK_DATA_MAX_TRIES) { + dev_err(&client->dev, + "ds1307_write_block_data failed\n"); + return -EIO; + } + for (i = 0; i < length; i++) { + ret = i2c_smbus_write_byte_data(client, command + i, + values[i]); + if (ret < 0) + return ret; + } + ret = ds1307_read_block_data_once(client, command, length, + currvalues); + if (ret < 0) + return ret; + } while (memcmp(currvalues, values, length)); + return length; +} + +/*----------------------------------------------------------------------*/ + /* * The IRQ logic includes a "real" handler running in IRQ context just * long enough to schedule this workqueue entry. We need a task context @@ -170,12 +267,7 @@ static void ds1307_work(struct work_struct *work) 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: @@ -202,8 +294,8 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) int tmp; /* read the RTC date and time registers all at once */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, - DS1307_REG_SECS, 7, ds1307->regs); + tmp = ds1307->read_block_data(ds1307->client, + ds1307->offset, 7, ds1307->regs); if (tmp != 7) { dev_err(dev, "%s error %d\n", "read", tmp); return -EIO; @@ -265,6 +357,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) switch (ds1307->type) { case ds_1337: case ds_1339: + case ds_3231: buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY; break; case ds_1340: @@ -279,7 +372,8 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) "write", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - result = i2c_smbus_write_i2c_block_data(ds1307->client, 0, 7, buf); + result = ds1307->write_block_data(ds1307->client, + ds1307->offset, 7, buf); if (result < 0) { dev_err(dev, "%s error %d\n", "write", result); return result; @@ -297,7 +391,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ - ret = i2c_smbus_read_i2c_block_data(client, + ret = ds1307->read_block_data(client, DS1339_REG_ALARM1_SECS, 9, ds1307->regs); if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm read", ret); @@ -356,7 +450,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ret = i2c_smbus_read_i2c_block_data(client, + ret = ds1307->read_block_data(client, DS1339_REG_ALARM1_SECS, 9, buf); if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm write", ret); @@ -391,7 +485,7 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) } buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ret = i2c_smbus_write_i2c_block_data(client, + ret = ds1307->write_block_data(client, DS1339_REG_ALARM1_SECS, 9, buf); if (ret < 0) { dev_err(dev, "can't set alarm time\n"); @@ -479,7 +573,7 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - result = i2c_smbus_read_i2c_block_data(client, 8 + off, count, buf); + result = ds1307->read_block_data(client, 8 + off, count, buf); if (result < 0) dev_err(&client->dev, "%s error %d\n", "nvram read", result); return result; @@ -490,9 +584,11 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client; + struct ds1307 *ds1307; int result; client = kobj_to_i2c_client(kobj); + ds1307 = i2c_get_clientdata(client); if (unlikely(off >= NVRAM_SIZE)) return -EFBIG; @@ -501,7 +597,7 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - result = i2c_smbus_write_i2c_block_data(client, 8 + off, count, buf); + result = ds1307->write_block_data(client, 8 + off, count, buf); if (result < 0) { dev_err(&client->dev, "%s error %d\n", "nvram write", result); return result; @@ -534,30 +630,45 @@ static int __devinit ds1307_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int want_irq = false; unsigned char *buf; - - if (!i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_WRITE_BYTE_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK)) + static const int bbsqi_bitpos[] = { + [ds_1337] = 0, + [ds_1339] = DS1339_BIT_BBSQI, + [ds_3231] = DS3231_BIT_BBSQW, + }; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) return -ENOMEM; - ds1307->client = client; i2c_set_clientdata(client, ds1307); - ds1307->type = id->driver_data; + + ds1307->client = client; + ds1307->type = id->driver_data; + ds1307->offset = 0; + buf = ds1307->regs; + if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { + ds1307->read_block_data = i2c_smbus_read_i2c_block_data; + ds1307->write_block_data = i2c_smbus_write_i2c_block_data; + } else { + ds1307->read_block_data = ds1307_read_block_data; + ds1307->write_block_data = ds1307_write_block_data; + } switch (ds1307->type) { case ds_1337: case ds_1339: + case ds_3231: /* has IRQ? */ if (ds1307->client->irq > 0 && chip->alarm) { INIT_WORK(&ds1307->work, ds1307_work); want_irq = true; } /* get registers that the "rtc" read below won't read... */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + tmp = ds1307->read_block_data(ds1307->client, DS1337_REG_CONTROL, 2, buf); if (tmp != 2) { pr_debug("read error %d\n", tmp); @@ -570,12 +681,12 @@ static int __devinit ds1307_probe(struct i2c_client *client, 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 + * For some variants, be sure alarms can trigger when we're + * running on Vbackup (BBSQI/BBSQW) */ if (want_irq) { - ds1307->regs[0] |= DS1337_BIT_INTCN | DS1339_BIT_BBSQI; + ds1307->regs[0] |= DS1337_BIT_INTCN + | bbsqi_bitpos[ds1307->type]; ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); } @@ -589,13 +700,82 @@ static int __devinit ds1307_probe(struct i2c_client *client, dev_warn(&client->dev, "SET TIME!\n"); } break; + + case rx_8025: + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); + if (tmp != 2) { + pr_debug("read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (!(ds1307->regs[1] & RX8025_BIT_XST)) { + ds1307->regs[1] |= RX8025_BIT_XST; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, + "oscillator stop detected - SET TIME!\n"); + } + + if (ds1307->regs[1] & RX8025_BIT_PON) { + ds1307->regs[1] &= ~RX8025_BIT_PON; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, "power-on detected\n"); + } + + if (ds1307->regs[1] & RX8025_BIT_VDET) { + ds1307->regs[1] &= ~RX8025_BIT_VDET; + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL2 << 4 | 0x08, + ds1307->regs[1]); + dev_warn(&client->dev, "voltage drop detected\n"); + } + + /* make sure we are running in 24hour mode */ + if (!(ds1307->regs[0] & RX8025_BIT_2412)) { + u8 hour; + + /* switch to 24 hour mode */ + i2c_smbus_write_byte_data(client, + RX8025_REG_CTRL1 << 4 | 0x08, + ds1307->regs[0] | + RX8025_BIT_2412); + + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + RX8025_REG_CTRL1 << 4 | 0x08, 2, buf); + if (tmp != 2) { + pr_debug("read error %d\n", tmp); + err = -EIO; + goto exit_free; + } + + /* correct hour */ + hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]); + if (hour == 12) + hour = 0; + if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + hour += 12; + + i2c_smbus_write_byte_data(client, + DS1307_REG_HOUR << 4 | 0x08, + hour); + } + break; + case ds_1388: + ds1307->offset = 1; /* Seconds starts at 1 */ + break; default: break; } read_rtc: /* read RTC registers */ - tmp = i2c_smbus_read_i2c_block_data(ds1307->client, 0, 8, buf); + tmp = ds1307->read_block_data(ds1307->client, 0, 8, buf); if (tmp != 8) { pr_debug("read error %d\n", tmp); err = -EIO; @@ -649,8 +829,11 @@ read_rtc: dev_warn(&client->dev, "SET TIME!\n"); } break; + case rx_8025: case ds_1337: case ds_1339: + case ds_1388: + case ds_3231: break; } @@ -662,6 +845,8 @@ read_rtc: * systems that will run through year 2100. */ break; + case rx_8025: + break; default: if (!(tmp & DS1307_BIT_12HR)) break; diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index a5b0fc09f0c6..713f7bf5afb3 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -222,16 +222,16 @@ static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) rtc_tm_to_time(&alarm->time, &new_alarm); rtc_tm_to_time(&now, &itime); - new_alarm -= itime; - /* This can happen due to races, in addition to dates that are * truly in the past. To avoid requiring the caller to check for * races, dates in the past are assumed to be in the recent past * (i.e. not something that we'd rather the caller know about via * an error), and the alarm is set to go off as soon as possible. */ - if (new_alarm <= 0) + if (time_before_eq(new_alarm, itime)) new_alarm = 1; + else + new_alarm -= itime; mutex_lock(&ds1374->mutex); @@ -283,7 +283,7 @@ static void ds1374_work(struct work_struct *work) stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR); if (stat < 0) - return; + goto unlock; if (stat & DS1374_REG_SR_AF) { stat &= ~DS1374_REG_SR_AF; @@ -296,18 +296,13 @@ static void ds1374_work(struct work_struct *work) control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE); i2c_smbus_write_byte_data(client, DS1374_REG_CR, control); - /* rtc_update_irq() assumes that it is called - * from IRQ-disabled context. - */ - local_irq_disable(); rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF); - local_irq_enable(); } out: if (!ds1374->exiting) enable_irq(client->irq); - +unlock: mutex_unlock(&ds1374->mutex); } diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index 38d472b63406..717288527c6b 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -329,8 +329,7 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) if (pdata->irq > 0) { writeb(0, ioaddr + RTC_INTERRUPTS); if (request_irq(pdata->irq, ds1553_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, - pdev->name, pdev) < 0) { + IRQF_DISABLED, pdev->name, pdev) < 0) { dev_warn(&pdev->dev, "interrupt not available.\n"); pdata->irq = 0; } diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 8bc8501bffc8..09249459e9a4 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -57,6 +57,7 @@ struct rtc_plat_data { size_t size; resource_size_t baseaddr; unsigned long last_jiffies; + struct bin_attribute nvram_attr; }; static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -157,18 +158,6 @@ static ssize_t ds1742_nvram_write(struct kobject *kobj, return count; } -static struct bin_attribute ds1742_nvram_attr = { - .attr = { - .name = "nvram", - .mode = S_IRUGO | S_IWUSR, - }, - .read = ds1742_nvram_read, - .write = ds1742_nvram_write, - /* REVISIT: size in sysfs won't match actual size... if it's - * not a constant, each RTC should have its own attribute. - */ -}; - static int __devinit ds1742_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; @@ -199,6 +188,12 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev) pdata->size_nvram = pdata->size - RTC_SIZE; pdata->ioaddr_rtc = ioaddr + pdata->size_nvram; + pdata->nvram_attr.attr.name = "nvram"; + pdata->nvram_attr.attr.mode = S_IRUGO | S_IWUSR; + pdata->nvram_attr.read = ds1742_nvram_read; + pdata->nvram_attr.write = ds1742_nvram_write; + pdata->nvram_attr.size = pdata->size_nvram; + /* turn RTC on if it was not on */ ioaddr = pdata->ioaddr_rtc; sec = readb(ioaddr + RTC_SECONDS); @@ -221,11 +216,13 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev) pdata->rtc = rtc; pdata->last_jiffies = jiffies; platform_set_drvdata(pdev, pdata); - ds1742_nvram_attr.size = max(ds1742_nvram_attr.size, - pdata->size_nvram); - ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr); - if (ret) + + ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr); + if (ret) { + dev_err(&pdev->dev, "creating nvram file in sysfs failed\n"); goto out; + } + return 0; out: if (pdata->rtc) @@ -242,7 +239,7 @@ static int __devexit ds1742_rtc_remove(struct platform_device *pdev) { struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - sysfs_remove_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr); + sysfs_remove_bin_file(&pdev->dev.kobj, &pdata->nvram_attr); rtc_device_unregister(pdata->rtc); iounmap(pdata->ioaddr_nvram); release_mem_region(pdata->baseaddr, pdata->size); diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c new file mode 100644 index 000000000000..550292304b0f --- /dev/null +++ b/drivers/rtc/rtc-efi.c @@ -0,0 +1,235 @@ +/* + * rtc-efi: RTC Class Driver for EFI-based systems + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Author: dann frazier <dannf@hp.com> + * Based on efirtc.c by Stephane Eranian + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/time.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/efi.h> + +#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT) +/* + * EFI Epoch is 1/1/1998 + */ +#define EFI_RTC_EPOCH 1998 + +/* + * returns day of the year [0-365] + */ +static inline int +compute_yday(efi_time_t *eft) +{ + /* efi_time_t.month is in the [1-12] so, we need -1 */ + return rtc_year_days(eft->day - 1, eft->month - 1, eft->year); +} +/* + * returns day of the week [0-6] 0=Sunday + * + * Don't try to provide a year that's before 1998, please ! + */ +static int +compute_wday(efi_time_t *eft) +{ + int y; + int ndays = 0; + + if (eft->year < 1998) { + printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n"); + return -1; + } + + for (y = EFI_RTC_EPOCH; y < eft->year; y++) + ndays += 365 + (is_leap_year(y) ? 1 : 0); + + ndays += compute_yday(eft); + + /* + * 4=1/1/1998 was a Thursday + */ + return (ndays + 4) % 7; +} + +static void +convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) +{ + eft->year = wtime->tm_year + 1900; + eft->month = wtime->tm_mon + 1; + eft->day = wtime->tm_mday; + eft->hour = wtime->tm_hour; + eft->minute = wtime->tm_min; + eft->second = wtime->tm_sec; + eft->nanosecond = 0; + eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0; + eft->timezone = EFI_UNSPECIFIED_TIMEZONE; +} + +static void +convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) +{ + memset(wtime, 0, sizeof(*wtime)); + wtime->tm_sec = eft->second; + wtime->tm_min = eft->minute; + wtime->tm_hour = eft->hour; + wtime->tm_mday = eft->day; + wtime->tm_mon = eft->month - 1; + wtime->tm_year = eft->year - 1900; + + /* day of the week [0-6], Sunday=0 */ + wtime->tm_wday = compute_wday(eft); + + /* day in the year [1-365]*/ + wtime->tm_yday = compute_yday(eft); + + + switch (eft->daylight & EFI_ISDST) { + case EFI_ISDST: + wtime->tm_isdst = 1; + break; + case EFI_TIME_ADJUST_DAYLIGHT: + wtime->tm_isdst = 0; + break; + default: + wtime->tm_isdst = -1; + } +} + +static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + /* + * As of EFI v1.10, this call always returns an unsupported status + */ + status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled, + (efi_bool_t *)&wkalrm->pending, &eft); + + if (status != EFI_SUCCESS) + return -EINVAL; + + convert_from_efi_time(&eft, &wkalrm->time); + + return rtc_valid_tm(&wkalrm->time); +} + +static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + efi_time_t eft; + efi_status_t status; + + convert_to_efi_time(&wkalrm->time, &eft); + + /* + * XXX Fixme: + * As of EFI 0.92 with the firmware I have on my + * machine this call does not seem to work quite + * right + * + * As of v1.10, this call always returns an unsupported status + */ + status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft); + + printk(KERN_WARNING "write status is %d\n", (int)status); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + +static int efi_read_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + efi_time_cap_t cap; + + status = efi.get_time(&eft, &cap); + + if (status != EFI_SUCCESS) { + /* should never happen */ + printk(KERN_ERR "efitime: can't read time\n"); + return -EINVAL; + } + + convert_from_efi_time(&eft, tm); + + return rtc_valid_tm(tm); +} + +static int efi_set_time(struct device *dev, struct rtc_time *tm) +{ + efi_status_t status; + efi_time_t eft; + + convert_to_efi_time(tm, &eft); + + status = efi.set_time(&eft); + + return status == EFI_SUCCESS ? 0 : -EINVAL; +} + +static const struct rtc_class_ops efi_rtc_ops = { + .read_time = efi_read_time, + .set_time = efi_set_time, + .read_alarm = efi_read_alarm, + .set_alarm = efi_set_alarm, +}; + +static int __init efi_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + + rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + + return 0; +} + +static int __exit efi_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + rtc_device_unregister(rtc); + + return 0; +} + +static struct platform_driver efi_rtc_driver = { + .driver = { + .name = "rtc-efi", + .owner = THIS_MODULE, + }, + .probe = efi_rtc_probe, + .remove = __exit_p(efi_rtc_remove), +}; + +static int __init efi_rtc_init(void) +{ + return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe); +} + +static void __exit efi_rtc_exit(void) +{ + platform_driver_unregister(&efi_rtc_driver); +} + +module_init(efi_rtc_init); +module_exit(efi_rtc_exit); + +MODULE_AUTHOR("dann frazier <dannf@hp.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EFI RTC driver"); diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index f7a3283dd029..551332e4ed02 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c @@ -12,32 +12,56 @@ #include <linux/module.h> #include <linux/rtc.h> #include <linux/platform_device.h> -#include <mach/hardware.h> +#include <linux/io.h> + +#define EP93XX_RTC_DATA 0x000 +#define EP93XX_RTC_MATCH 0x004 +#define EP93XX_RTC_STATUS 0x008 +#define EP93XX_RTC_STATUS_INTR (1<<0) +#define EP93XX_RTC_LOAD 0x00C +#define EP93XX_RTC_CONTROL 0x010 +#define EP93XX_RTC_CONTROL_MIE (1<<0) +#define EP93XX_RTC_SWCOMP 0x108 +#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000 +#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16 +#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff +#define EP93XX_RTC_SWCOMP_INT_SHIFT 0 + +#define DRV_VERSION "0.3" -#define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x)) -#define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000) -#define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C) -#define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108) - -#define DRV_VERSION "0.2" +/* + * struct device dev.platform_data is used to store our private data + * because struct rtc_device does not have a variable to hold it. + */ +struct ep93xx_rtc { + void __iomem *mmio_base; +}; -static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload, +static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload, unsigned short *delete) { - unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP); + struct ep93xx_rtc *ep93xx_rtc = dev->platform_data; + unsigned long comp; + + comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP); if (preload) - *preload = comp & 0xffff; + *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK) + >> EP93XX_RTC_SWCOMP_INT_SHIFT; if (delete) - *delete = (comp >> 16) & 0x1f; + *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK) + >> EP93XX_RTC_SWCOMP_DEL_SHIFT; return 0; } static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) { - unsigned long time = __raw_readl(EP93XX_RTC_DATA); + struct ep93xx_rtc *ep93xx_rtc = dev->platform_data; + unsigned long time; + + time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA); rtc_time_to_tm(time, tm); return 0; @@ -45,7 +69,9 @@ static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm) static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs) { - __raw_writel(secs + 1, EP93XX_RTC_LOAD); + struct ep93xx_rtc *ep93xx_rtc = dev->platform_data; + + __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD); return 0; } @@ -53,7 +79,7 @@ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) { unsigned short preload, delete; - ep93xx_get_swcomp(dev, &preload, &delete); + ep93xx_rtc_get_swcomp(dev, &preload, &delete); seq_printf(seq, "preload\t\t: %d\n", preload); seq_printf(seq, "delete\t\t: %d\n", delete); @@ -67,54 +93,104 @@ static const struct rtc_class_ops ep93xx_rtc_ops = { .proc = ep93xx_rtc_proc, }; -static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev, +static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev, struct device_attribute *attr, char *buf) { unsigned short preload; - ep93xx_get_swcomp(dev, &preload, NULL); + ep93xx_rtc_get_swcomp(dev, &preload, NULL); return sprintf(buf, "%d\n", preload); } -static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL); +static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL); -static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev, +static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev, struct device_attribute *attr, char *buf) { unsigned short delete; - ep93xx_get_swcomp(dev, NULL, &delete); + ep93xx_rtc_get_swcomp(dev, NULL, &delete); return sprintf(buf, "%d\n", delete); } -static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL); +static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL); -static int __devinit ep93xx_rtc_probe(struct platform_device *dev) +static int __init ep93xx_rtc_probe(struct platform_device *pdev) { - struct rtc_device *rtc = rtc_device_register("ep93xx", - &dev->dev, &ep93xx_rtc_ops, THIS_MODULE); + struct ep93xx_rtc *ep93xx_rtc; + struct resource *res; + struct rtc_device *rtc; + int err; + + ep93xx_rtc = kzalloc(sizeof(struct ep93xx_rtc), GFP_KERNEL); + if (ep93xx_rtc == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENXIO; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) + return -EBUSY; + + ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res)); + if (ep93xx_rtc->mmio_base == NULL) { + err = -ENXIO; + goto fail; + } + pdev->dev.platform_data = ep93xx_rtc; + + rtc = rtc_device_register(pdev->name, + &pdev->dev, &ep93xx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { - return PTR_ERR(rtc); + err = PTR_ERR(rtc); + goto fail; } - platform_set_drvdata(dev, rtc); + platform_set_drvdata(pdev, rtc); - device_create_file(&dev->dev, &dev_attr_comp_preload); - device_create_file(&dev->dev, &dev_attr_comp_delete); + err = device_create_file(&pdev->dev, &dev_attr_comp_preload); + if (err) + goto fail; + err = device_create_file(&pdev->dev, &dev_attr_comp_delete); + if (err) { + device_remove_file(&pdev->dev, &dev_attr_comp_preload); + goto fail; + } return 0; + +fail: + if (ep93xx_rtc->mmio_base) { + iounmap(ep93xx_rtc->mmio_base); + pdev->dev.platform_data = NULL; + } + release_mem_region(res->start, resource_size(res)); + return err; } -static int __devexit ep93xx_rtc_remove(struct platform_device *dev) +static int __exit ep93xx_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct ep93xx_rtc *ep93xx_rtc = pdev->dev.platform_data; + struct resource *res; + + /* cleanup sysfs */ + device_remove_file(&pdev->dev, &dev_attr_comp_delete); + device_remove_file(&pdev->dev, &dev_attr_comp_preload); + + rtc_device_unregister(rtc); + + iounmap(ep93xx_rtc->mmio_base); + pdev->dev.platform_data = NULL; - if (rtc) - rtc_device_unregister(rtc); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(dev, NULL); + platform_set_drvdata(pdev, NULL); return 0; } @@ -122,23 +198,22 @@ static int __devexit ep93xx_rtc_remove(struct platform_device *dev) /* work with hotplug and coldplug */ MODULE_ALIAS("platform:ep93xx-rtc"); -static struct platform_driver ep93xx_rtc_platform_driver = { +static struct platform_driver ep93xx_rtc_driver = { .driver = { .name = "ep93xx-rtc", .owner = THIS_MODULE, }, - .probe = ep93xx_rtc_probe, - .remove = __devexit_p(ep93xx_rtc_remove), + .remove = __exit_p(ep93xx_rtc_remove), }; static int __init ep93xx_rtc_init(void) { - return platform_driver_register(&ep93xx_rtc_platform_driver); + return platform_driver_probe(&ep93xx_rtc_driver, ep93xx_rtc_probe); } static void __exit ep93xx_rtc_exit(void) { - platform_driver_unregister(&ep93xx_rtc_platform_driver); + platform_driver_unregister(&ep93xx_rtc_driver); } MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c new file mode 100644 index 000000000000..98322004ad2e --- /dev/null +++ b/drivers/rtc/rtc-generic.c @@ -0,0 +1,84 @@ +/* rtc-generic: RTC driver using the generic RTC abstraction + * + * Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/time.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#include <asm/rtc.h> + +static int generic_get_time(struct device *dev, struct rtc_time *tm) +{ + unsigned int ret = get_rtc_time(tm); + + if (ret & RTC_BATT_BAD) + return -EOPNOTSUPP; + + return rtc_valid_tm(tm); +} + +static int generic_set_time(struct device *dev, struct rtc_time *tm) +{ + if (set_rtc_time(tm) < 0) + return -EOPNOTSUPP; + + return 0; +} + +static const struct rtc_class_ops generic_rtc_ops = { + .read_time = generic_get_time, + .set_time = generic_set_time, +}; + +static int __init generic_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + + rtc = rtc_device_register("rtc-generic", &dev->dev, &generic_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + + return 0; +} + +static int __exit generic_rtc_remove(struct platform_device *dev) +{ + struct rtc_device *rtc = platform_get_drvdata(dev); + + rtc_device_unregister(rtc); + + return 0; +} + +static struct platform_driver generic_rtc_driver = { + .driver = { + .name = "rtc-generic", + .owner = THIS_MODULE, + }, + .remove = __exit_p(generic_rtc_remove), +}; + +static int __init generic_rtc_init(void) +{ + return platform_driver_probe(&generic_rtc_driver, generic_rtc_probe); +} + +static void __exit generic_rtc_fini(void) +{ + platform_driver_unregister(&generic_rtc_driver); +} + +module_init(generic_rtc_init); +module_exit(generic_rtc_fini); + +MODULE_AUTHOR("Kyle McMartin <kyle@mcmartin.ca>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic RTC driver"); +MODULE_ALIAS("platform:rtc-generic"); diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index dd70bf73ce9d..773851f338b8 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -26,14 +26,13 @@ static const unsigned short rtc_ydays[2][13] = { }; #define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) -#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) /* * The number of days in the month. */ int rtc_month_days(unsigned int month, unsigned int year) { - return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1); + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); } EXPORT_SYMBOL(rtc_month_days); @@ -42,7 +41,7 @@ EXPORT_SYMBOL(rtc_month_days); */ int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) { - return rtc_ydays[LEAP_YEAR(year)][month] + day-1; + return rtc_ydays[is_leap_year(year)][month] + day-1; } EXPORT_SYMBOL(rtc_year_days); @@ -66,7 +65,7 @@ void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) - LEAPS_THRU_END_OF(1970 - 1); if (days < 0) { year -= 1; - days += 365 + LEAP_YEAR(year); + days += 365 + is_leap_year(year); } tm->tm_year = year - 1900; tm->tm_yday = days + 1; diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 893f7dece239..60fe266f0f49 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -64,10 +64,12 @@ #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 M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */ #define DRV_VERSION "0.05" static const struct i2c_device_id m41t80_id[] = { + { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD }, { "m41t80", M41T80_FEATURE_SQ }, { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, @@ -393,12 +395,15 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *clientdata = i2c_get_clientdata(client); - int val; + int val, reg_sqw; if (!(clientdata->features & M41T80_FEATURE_SQ)) return -EINVAL; - val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + reg_sqw = M41T80_REG_SQW; + if (clientdata->features & M41T80_FEATURE_SQ_ALT) + reg_sqw = M41T80_REG_WDAY; + val = i2c_smbus_read_byte_data(client, reg_sqw); if (val < 0) return -EIO; val = (val >> 4) & 0xf; @@ -419,7 +424,7 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct m41t80_data *clientdata = i2c_get_clientdata(client); - int almon, sqw; + int almon, sqw, reg_sqw; int val = simple_strtoul(buf, NULL, 0); if (!(clientdata->features & M41T80_FEATURE_SQ)) @@ -440,13 +445,16 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); if (almon < 0) return -EIO; - sqw = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + reg_sqw = M41T80_REG_SQW; + if (clientdata->features & M41T80_FEATURE_SQ_ALT) + reg_sqw = M41T80_REG_WDAY; + sqw = i2c_smbus_read_byte_data(client, reg_sqw); if (sqw < 0) return -EIO; sqw = (sqw & 0x0f) | (val << 4); if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, almon & ~M41T80_ALMON_SQWE) < 0 || - i2c_smbus_write_byte_data(client, M41T80_REG_SQW, sqw) < 0) + i2c_smbus_write_byte_data(client, reg_sqw, sqw) < 0) return -EIO; if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, almon | M41T80_ALMON_SQWE) < 0) diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index 45f12dcd3716..e0263d2005ee 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -12,6 +12,7 @@ #include <linux/bcd.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/delay.h> #define RTC_TIME_REG_OFFS 0 @@ -119,6 +120,16 @@ static int __init mv_rtc_probe(struct platform_device *pdev) return -EINVAL; } + /* make sure it is actually functional */ + if (rtc_time == 0x01000000) { + ssleep(1); + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); + if (rtc_time == 0x01000000) { + dev_err(&pdev->dev, "internal RTC not ticking\n"); + return -ENODEV; + } + } + platform_set_drvdata(pdev, pdata); pdata->rtc = rtc_device_register(pdev->name, &pdev->dev, &mv_rtc_ops, THIS_MODULE); diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 2cbeb0794f14..bd1ce8e2bc18 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -377,13 +377,13 @@ static int __init omap_rtc_probe(struct platform_device *pdev) /* handle periodic and alarm irqs */ if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED, - rtc->dev.bus_id, rtc)) { + dev_name(&rtc->dev), rtc)) { pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n", pdev->name, omap_rtc_timer); goto fail0; } if (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED, - rtc->dev.bus_id, rtc)) { + dev_name(&rtc->dev), rtc)) { pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n", pdev->name, omap_rtc_alarm); goto fail1; diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c deleted file mode 100644 index c6bfa6fe1a2a..000000000000 --- a/drivers/rtc/rtc-parisc.c +++ /dev/null @@ -1,112 +0,0 @@ -/* rtc-parisc: RTC for HP PA-RISC firmware - * - * Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/time.h> -#include <linux/platform_device.h> - -#include <asm/rtc.h> - -/* as simple as can be, and no simpler. */ -struct parisc_rtc { - struct rtc_device *rtc; - spinlock_t lock; -}; - -static int parisc_get_time(struct device *dev, struct rtc_time *tm) -{ - struct parisc_rtc *p = dev_get_drvdata(dev); - unsigned long flags, ret; - - spin_lock_irqsave(&p->lock, flags); - ret = get_rtc_time(tm); - spin_unlock_irqrestore(&p->lock, flags); - - if (ret & RTC_BATT_BAD) - return -EOPNOTSUPP; - - return 0; -} - -static int parisc_set_time(struct device *dev, struct rtc_time *tm) -{ - struct parisc_rtc *p = dev_get_drvdata(dev); - unsigned long flags; - int ret; - - spin_lock_irqsave(&p->lock, flags); - ret = set_rtc_time(tm); - spin_unlock_irqrestore(&p->lock, flags); - - if (ret < 0) - return -EOPNOTSUPP; - - return 0; -} - -static const struct rtc_class_ops parisc_rtc_ops = { - .read_time = parisc_get_time, - .set_time = parisc_set_time, -}; - -static int __devinit parisc_rtc_probe(struct platform_device *dev) -{ - struct parisc_rtc *p; - - p = kzalloc(sizeof (*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - - spin_lock_init(&p->lock); - - p->rtc = rtc_device_register("rtc-parisc", &dev->dev, &parisc_rtc_ops, - THIS_MODULE); - if (IS_ERR(p->rtc)) { - int err = PTR_ERR(p->rtc); - kfree(p); - return err; - } - - platform_set_drvdata(dev, p); - - return 0; -} - -static int __devexit parisc_rtc_remove(struct platform_device *dev) -{ - struct parisc_rtc *p = platform_get_drvdata(dev); - - rtc_device_unregister(p->rtc); - kfree(p); - - return 0; -} - -static struct platform_driver parisc_rtc_driver = { - .driver = { - .name = "rtc-parisc", - .owner = THIS_MODULE, - }, - .probe = parisc_rtc_probe, - .remove = __devexit_p(parisc_rtc_remove), -}; - -static int __init parisc_rtc_init(void) -{ - return platform_driver_register(&parisc_rtc_driver); -} - -static void __exit parisc_rtc_fini(void) -{ - platform_driver_unregister(&parisc_rtc_driver); -} - -module_init(parisc_rtc_init); -module_exit(parisc_rtc_fini); - -MODULE_AUTHOR("Kyle McMartin <kyle@mcmartin.ca>"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HP PA-RISC RTC driver"); diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c index 826153552157..457231bb1029 100644 --- a/drivers/rtc/rtc-pl030.c +++ b/drivers/rtc/rtc-pl030.c @@ -102,7 +102,7 @@ static const struct rtc_class_ops pl030_ops = { .set_alarm = pl030_set_alarm, }; -static int pl030_probe(struct amba_device *dev, void *id) +static int pl030_probe(struct amba_device *dev, struct amba_id *id) { struct pl030_rtc *rtc; int ret; @@ -117,7 +117,7 @@ static int pl030_probe(struct amba_device *dev, void *id) goto err_rtc; } - rtc->base = ioremap(dev->res.start, SZ_4K); + rtc->base = ioremap(dev->res.start, resource_size(&dev->res)); if (!rtc->base) { ret = -ENOMEM; goto err_map; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 333eec689d2f..f41873f98f66 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -127,7 +127,7 @@ static int pl031_remove(struct amba_device *adev) return 0; } -static int pl031_probe(struct amba_device *adev, void *id) +static int pl031_probe(struct amba_device *adev, struct amba_id *id) { int ret; struct pl031_local *ldata; @@ -142,8 +142,7 @@ static int pl031_probe(struct amba_device *adev, void *id) goto out; } - ldata->base = ioremap(adev->res.start, - adev->res.end - adev->res.start + 1); + ldata->base = ioremap(adev->res.start, resource_size(&adev->res)); if (!ldata->base) { ret = -ENOMEM; goto out_no_remap; diff --git a/drivers/rtc/rtc-ppc.c b/drivers/rtc/rtc-ppc.c deleted file mode 100644 index c8e97e25ef7e..000000000000 --- a/drivers/rtc/rtc-ppc.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * RTC driver for ppc_md RTC functions - * - * © 2007 Red Hat, Inc. - * - * Author: David Woodhouse <dwmw2@infradead.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - -#include <linux/module.h> -#include <linux/err.h> -#include <linux/rtc.h> -#include <linux/platform_device.h> -#include <asm/machdep.h> - -static int ppc_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - ppc_md.get_rtc_time(tm); - return 0; -} - -static int ppc_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - return ppc_md.set_rtc_time(tm); -} - -static const struct rtc_class_ops ppc_rtc_ops = { - .set_time = ppc_rtc_set_time, - .read_time = ppc_rtc_read_time, -}; - -static struct rtc_device *rtc; -static struct platform_device *ppc_rtc_pdev; - -static int __init ppc_rtc_init(void) -{ - if (!ppc_md.get_rtc_time || !ppc_md.set_rtc_time) - return -ENODEV; - - ppc_rtc_pdev = platform_device_register_simple("ppc-rtc", 0, NULL, 0); - if (IS_ERR(ppc_rtc_pdev)) - return PTR_ERR(ppc_rtc_pdev); - - rtc = rtc_device_register("ppc_md", &ppc_rtc_pdev->dev, - &ppc_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - platform_device_unregister(ppc_rtc_pdev); - return PTR_ERR(rtc); - } - - return 0; -} - -static void __exit ppc_rtc_exit(void) -{ - rtc_device_unregister(rtc); - platform_device_unregister(ppc_rtc_pdev); -} - -module_init(ppc_rtc_init); -module_exit(ppc_rtc_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("Generic RTC class driver for PowerPC"); diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 0c6257a034ff..c086fc30a84c 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -105,14 +105,8 @@ static const struct file_operations rtc_proc_fops = { void rtc_proc_add_device(struct rtc_device *rtc) { - if (rtc->id == 0) { - struct proc_dir_entry *ent; - - ent = proc_create_data("driver/rtc", 0, NULL, - &rtc_proc_fops, rtc); - if (ent) - ent->owner = rtc->owner; - } + if (rtc->id == 0) + proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc); } void rtc_proc_del_device(struct rtc_device *rtc) diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c new file mode 100644 index 000000000000..968133ce1ee8 --- /dev/null +++ b/drivers/rtc/rtc-ps3.c @@ -0,0 +1,104 @@ +/* + * PS3 RTC Driver + * + * Copyright 2009 Sony Corporation + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#include <asm/lv1call.h> +#include <asm/ps3.h> + + +static u64 read_rtc(void) +{ + int result; + u64 rtc_val; + u64 tb_val; + + result = lv1_get_rtc(&rtc_val, &tb_val); + BUG_ON(result); + + return rtc_val; +} + +static int ps3_get_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm); + return rtc_valid_tm(tm); +} + +static int ps3_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long now; + + rtc_tm_to_time(tm, &now); + ps3_os_area_set_rtc_diff(now - read_rtc()); + return 0; +} + +static const struct rtc_class_ops ps3_rtc_ops = { + .read_time = ps3_get_time, + .set_time = ps3_set_time, +}; + +static int __init ps3_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + + rtc = rtc_device_register("rtc-ps3", &dev->dev, &ps3_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(dev, rtc); + return 0; +} + +static int __exit ps3_rtc_remove(struct platform_device *dev) +{ + rtc_device_unregister(platform_get_drvdata(dev)); + return 0; +} + +static struct platform_driver ps3_rtc_driver = { + .driver = { + .name = "rtc-ps3", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ps3_rtc_remove), +}; + +static int __init ps3_rtc_init(void) +{ + return platform_driver_probe(&ps3_rtc_driver, ps3_rtc_probe); +} + +static void __exit ps3_rtc_fini(void) +{ + platform_driver_unregister(&ps3_rtc_driver); +} + +module_init(ps3_rtc_init); +module_exit(ps3_rtc_fini); + +MODULE_AUTHOR("Sony Corporation"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ps3 RTC driver"); +MODULE_ALIAS("platform:rtc-ps3"); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c new file mode 100644 index 000000000000..b1a29bcfdf13 --- /dev/null +++ b/drivers/rtc/rtc-rx8025.c @@ -0,0 +1,688 @@ +/* + * Driver for Epson's RTC module RX-8025 SA/NB + * + * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> + * + * Copyright (C) 2005 by Digi International Inc. + * All rights reserved. + * + * Modified by fengjh at rising.com.cn + * <http://lists.lm-sensors.org/mailman/listinfo/lm-sensors> + * 2006.11 + * + * Code cleanup by Sergei Poselenov, <sposelenov@emcraft.com> + * Converted to new style by Wolfgang Grandegger <wg@grandegger.com> + * Alarm and periodic interrupt added by Dmitry Rakhchev <rda@emcraft.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bcd.h> +#include <linux/i2c.h> +#include <linux/list.h> +#include <linux/rtc.h> + +/* Register definitions */ +#define RX8025_REG_SEC 0x00 +#define RX8025_REG_MIN 0x01 +#define RX8025_REG_HOUR 0x02 +#define RX8025_REG_WDAY 0x03 +#define RX8025_REG_MDAY 0x04 +#define RX8025_REG_MONTH 0x05 +#define RX8025_REG_YEAR 0x06 +#define RX8025_REG_DIGOFF 0x07 +#define RX8025_REG_ALWMIN 0x08 +#define RX8025_REG_ALWHOUR 0x09 +#define RX8025_REG_ALWWDAY 0x0a +#define RX8025_REG_ALDMIN 0x0b +#define RX8025_REG_ALDHOUR 0x0c +/* 0x0d is reserved */ +#define RX8025_REG_CTRL1 0x0e +#define RX8025_REG_CTRL2 0x0f + +#define RX8025_BIT_CTRL1_CT (7 << 0) +/* 1 Hz periodic level irq */ +#define RX8025_BIT_CTRL1_CT_1HZ 4 +#define RX8025_BIT_CTRL1_TEST (1 << 3) +#define RX8025_BIT_CTRL1_1224 (1 << 5) +#define RX8025_BIT_CTRL1_DALE (1 << 6) +#define RX8025_BIT_CTRL1_WALE (1 << 7) + +#define RX8025_BIT_CTRL2_DAFG (1 << 0) +#define RX8025_BIT_CTRL2_WAFG (1 << 1) +#define RX8025_BIT_CTRL2_CTFG (1 << 2) +#define RX8025_BIT_CTRL2_PON (1 << 4) +#define RX8025_BIT_CTRL2_XST (1 << 5) +#define RX8025_BIT_CTRL2_VDET (1 << 6) + +/* Clock precision adjustment */ +#define RX8025_ADJ_RESOLUTION 3050 /* in ppb */ +#define RX8025_ADJ_DATA_MAX 62 +#define RX8025_ADJ_DATA_MIN -62 + +static const struct i2c_device_id rx8025_id[] = { + { "rx8025", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8025_id); + +struct rx8025_data { + struct i2c_client *client; + struct rtc_device *rtc; + struct work_struct work; + u8 ctrl1; + unsigned exiting:1; +}; + +static int rx8025_read_reg(struct i2c_client *client, int number, u8 *value) +{ + int ret = i2c_smbus_read_byte_data(client, (number << 4) | 0x08); + + if (ret < 0) { + dev_err(&client->dev, "Unable to read register #%d\n", number); + return ret; + } + + *value = ret; + return 0; +} + +static int rx8025_read_regs(struct i2c_client *client, + int number, u8 length, u8 *values) +{ + int ret = i2c_smbus_read_i2c_block_data(client, (number << 4) | 0x08, + length, values); + + if (ret != length) { + dev_err(&client->dev, "Unable to read registers #%d..#%d\n", + number, number + length - 1); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int rx8025_write_reg(struct i2c_client *client, int number, u8 value) +{ + int ret = i2c_smbus_write_byte_data(client, number << 4, value); + + if (ret) + dev_err(&client->dev, "Unable to write register #%d\n", + number); + + return ret; +} + +static int rx8025_write_regs(struct i2c_client *client, + int number, u8 length, u8 *values) +{ + int ret = i2c_smbus_write_i2c_block_data(client, (number << 4) | 0x08, + length, values); + + if (ret) + dev_err(&client->dev, "Unable to write registers #%d..#%d\n", + number, number + length - 1); + + return ret; +} + +static irqreturn_t rx8025_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + + disable_irq_nosync(irq); + schedule_work(&rx8025->work); + return IRQ_HANDLED; +} + +static void rx8025_work(struct work_struct *work) +{ + struct rx8025_data *rx8025 = container_of(work, struct rx8025_data, + work); + struct i2c_client *client = rx8025->client; + struct mutex *lock = &rx8025->rtc->ops_lock; + u8 status; + + mutex_lock(lock); + + if (rx8025_read_reg(client, RX8025_REG_CTRL2, &status)) + goto out; + + if (!(status & RX8025_BIT_CTRL2_XST)) + dev_warn(&client->dev, "Oscillation stop was detected," + "you may have to readjust the clock\n"); + + if (status & RX8025_BIT_CTRL2_CTFG) { + /* periodic */ + status &= ~RX8025_BIT_CTRL2_CTFG; + local_irq_disable(); + rtc_update_irq(rx8025->rtc, 1, RTC_PF | RTC_IRQF); + local_irq_enable(); + } + + if (status & RX8025_BIT_CTRL2_DAFG) { + /* alarm */ + status &= RX8025_BIT_CTRL2_DAFG; + if (rx8025_write_reg(client, RX8025_REG_CTRL1, + rx8025->ctrl1 & ~RX8025_BIT_CTRL1_DALE)) + goto out; + local_irq_disable(); + rtc_update_irq(rx8025->rtc, 1, RTC_AF | RTC_IRQF); + local_irq_enable(); + } + + /* acknowledge IRQ */ + rx8025_write_reg(client, RX8025_REG_CTRL2, + status | RX8025_BIT_CTRL2_XST); + +out: + if (!rx8025->exiting) + enable_irq(client->irq); + + mutex_unlock(lock); +} + +static int rx8025_get_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 date[7]; + int err; + + err = rx8025_read_regs(rx8025->client, RX8025_REG_SEC, 7, date); + if (err) + return err; + + dev_dbg(dev, "%s: read 0x%02x 0x%02x " + "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, + date[0], date[1], date[2], date[3], date[4], + date[5], date[6]); + + dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f); + dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f); + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f); + else + dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12 + + (date[RX8025_REG_HOUR] & 0x20 ? 12 : 0); + + dt->tm_mday = bcd2bin(date[RX8025_REG_MDAY] & 0x3f); + dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1; + dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]); + + if (dt->tm_year < 70) + dt->tm_year += 100; + + dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__, + dt->tm_sec, dt->tm_min, dt->tm_hour, + dt->tm_mday, dt->tm_mon, dt->tm_year); + + return rtc_valid_tm(dt); +} + +static int rx8025_set_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 date[7]; + + /* + * BUG: The HW assumes every year that is a multiple of 4 to be a leap + * year. Next time this is wrong is 2100, which will not be a leap + * year. + */ + + /* + * Here the read-only bits are written as "0". I'm not sure if that + * is sound. + */ + date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec); + date[RX8025_REG_MIN] = bin2bcd(dt->tm_min); + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour); + else + date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0) + | bin2bcd((dt->tm_hour + 11) % 12 + 1); + + date[RX8025_REG_WDAY] = bin2bcd(dt->tm_wday); + date[RX8025_REG_MDAY] = bin2bcd(dt->tm_mday); + date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1); + date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year % 100); + + dev_dbg(dev, + "%s: write 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + return rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date); +} + +static int rx8025_init_client(struct i2c_client *client, int *need_reset) +{ + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + u8 ctrl[2], ctrl2; + int need_clear = 0; + int err; + + err = rx8025_read_regs(rx8025->client, RX8025_REG_CTRL1, 2, ctrl); + if (err) + goto out; + + /* Keep test bit zero ! */ + rx8025->ctrl1 = ctrl[0] & ~RX8025_BIT_CTRL1_TEST; + + if (ctrl[1] & RX8025_BIT_CTRL2_PON) { + dev_warn(&client->dev, "power-on reset was detected, " + "you may have to readjust the clock\n"); + *need_reset = 1; + } + + if (ctrl[1] & RX8025_BIT_CTRL2_VDET) { + dev_warn(&client->dev, "a power voltage drop was detected, " + "you may have to readjust the clock\n"); + *need_reset = 1; + } + + if (!(ctrl[1] & RX8025_BIT_CTRL2_XST)) { + dev_warn(&client->dev, "Oscillation stop was detected," + "you may have to readjust the clock\n"); + *need_reset = 1; + } + + if (ctrl[1] & (RX8025_BIT_CTRL2_DAFG | RX8025_BIT_CTRL2_WAFG)) { + dev_warn(&client->dev, "Alarm was detected\n"); + need_clear = 1; + } + + if (!(ctrl[1] & RX8025_BIT_CTRL2_CTFG)) + need_clear = 1; + + if (*need_reset || need_clear) { + ctrl2 = ctrl[0]; + ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET | + RX8025_BIT_CTRL2_CTFG | RX8025_BIT_CTRL2_WAFG | + RX8025_BIT_CTRL2_DAFG); + ctrl2 |= RX8025_BIT_CTRL2_XST; + + err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2); + } +out: + return err; +} + +/* Alarm support */ +static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + struct i2c_client *client = rx8025->client; + u8 ctrl2, ald[2]; + int err; + + if (client->irq <= 0) + return -EINVAL; + + err = rx8025_read_regs(client, RX8025_REG_ALDMIN, 2, ald); + if (err) + return err; + + err = rx8025_read_reg(client, RX8025_REG_CTRL2, &ctrl2); + if (err) + return err; + + dev_dbg(dev, "%s: read alarm 0x%02x 0x%02x ctrl2 %02x\n", + __func__, ald[0], ald[1], ctrl2); + + /* Hardware alarms precision is 1 minute! */ + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + t->time.tm_hour = bcd2bin(ald[1] & 0x3f); + else + t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12 + + (ald[1] & 0x20 ? 12 : 0); + + t->time.tm_wday = -1; + t->time.tm_mday = -1; + t->time.tm_mon = -1; + t->time.tm_year = -1; + + dev_dbg(dev, "%s: date: %ds %dm %dh %dmd %dm %dy\n", + __func__, + t->time.tm_sec, t->time.tm_min, t->time.tm_hour, + t->time.tm_mday, t->time.tm_mon, t->time.tm_year); + t->enabled = !!(rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE); + t->pending = (ctrl2 & RX8025_BIT_CTRL2_DAFG) && t->enabled; + + return err; +} + +static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 ald[2]; + int err; + + if (client->irq <= 0) + return -EINVAL; + + /* Hardware alarm precision is 1 minute! */ + ald[0] = bin2bcd(t->time.tm_min); + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224) + ald[1] = bin2bcd(t->time.tm_hour); + else + ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0) + | bin2bcd((t->time.tm_hour + 11) % 12 + 1); + + dev_dbg(dev, "%s: write 0x%02x 0x%02x\n", __func__, ald[0], ald[1]); + + if (rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE) { + rx8025->ctrl1 &= ~RX8025_BIT_CTRL1_DALE; + err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + err = rx8025_write_regs(rx8025->client, RX8025_REG_ALDMIN, 2, ald); + if (err) + return err; + + if (t->enabled) { + rx8025->ctrl1 |= RX8025_BIT_CTRL1_DALE; + err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + + return 0; +} + +static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 ctrl1; + int err; + + ctrl1 = rx8025->ctrl1; + if (enabled) + ctrl1 |= RX8025_BIT_CTRL1_DALE; + else + ctrl1 &= ~RX8025_BIT_CTRL1_DALE; + + if (ctrl1 != rx8025->ctrl1) { + rx8025->ctrl1 = ctrl1; + err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + return 0; +} + +static int rx8025_irq_set_state(struct device *dev, int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + int ctrl1; + int err; + + if (client->irq <= 0) + return -ENXIO; + + ctrl1 = rx8025->ctrl1 & ~RX8025_BIT_CTRL1_CT; + if (enabled) + ctrl1 |= RX8025_BIT_CTRL1_CT_1HZ; + if (ctrl1 != rx8025->ctrl1) { + rx8025->ctrl1 = ctrl1; + err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1, + rx8025->ctrl1); + if (err) + return err; + } + + return 0; +} + +static struct rtc_class_ops rx8025_rtc_ops = { + .read_time = rx8025_get_time, + .set_time = rx8025_set_time, + .read_alarm = rx8025_read_alarm, + .set_alarm = rx8025_set_alarm, + .alarm_irq_enable = rx8025_alarm_irq_enable, + .irq_set_state = rx8025_irq_set_state, +}; + +/* + * Clock precision adjustment support + * + * According to the RX8025 SA/NB application manual the frequency and + * temperature charateristics can be approximated using the following + * equation: + * + * df = a * (ut - t)**2 + * + * df: Frequency deviation in any temperature + * a : Coefficient = (-35 +-5) * 10**-9 + * ut: Ultimate temperature in degree = +25 +-5 degree + * t : Any temperature in degree + * + * Note that the clock adjustment in ppb must be entered (which is + * the negative value of the deviation). + */ +static int rx8025_get_clock_adjust(struct device *dev, int *adj) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 digoff; + int err; + + err = rx8025_read_reg(client, RX8025_REG_DIGOFF, &digoff); + if (err) + return err; + + *adj = digoff >= 64 ? digoff - 128 : digoff; + if (*adj > 0) + (*adj)--; + *adj *= -RX8025_ADJ_RESOLUTION; + + return 0; +} + +static int rx8025_set_clock_adjust(struct device *dev, int adj) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 digoff; + int err; + + adj /= -RX8025_ADJ_RESOLUTION; + if (adj > RX8025_ADJ_DATA_MAX) + adj = RX8025_ADJ_DATA_MAX; + else if (adj < RX8025_ADJ_DATA_MIN) + adj = RX8025_ADJ_DATA_MIN; + else if (adj > 0) + adj++; + else if (adj < 0) + adj += 128; + digoff = adj; + + err = rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff); + if (err) + return err; + + dev_dbg(dev, "%s: write 0x%02x\n", __func__, digoff); + + return 0; +} + +static ssize_t rx8025_sysfs_show_clock_adjust(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err, adj; + + err = rx8025_get_clock_adjust(dev, &adj); + if (err) + return err; + + return sprintf(buf, "%d\n", adj); +} + +static ssize_t rx8025_sysfs_store_clock_adjust(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int adj, err; + + if (sscanf(buf, "%i", &adj) != 1) + return -EINVAL; + + err = rx8025_set_clock_adjust(dev, adj); + + return err ? err : count; +} + +static DEVICE_ATTR(clock_adjust_ppb, S_IRUGO | S_IWUSR, + rx8025_sysfs_show_clock_adjust, + rx8025_sysfs_store_clock_adjust); + +static int rx8025_sysfs_register(struct device *dev) +{ + return device_create_file(dev, &dev_attr_clock_adjust_ppb); +} + +static void rx8025_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_clock_adjust_ppb); +} + +static int __devinit rx8025_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rx8025_data *rx8025; + int err, need_reset = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, + "doesn't support required functionality\n"); + err = -EIO; + goto errout; + } + + rx8025 = kzalloc(sizeof(*rx8025), GFP_KERNEL); + if (!rx8025) { + dev_err(&adapter->dev, "failed to alloc memory\n"); + err = -ENOMEM; + goto errout; + } + + rx8025->client = client; + i2c_set_clientdata(client, rx8025); + INIT_WORK(&rx8025->work, rx8025_work); + + err = rx8025_init_client(client, &need_reset); + if (err) + goto errout_free; + + if (need_reset) { + struct rtc_time tm; + dev_info(&client->dev, + "bad conditions detected, resetting date\n"); + rtc_time_to_tm(0, &tm); /* 1970/1/1 */ + rx8025_set_time(&client->dev, &tm); + } + + rx8025->rtc = rtc_device_register(client->name, &client->dev, + &rx8025_rtc_ops, THIS_MODULE); + if (IS_ERR(rx8025->rtc)) { + err = PTR_ERR(rx8025->rtc); + dev_err(&client->dev, "unable to register the class device\n"); + goto errout_free; + } + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = request_irq(client->irq, rx8025_irq, + 0, "rx8025", client); + if (err) { + dev_err(&client->dev, "unable to request IRQ\n"); + goto errout_reg; + } + } + + rx8025->rtc->irq_freq = 1; + rx8025->rtc->max_user_freq = 1; + + err = rx8025_sysfs_register(&client->dev); + if (err) + goto errout_irq; + + return 0; + +errout_irq: + if (client->irq > 0) + free_irq(client->irq, client); + +errout_reg: + rtc_device_unregister(rx8025->rtc); + +errout_free: + i2c_set_clientdata(client, NULL); + kfree(rx8025); + +errout: + dev_err(&adapter->dev, "probing for rx8025 failed\n"); + return err; +} + +static int __devexit rx8025_remove(struct i2c_client *client) +{ + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + struct mutex *lock = &rx8025->rtc->ops_lock; + + if (client->irq > 0) { + mutex_lock(lock); + rx8025->exiting = 1; + mutex_unlock(lock); + + free_irq(client->irq, client); + flush_scheduled_work(); + } + + rx8025_sysfs_unregister(&client->dev); + rtc_device_unregister(rx8025->rtc); + i2c_set_clientdata(client, NULL); + kfree(rx8025); + return 0; +} + +static struct i2c_driver rx8025_driver = { + .driver = { + .name = "rtc-rx8025", + .owner = THIS_MODULE, + }, + .probe = rx8025_probe, + .remove = __devexit_p(rx8025_remove), + .id_table = rx8025_id, +}; + +static int __init rx8025_init(void) +{ + return i2c_add_driver(&rx8025_driver); +} + +static void __exit rx8025_exit(void) +{ + i2c_del_driver(&rx8025_driver); +} + +MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); +MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver"); +MODULE_LICENSE("GPL"); + +module_init(rx8025_init); +module_exit(rx8025_exit); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index d26a5f82aaba..4f247e4dd3f9 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -35,7 +35,8 @@ #include <asm/irq.h> #ifdef CONFIG_ARCH_PXA -#include <mach/pxa-regs.h> +#include <mach/regs-rtc.h> +#include <mach/regs-ost.h> #endif #define RTC_DEF_DIVIDER 32768 - 1 diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 1c3fc6b428e9..d7310adb7152 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -1,7 +1,7 @@ /* * SuperH On-Chip RTC Support * - * Copyright (C) 2006, 2007, 2008 Paul Mundt + * Copyright (C) 2006 - 2009 Paul Mundt * Copyright (C) 2006 Jamie Lenehan * Copyright (C) 2008 Angelo Castello * @@ -25,10 +25,11 @@ #include <linux/spinlock.h> #include <linux/io.h> #include <linux/log2.h> +#include <linux/clk.h> #include <asm/rtc.h> #define DRV_NAME "sh-rtc" -#define DRV_VERSION "0.2.0" +#define DRV_VERSION "0.2.2" #define RTC_REG(r) ((r) * rtc_reg_size) @@ -87,68 +88,64 @@ #define RCR2_START 0x01 /* Start bit */ struct sh_rtc { - void __iomem *regbase; - unsigned long regsize; - struct resource *res; - int alarm_irq; - int periodic_irq; - int carry_irq; - struct rtc_device *rtc_dev; - spinlock_t lock; - unsigned long capabilities; /* See asm-sh/rtc.h for cap bits */ - unsigned short periodic_freq; + void __iomem *regbase; + unsigned long regsize; + struct resource *res; + int alarm_irq; + int periodic_irq; + int carry_irq; + struct clk *clk; + struct rtc_device *rtc_dev; + spinlock_t lock; + unsigned long capabilities; /* See asm/rtc.h for cap bits */ + unsigned short periodic_freq; }; -static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) +static int __sh_rtc_interrupt(struct sh_rtc *rtc) { - struct sh_rtc *rtc = dev_id; - unsigned int tmp; - - spin_lock(&rtc->lock); + unsigned int tmp, pending; tmp = readb(rtc->regbase + RCR1); + pending = tmp & RCR1_CF; tmp &= ~RCR1_CF; writeb(tmp, rtc->regbase + RCR1); /* Users have requested One x Second IRQ */ - if (rtc->periodic_freq & PF_OXS) + if (pending && rtc->periodic_freq & PF_OXS) rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); - spin_unlock(&rtc->lock); - - return IRQ_HANDLED; + return pending; } -static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) +static int __sh_rtc_alarm(struct sh_rtc *rtc) { - struct sh_rtc *rtc = dev_id; - unsigned int tmp; - - spin_lock(&rtc->lock); + unsigned int tmp, pending; tmp = readb(rtc->regbase + RCR1); + pending = tmp & RCR1_AF; tmp &= ~(RCR1_AF | RCR1_AIE); - writeb(tmp, rtc->regbase + RCR1); - - rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + writeb(tmp, rtc->regbase + RCR1); - spin_unlock(&rtc->lock); + if (pending) + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); - return IRQ_HANDLED; + return pending; } -static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) +static int __sh_rtc_periodic(struct sh_rtc *rtc) { - struct sh_rtc *rtc = dev_id; struct rtc_device *rtc_dev = rtc->rtc_dev; - unsigned int tmp; - - spin_lock(&rtc->lock); + struct rtc_task *irq_task; + unsigned int tmp, pending; tmp = readb(rtc->regbase + RCR2); + pending = tmp & RCR2_PEF; tmp &= ~RCR2_PEF; writeb(tmp, rtc->regbase + RCR2); + if (!pending) + return 0; + /* Half period enabled than one skipped and the next notified */ if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) rtc->periodic_freq &= ~PF_COUNT; @@ -157,16 +154,65 @@ static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) rtc->periodic_freq |= PF_COUNT; if (rtc->periodic_freq & PF_KOU) { spin_lock(&rtc_dev->irq_task_lock); - if (rtc_dev->irq_task) - rtc_dev->irq_task->func(rtc_dev->irq_task->private_data); + irq_task = rtc_dev->irq_task; + if (irq_task) + irq_task->func(irq_task->private_data); spin_unlock(&rtc_dev->irq_task_lock); } else rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); } + return pending; +} + +static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) +{ + struct sh_rtc *rtc = dev_id; + int ret; + + spin_lock(&rtc->lock); + ret = __sh_rtc_interrupt(rtc); spin_unlock(&rtc->lock); - return IRQ_HANDLED; + return IRQ_RETVAL(ret); +} + +static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) +{ + struct sh_rtc *rtc = dev_id; + int ret; + + spin_lock(&rtc->lock); + ret = __sh_rtc_alarm(rtc); + spin_unlock(&rtc->lock); + + return IRQ_RETVAL(ret); +} + +static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) +{ + struct sh_rtc *rtc = dev_id; + int ret; + + spin_lock(&rtc->lock); + ret = __sh_rtc_periodic(rtc); + spin_unlock(&rtc->lock); + + return IRQ_RETVAL(ret); +} + +static irqreturn_t sh_rtc_shared(int irq, void *dev_id) +{ + struct sh_rtc *rtc = dev_id; + int ret; + + spin_lock(&rtc->lock); + ret = __sh_rtc_interrupt(rtc); + ret |= __sh_rtc_alarm(rtc); + ret |= __sh_rtc_periodic(rtc); + spin_unlock(&rtc->lock); + + return IRQ_RETVAL(ret); } static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) @@ -250,10 +296,10 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable) tmp = readb(rtc->regbase + RCR1); - if (!enable) - tmp &= ~RCR1_AIE; - else + if (enable) tmp |= RCR1_AIE; + else + tmp &= ~RCR1_AIE; writeb(tmp, rtc->regbase + RCR1); @@ -275,6 +321,25 @@ static int sh_rtc_proc(struct device *dev, struct seq_file *seq) return 0; } +static inline void sh_rtc_setcie(struct device *dev, unsigned int enable) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int tmp; + + spin_lock_irq(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + + if (!enable) + tmp &= ~RCR1_CIE; + else + tmp |= RCR1_CIE; + + writeb(tmp, rtc->regbase + RCR1); + + spin_unlock_irq(&rtc->lock); +} + static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { struct sh_rtc *rtc = dev_get_drvdata(dev); @@ -291,9 +356,11 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) break; case RTC_UIE_OFF: rtc->periodic_freq &= ~PF_OXS; + sh_rtc_setcie(dev, 0); break; case RTC_UIE_ON: rtc->periodic_freq |= PF_OXS; + sh_rtc_setcie(dev, 1); break; case RTC_IRQP_READ: ret = put_user(rtc->rtc_dev->irq_freq, @@ -356,18 +423,17 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_sec--; #endif + /* only keep the carry interrupt enabled if UIE is on */ + if (!(rtc->periodic_freq & PF_OXS)) + sh_rtc_setcie(dev, 0); + dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); - if (rtc_valid_tm(tm) < 0) { - dev_err(dev, "invalid date\n"); - rtc_time_to_tm(0, tm); - } - - return 0; + return rtc_valid_tm(tm); } static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -554,6 +620,7 @@ static int sh_rtc_irq_set_freq(struct device *dev, int freq) { if (!is_power_of_2(freq)) return -EINVAL; + return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq); } @@ -572,8 +639,9 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) { struct sh_rtc *rtc; struct resource *res; - unsigned int tmp; - int ret; + struct rtc_time r; + char clk_name[6]; + int clk_id, ret; rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); if (unlikely(!rtc)) @@ -585,26 +653,13 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (unlikely(ret <= 0)) { ret = -ENOENT; - dev_err(&pdev->dev, "No IRQ for period\n"); - goto err_badres; - } - rtc->periodic_irq = ret; - - ret = platform_get_irq(pdev, 1); - if (unlikely(ret <= 0)) { - ret = -ENOENT; - dev_err(&pdev->dev, "No IRQ for carry\n"); + dev_err(&pdev->dev, "No IRQ resource\n"); goto err_badres; } - rtc->carry_irq = ret; - ret = platform_get_irq(pdev, 2); - if (unlikely(ret <= 0)) { - ret = -ENOENT; - dev_err(&pdev->dev, "No IRQ for alarm\n"); - goto err_badres; - } - rtc->alarm_irq = ret; + rtc->periodic_irq = ret; + rtc->carry_irq = platform_get_irq(pdev, 1); + rtc->alarm_irq = platform_get_irq(pdev, 2); res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (unlikely(res == NULL)) { @@ -613,7 +668,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) goto err_badres; } - rtc->regsize = res->end - res->start + 1; + rtc->regsize = resource_size(res); rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name); if (unlikely(!rtc->res)) { @@ -627,6 +682,26 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) goto err_badmap; } + clk_id = pdev->id; + /* With a single device, the clock id is still "rtc0" */ + if (clk_id < 0) + clk_id = 0; + + snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id); + + rtc->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(rtc->clk)) { + /* + * No error handling for rtc->clk intentionally, not all + * platforms will have a unique clock for the RTC, and + * the clk API can handle the struct clk pointer being + * NULL. + */ + rtc->clk = NULL; + } + + clk_enable(rtc->clk); + rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, &sh_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc_dev)) { @@ -646,50 +721,71 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) } rtc->rtc_dev->max_user_freq = 256; - rtc->rtc_dev->irq_freq = 1; - rtc->periodic_freq = 0x60; platform_set_drvdata(pdev, rtc); - /* register periodic/carry/alarm irqs */ - ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, - "sh-rtc period", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request period IRQ failed with %d, IRQ %d\n", ret, - rtc->periodic_irq); - goto err_unmap; - } + if (rtc->carry_irq <= 0) { + /* register shared periodic/carry/alarm irq */ + ret = request_irq(rtc->periodic_irq, sh_rtc_shared, + IRQF_DISABLED, "sh-rtc", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request IRQ failed with %d, IRQ %d\n", ret, + rtc->periodic_irq); + goto err_unmap; + } + } else { + /* register periodic/carry/alarm irqs */ + ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, + IRQF_DISABLED, "sh-rtc period", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request period IRQ failed with %d, IRQ %d\n", + ret, rtc->periodic_irq); + goto err_unmap; + } - ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, - "sh-rtc carry", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request carry IRQ failed with %d, IRQ %d\n", ret, - rtc->carry_irq); - free_irq(rtc->periodic_irq, rtc); - goto err_unmap; - } + ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, + IRQF_DISABLED, "sh-rtc carry", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request carry IRQ failed with %d, IRQ %d\n", + ret, rtc->carry_irq); + free_irq(rtc->periodic_irq, rtc); + goto err_unmap; + } - ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, - "sh-rtc alarm", rtc); - if (unlikely(ret)) { - dev_err(&pdev->dev, - "request alarm IRQ failed with %d, IRQ %d\n", ret, - rtc->alarm_irq); - free_irq(rtc->carry_irq, rtc); - free_irq(rtc->periodic_irq, rtc); - goto err_unmap; + ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, + IRQF_DISABLED, "sh-rtc alarm", rtc); + if (unlikely(ret)) { + dev_err(&pdev->dev, + "request alarm IRQ failed with %d, IRQ %d\n", + ret, rtc->alarm_irq); + free_irq(rtc->carry_irq, rtc); + free_irq(rtc->periodic_irq, rtc); + goto err_unmap; + } } - tmp = readb(rtc->regbase + RCR1); - tmp &= ~RCR1_CF; - tmp |= RCR1_CIE; - writeb(tmp, rtc->regbase + RCR1); + /* everything disabled by default */ + rtc->periodic_freq = 0; + rtc->rtc_dev->irq_freq = 0; + sh_rtc_setpie(&pdev->dev, 0); + sh_rtc_setaie(&pdev->dev, 0); + sh_rtc_setcie(&pdev->dev, 0); + /* reset rtc to epoch 0 if time is invalid */ + if (rtc_read_time(rtc->rtc_dev, &r) < 0) { + rtc_time_to_tm(0, &r); + rtc_set_time(rtc->rtc_dev, &r); + } + + device_init_wakeup(&pdev->dev, 1); return 0; err_unmap: + clk_disable(rtc->clk); + clk_put(rtc->clk); iounmap(rtc->regbase); err_badmap: release_resource(rtc->res); @@ -708,25 +804,68 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) sh_rtc_setpie(&pdev->dev, 0); sh_rtc_setaie(&pdev->dev, 0); + sh_rtc_setcie(&pdev->dev, 0); - free_irq(rtc->carry_irq, rtc); free_irq(rtc->periodic_irq, rtc); - free_irq(rtc->alarm_irq, rtc); + + if (rtc->carry_irq > 0) { + free_irq(rtc->carry_irq, rtc); + free_irq(rtc->alarm_irq, rtc); + } release_resource(rtc->res); iounmap(rtc->regbase); + clk_disable(rtc->clk); + clk_put(rtc->clk); + platform_set_drvdata(pdev, NULL); kfree(rtc); return 0; } + +static void sh_rtc_set_irq_wake(struct device *dev, int enabled) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + + set_irq_wake(rtc->periodic_irq, enabled); + + if (rtc->carry_irq > 0) { + set_irq_wake(rtc->carry_irq, enabled); + set_irq_wake(rtc->alarm_irq, enabled); + } +} + +static int sh_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) + sh_rtc_set_irq_wake(dev, 1); + + return 0; +} + +static int sh_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) + sh_rtc_set_irq_wake(dev, 0); + + return 0; +} + +static struct dev_pm_ops sh_rtc_dev_pm_ops = { + .suspend = sh_rtc_suspend, + .resume = sh_rtc_resume, +}; + static struct platform_driver sh_rtc_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = &sh_rtc_dev_pm_ops, }, .probe = sh_rtc_probe, .remove = __devexit_p(sh_rtc_remove), diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c index e478280ff628..51725f7755b0 100644 --- a/drivers/rtc/rtc-test.c +++ b/drivers/rtc/rtc-test.c @@ -93,7 +93,6 @@ static ssize_t test_irq_store(struct device *dev, struct rtc_device *rtc = platform_get_drvdata(plat_dev); retval = count; - local_irq_disable(); if (strncmp(buf, "tick", 4) == 0) rtc_update_irq(rtc, 1, RTC_PF | RTC_IRQF); else if (strncmp(buf, "alarm", 5) == 0) @@ -102,7 +101,6 @@ static ssize_t test_irq_store(struct device *dev, rtc_update_irq(rtc, 1, RTC_UF | RTC_IRQF); else retval = -EINVAL; - local_irq_enable(); return retval; } diff --git a/drivers/rtc/rtc-twl4030.c b/drivers/rtc/rtc-twl4030.c index ad35f76c46b7..9c8c70c497dc 100644 --- a/drivers/rtc/rtc-twl4030.c +++ b/drivers/rtc/rtc-twl4030.c @@ -426,7 +426,7 @@ static int __devinit twl4030_rtc_probe(struct platform_device *pdev) ret = request_irq(irq, twl4030_rtc_interrupt, IRQF_TRIGGER_RISING, - rtc->dev.bus_id, rtc); + dev_name(&rtc->dev), rtc); if (ret < 0) { dev_err(&pdev->dev, "IRQ is not free.\n"); goto out1; @@ -495,9 +495,7 @@ static int twl4030_rtc_suspend(struct platform_device *pdev, pm_message_t state) { irqstat = rtc_irq_bits; - /* REVISIT alarm may need to wake us from sleep */ - mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M | - BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); return 0; } diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 4ee4857ff207..4a6ed1104fbb 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -261,10 +261,8 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev) tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP); if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt, - IRQF_DISABLED | IRQF_SHARED, - pdev->name, &pdev->dev) < 0) { + IRQF_DISABLED, pdev->name, &pdev->dev) < 0) return -EBUSY; - } rtc = rtc_device_register(pdev->name, &pdev->dev, &tx4939_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index 14d4f036a768..ad164056feb6 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -27,17 +27,162 @@ #include <linux/bcd.h> #include <linux/rtc-v3020.h> #include <linux/delay.h> +#include <linux/gpio.h> -#include <asm/io.h> +#include <linux/io.h> #undef DEBUG +struct v3020; + +struct v3020_chip_ops { + int (*map_io)(struct v3020 *chip, struct platform_device *pdev, + struct v3020_platform_data *pdata); + void (*unmap_io)(struct v3020 *chip); + unsigned char (*read_bit)(struct v3020 *chip); + void (*write_bit)(struct v3020 *chip, unsigned char bit); +}; + +#define V3020_CS 0 +#define V3020_WR 1 +#define V3020_RD 2 +#define V3020_IO 3 + +struct v3020_gpio { + const char *name; + unsigned int gpio; +}; + struct v3020 { + /* MMIO access */ void __iomem *ioaddress; int leftshift; + + /* GPIO access */ + struct v3020_gpio *gpio; + + struct v3020_chip_ops *ops; + struct rtc_device *rtc; }; + +static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev, + struct v3020_platform_data *pdata) +{ + if (pdev->num_resources != 1) + return -EBUSY; + + if (pdev->resource[0].flags != IORESOURCE_MEM) + return -EBUSY; + + chip->leftshift = pdata->leftshift; + chip->ioaddress = ioremap(pdev->resource[0].start, 1); + if (chip->ioaddress == NULL) + return -EBUSY; + + return 0; +} + +static void v3020_mmio_unmap(struct v3020 *chip) +{ + iounmap(chip->ioaddress); +} + +static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) +{ + writel(bit << chip->leftshift, chip->ioaddress); +} + +static unsigned char v3020_mmio_read_bit(struct v3020 *chip) +{ + return readl(chip->ioaddress) & (1 << chip->leftshift); +} + +static struct v3020_chip_ops v3020_mmio_ops = { + .map_io = v3020_mmio_map, + .unmap_io = v3020_mmio_unmap, + .read_bit = v3020_mmio_read_bit, + .write_bit = v3020_mmio_write_bit, +}; + +static struct v3020_gpio v3020_gpio[] = { + { "RTC CS", 0 }, + { "RTC WR", 0 }, + { "RTC RD", 0 }, + { "RTC IO", 0 }, +}; + +static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev, + struct v3020_platform_data *pdata) +{ + int i, err; + + v3020_gpio[V3020_CS].gpio = pdata->gpio_cs; + v3020_gpio[V3020_WR].gpio = pdata->gpio_wr; + v3020_gpio[V3020_RD].gpio = pdata->gpio_rd; + v3020_gpio[V3020_IO].gpio = pdata->gpio_io; + + for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) { + err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name); + if (err) + goto err_request; + + gpio_direction_output(v3020_gpio[i].gpio, 1); + } + + chip->gpio = v3020_gpio; + + return 0; + +err_request: + while (--i >= 0) + gpio_free(v3020_gpio[i].gpio); + + return err; +} + +static void v3020_gpio_unmap(struct v3020 *chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) + gpio_free(v3020_gpio[i].gpio); +} + +static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit) +{ + gpio_direction_output(chip->gpio[V3020_IO].gpio, bit); + gpio_set_value(chip->gpio[V3020_CS].gpio, 0); + gpio_set_value(chip->gpio[V3020_WR].gpio, 0); + udelay(1); + gpio_set_value(chip->gpio[V3020_WR].gpio, 1); + gpio_set_value(chip->gpio[V3020_CS].gpio, 1); +} + +static unsigned char v3020_gpio_read_bit(struct v3020 *chip) +{ + int bit; + + gpio_direction_input(chip->gpio[V3020_IO].gpio); + gpio_set_value(chip->gpio[V3020_CS].gpio, 0); + gpio_set_value(chip->gpio[V3020_RD].gpio, 0); + udelay(1); + bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio); + udelay(1); + gpio_set_value(chip->gpio[V3020_RD].gpio, 1); + gpio_set_value(chip->gpio[V3020_CS].gpio, 1); + + return bit; +} + +static struct v3020_chip_ops v3020_gpio_ops = { + .map_io = v3020_gpio_map, + .unmap_io = v3020_gpio_unmap, + .read_bit = v3020_gpio_read_bit, + .write_bit = v3020_gpio_write_bit, +}; + static void v3020_set_reg(struct v3020 *chip, unsigned char address, unsigned char data) { @@ -46,7 +191,7 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address, tmp = address; for (i = 0; i < 4; i++) { - writel((tmp & 1) << chip->leftshift, chip->ioaddress); + chip->ops->write_bit(chip, (tmp & 1)); tmp >>= 1; udelay(1); } @@ -54,7 +199,7 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address, /* Commands dont have data */ if (!V3020_IS_COMMAND(address)) { for (i = 0; i < 8; i++) { - writel((data & 1) << chip->leftshift, chip->ioaddress); + chip->ops->write_bit(chip, (data & 1)); data >>= 1; udelay(1); } @@ -63,18 +208,18 @@ static void v3020_set_reg(struct v3020 *chip, unsigned char address, static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address) { - unsigned int data=0; + unsigned int data = 0; int i; for (i = 0; i < 4; i++) { - writel((address & 1) << chip->leftshift, chip->ioaddress); + chip->ops->write_bit(chip, (address & 1)); address >>= 1; udelay(1); } for (i = 0; i < 8; i++) { data >>= 1; - if (readl(chip->ioaddress) & (1 << chip->leftshift)) + if (chip->ops->read_bit(chip)) data |= 0x80; udelay(1); } @@ -106,16 +251,14 @@ static int v3020_read_time(struct device *dev, struct rtc_time *dt) tmp = v3020_get_reg(chip, V3020_YEAR); dt->tm_year = bcd2bin(tmp)+100; -#ifdef DEBUG - printk("\n%s : Read RTC values\n",__func__); - printk("tm_hour: %i\n",dt->tm_hour); - printk("tm_min : %i\n",dt->tm_min); - printk("tm_sec : %i\n",dt->tm_sec); - printk("tm_year: %i\n",dt->tm_year); - printk("tm_mon : %i\n",dt->tm_mon); - printk("tm_mday: %i\n",dt->tm_mday); - printk("tm_wday: %i\n",dt->tm_wday); -#endif + 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_year: %i\n", dt->tm_year); + dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon); + dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); return 0; } @@ -125,15 +268,13 @@ static int v3020_set_time(struct device *dev, struct rtc_time *dt) { struct v3020 *chip = dev_get_drvdata(dev); -#ifdef DEBUG - printk("\n%s : Setting RTC values\n",__func__); - printk("tm_sec : %i\n",dt->tm_sec); - printk("tm_min : %i\n",dt->tm_min); - printk("tm_hour: %i\n",dt->tm_hour); - printk("tm_mday: %i\n",dt->tm_mday); - printk("tm_wday: %i\n",dt->tm_wday); - printk("tm_year: %i\n",dt->tm_year); -#endif + 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_mday: %i\n", dt->tm_mday); + dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday); + dev_dbg(dev, "tm_year: %i\n", dt->tm_year); /* Write all the values to ram... */ v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); @@ -168,30 +309,28 @@ static int rtc_probe(struct platform_device *pdev) int i; int temp; - if (pdev->num_resources != 1) - return -EBUSY; - - if (pdev->resource[0].flags != IORESOURCE_MEM) - return -EBUSY; - chip = kzalloc(sizeof *chip, GFP_KERNEL); if (!chip) return -ENOMEM; - chip->leftshift = pdata->leftshift; - chip->ioaddress = ioremap(pdev->resource[0].start, 1); - if (chip->ioaddress == NULL) + if (pdata->use_gpio) + chip->ops = &v3020_gpio_ops; + else + chip->ops = &v3020_mmio_ops; + + retval = chip->ops->map_io(chip, pdev, pdata); + if (retval) goto err_chip; /* Make sure the v3020 expects a communication cycle * by reading 8 times */ for (i = 0; i < 8; i++) - temp = readl(chip->ioaddress); + temp = chip->ops->read_bit(chip); /* Test chip by doing a write/read sequence * to the chip ram */ v3020_set_reg(chip, V3020_SECONDS, 0x33); - if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) { + if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) { retval = -ENODEV; goto err_io; } @@ -200,10 +339,17 @@ static int rtc_probe(struct platform_device *pdev) * are all disabled */ v3020_set_reg(chip, V3020_STATUS_0, 0x0); - dev_info(&pdev->dev, "Chip available at physical address 0x%llx," - "data connected to D%d\n", - (unsigned long long)pdev->resource[0].start, - chip->leftshift); + if (pdata->use_gpio) + dev_info(&pdev->dev, "Chip available at GPIOs " + "%d, %d, %d, %d\n", + chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio, + chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio); + else + dev_info(&pdev->dev, "Chip available at " + "physical address 0x%llx," + "data connected to D%d\n", + (unsigned long long)pdev->resource[0].start, + chip->leftshift); platform_set_drvdata(pdev, chip); @@ -218,7 +364,7 @@ static int rtc_probe(struct platform_device *pdev) return 0; err_io: - iounmap(chip->ioaddress); + chip->ops->unmap_io(chip); err_chip: kfree(chip); @@ -233,7 +379,7 @@ static int rtc_remove(struct platform_device *dev) if (rtc) rtc_device_unregister(rtc); - iounmap(chip->ioaddress); + chip->ops->unmap_io(chip); kfree(chip); return 0; diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index f11297aff854..2c839d0d21bd 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -1,7 +1,7 @@ /* * Driver for NEC VR4100 series Real Time Clock unit. * - * Copyright (C) 2003-2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org> * * 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 @@ -33,7 +33,7 @@ #include <asm/io.h> #include <asm/uaccess.h> -MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>"); +MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); MODULE_DESCRIPTION("NEC VR4100 series RTC driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 5c5e3aa91385..c91edc572eb6 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -122,7 +122,7 @@ static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm) do { rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); schedule_timeout_uninterruptible(msecs_to_jiffies(1)); - } while (retries-- && !(rtc_ctrl & WM8350_RTC_STS)); + } while (--retries && !(rtc_ctrl & WM8350_RTC_STS)); if (!retries) { dev_err(dev, "timed out on set confirmation\n"); @@ -236,6 +236,17 @@ static int wm8350_rtc_start_alarm(struct wm8350 *wm8350) return 0; } +static int wm8350_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + if (enabled) + return wm8350_rtc_start_alarm(wm8350); + else + return wm8350_rtc_stop_alarm(wm8350); +} + static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct wm8350 *wm8350 = dev_get_drvdata(dev); @@ -291,30 +302,15 @@ static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; } -/* - * Handle commands from user-space - */ -static int wm8350_rtc_ioctl(struct device *dev, unsigned int cmd, - unsigned long arg) +static int wm8350_rtc_update_irq_enable(struct device *dev, + unsigned int enabled) { struct wm8350 *wm8350 = dev_get_drvdata(dev); - switch (cmd) { - case RTC_AIE_OFF: - return wm8350_rtc_stop_alarm(wm8350); - case RTC_AIE_ON: - return wm8350_rtc_start_alarm(wm8350); - - case RTC_UIE_OFF: - wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); - break; - case RTC_UIE_ON: + if (enabled) wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC); - break; - - default: - return -ENOIOCTLCMD; - } + else + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); return 0; } @@ -345,11 +341,12 @@ static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq, } static const struct rtc_class_ops wm8350_rtc_ops = { - .ioctl = wm8350_rtc_ioctl, .read_time = wm8350_rtc_readtime, .set_time = wm8350_rtc_settime, .read_alarm = wm8350_rtc_readalarm, .set_alarm = wm8350_rtc_setalarm, + .alarm_irq_enable = wm8350_rtc_alarm_irq_enable, + .update_irq_enable = wm8350_rtc_update_irq_enable, }; #ifdef CONFIG_PM @@ -440,7 +437,7 @@ static int wm8350_rtc_probe(struct platform_device *pdev) do { timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); - } while (timectl & WM8350_RTC_STS && retries--); + } while (timectl & WM8350_RTC_STS && --retries); if (retries == 0) { dev_err(&pdev->dev, "failed to start: timeout\n"); |
