diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 2 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c (renamed from drivers/rtc/rtc-at91.c) | 0 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1672.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-lib.c | 81 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-rs5c372.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-sh.c | 245 | ||||
-rw-r--r-- | drivers/rtc/rtc-x1205.c | 10 |
9 files changed, 304 insertions, 46 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 2a63ab2b47f4..09660e2ab051 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -288,7 +288,7 @@ config RTC_DRV_PL031 To compile this driver as a module, choose M here: the module will be called rtc-pl031. -config RTC_DRV_AT91 +config RTC_DRV_AT91RM9200 tristate "AT91RM9200" depends on RTC_CLASS && ARCH_AT91RM9200 help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index bd4c45d333f0..e6beedacc966 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -35,5 +35,5 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o -obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o +obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91rm9200.c index 5c8addcaf1fb..5c8addcaf1fb 100644 --- a/drivers/rtc/rtc-at91.c +++ b/drivers/rtc/rtc-at91rm9200.c diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index dfef1637bfb8..205fa28593b7 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -199,7 +199,7 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) struct i2c_client *client; struct rtc_device *rtc; - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__); if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { err = -ENODEV; diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c index ba795a4db1e9..7bbc26a34bd2 100644 --- a/drivers/rtc/rtc-lib.c +++ b/drivers/rtc/rtc-lib.c @@ -117,4 +117,85 @@ int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) } EXPORT_SYMBOL(rtc_tm_to_time); + +/* Merge the valid (i.e. non-negative) fields of alarm into the current + * time. If the valid alarm fields are earlier than the equivalent + * fields in the time, carry one into the least significant invalid + * field, so that the alarm expiry is in the future. It assumes that the + * least significant invalid field is more significant than the most + * significant valid field, and that the seconds field is valid. + * + * This is used by alarms that take relative (rather than absolute) + * times, and/or have a simple binary second counter instead of + * day/hour/minute/sec registers. + */ +void rtc_merge_alarm(struct rtc_time *now, struct rtc_time *alarm) +{ + int *alarmp = &alarm->tm_sec; + int *timep = &now->tm_sec; + int carry_into, i; + + /* Ignore everything past the 6th element (tm_year). */ + for (i = 5; i > 0; i--) { + if (alarmp[i] < 0) + alarmp[i] = timep[i]; + else + break; + } + + /* No carry needed if all fields are valid. */ + if (i == 5) + return; + + for (carry_into = i + 1; i >= 0; i--) { + if (alarmp[i] < timep[i]) + break; + + if (alarmp[i] > timep[i]) + return; + } + + switch (carry_into) { + case 1: + alarm->tm_min++; + + if (alarm->tm_min < 60) + return; + + alarm->tm_min = 0; + /* fall-through */ + + case 2: + alarm->tm_hour++; + + if (alarm->tm_hour < 60) + return; + + alarm->tm_hour = 0; + /* fall-through */ + + case 3: + alarm->tm_mday++; + + if (alarm->tm_mday <= rtc_days_in_month[alarm->tm_mon]) + return; + + alarm->tm_mday = 1; + /* fall-through */ + + case 4: + alarm->tm_mon++; + + if (alarm->tm_mon <= 12) + return; + + alarm->tm_mon = 1; + /* fall-through */ + + case 5: + alarm->tm_year++; + } +} +EXPORT_SYMBOL(rtc_merge_alarm); + MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index a760cf69af90..4b72b8ef5d66 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -192,7 +192,7 @@ static int pcf8563_validate_client(struct i2c_client *client) xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (xfer != ARRAY_SIZE(msgs)) { - dev_err(&client->adapter->dev, + dev_err(&client->dev, "%s: could not read register 0x%02X\n", __FUNCTION__, pattern[i].reg); @@ -203,7 +203,7 @@ static int pcf8563_validate_client(struct i2c_client *client) if (value > pattern[i].max || value < pattern[i].min) { - dev_dbg(&client->adapter->dev, + dev_dbg(&client->dev, "%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, " "max=%d, value=%d, raw=0x%02X\n", __FUNCTION__, i, pattern[i].reg, pattern[i].mask, @@ -253,7 +253,7 @@ static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind) int err = 0; - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__); if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { err = -ENODEV; diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index e2c7698fdba3..1460f6b769f2 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -200,7 +200,7 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) struct i2c_client *client; struct rs5c372 *rs5c372; - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__); if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { err = -ENODEV; diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 143302a8e79c..72ba1a70f35f 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -2,6 +2,7 @@ * SuperH On-Chip RTC Support * * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2006 Jamie Lenehan * * Based on the old arch/sh/kernel/cpu/rtc.c by: * @@ -21,7 +22,10 @@ #include <linux/seq_file.h> #include <linux/interrupt.h> #include <linux/spinlock.h> -#include <asm/io.h> +#include <linux/io.h> + +#define DRV_NAME "sh-rtc" +#define DRV_VERSION "0.1.2" #ifdef CONFIG_CPU_SH3 #define rtc_reg_size sizeof(u16) @@ -33,22 +37,26 @@ #define RTC_REG(r) ((r) * rtc_reg_size) -#define R64CNT RTC_REG(0) -#define RSECCNT RTC_REG(1) -#define RMINCNT RTC_REG(2) -#define RHRCNT RTC_REG(3) -#define RWKCNT RTC_REG(4) -#define RDAYCNT RTC_REG(5) -#define RMONCNT RTC_REG(6) -#define RYRCNT RTC_REG(7) -#define RSECAR RTC_REG(8) -#define RMINAR RTC_REG(9) -#define RHRAR RTC_REG(10) -#define RWKAR RTC_REG(11) -#define RDAYAR RTC_REG(12) -#define RMONAR RTC_REG(13) -#define RCR1 RTC_REG(14) -#define RCR2 RTC_REG(15) +#define R64CNT RTC_REG(0) + +#define RSECCNT RTC_REG(1) /* RTC sec */ +#define RMINCNT RTC_REG(2) /* RTC min */ +#define RHRCNT RTC_REG(3) /* RTC hour */ +#define RWKCNT RTC_REG(4) /* RTC week */ +#define RDAYCNT RTC_REG(5) /* RTC day */ +#define RMONCNT RTC_REG(6) /* RTC month */ +#define RYRCNT RTC_REG(7) /* RTC year */ +#define RSECAR RTC_REG(8) /* ALARM sec */ +#define RMINAR RTC_REG(9) /* ALARM min */ +#define RHRAR RTC_REG(10) /* ALARM hour */ +#define RWKAR RTC_REG(11) /* ALARM week */ +#define RDAYAR RTC_REG(12) /* ALARM day */ +#define RMONAR RTC_REG(13) /* ALARM month */ +#define RCR1 RTC_REG(14) /* Control */ +#define RCR2 RTC_REG(15) /* Control */ + +/* ALARM Bits - or with BCD encoded value */ +#define AR_ENB 0x80 /* Enable for alarm cmp */ /* RCR1 Bits */ #define RCR1_CF 0x80 /* Carry Flag */ @@ -71,22 +79,28 @@ struct sh_rtc { unsigned int alarm_irq, periodic_irq, carry_irq; struct rtc_device *rtc_dev; spinlock_t lock; + int rearm_aie; }; -static irqreturn_t sh_rtc_interrupt(int irq, void *id) +static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) { - struct platform_device *pdev = id; + struct platform_device *pdev = to_platform_device(dev_id); struct sh_rtc *rtc = platform_get_drvdata(pdev); unsigned int tmp, events = 0; spin_lock(&rtc->lock); tmp = readb(rtc->regbase + RCR1); + tmp &= ~RCR1_CF; - if (tmp & RCR1_AF) - events |= RTC_AF | RTC_IRQF; - - tmp &= ~(RCR1_CF | RCR1_AF); + if (rtc->rearm_aie) { + if (tmp & RCR1_AF) + tmp &= ~RCR1_AF; /* try to clear AF again */ + else { + tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */ + rtc->rearm_aie = 0; + } + } writeb(tmp, rtc->regbase + RCR1); @@ -97,9 +111,45 @@ static irqreturn_t sh_rtc_interrupt(int irq, void *id) return IRQ_HANDLED; } -static irqreturn_t sh_rtc_periodic(int irq, void *id) +static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) +{ + struct platform_device *pdev = to_platform_device(dev_id); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + unsigned int tmp, events = 0; + + spin_lock(&rtc->lock); + + tmp = readb(rtc->regbase + RCR1); + + /* + * If AF is set then the alarm has triggered. If we clear AF while + * the alarm time still matches the RTC time then AF will + * immediately be set again, and if AIE is enabled then the alarm + * interrupt will immediately be retrigger. So we clear AIE here + * and use rtc->rearm_aie so that the carry interrupt will keep + * trying to clear AF and once it stays cleared it'll re-enable + * AIE. + */ + if (tmp & RCR1_AF) { + events |= RTC_AF | RTC_IRQF; + + tmp &= ~(RCR1_AF|RCR1_AIE); + + writeb(tmp, rtc->regbase + RCR1); + + rtc->rearm_aie = 1; + + rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events); + } + + spin_unlock(&rtc->lock); + return IRQ_HANDLED; +} + +static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) { - struct sh_rtc *rtc = dev_get_drvdata(id); + struct platform_device *pdev = to_platform_device(dev_id); + struct sh_rtc *rtc = platform_get_drvdata(pdev); spin_lock(&rtc->lock); @@ -139,10 +189,11 @@ 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; + rtc->rearm_aie = 0; + } else if (rtc->rearm_aie == 0) + tmp |= RCR1_AIE; writeb(tmp, rtc->regbase + RCR1); @@ -177,7 +228,7 @@ static int sh_rtc_open(struct device *dev) goto err_bad_carry; } - ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, IRQF_DISABLED, + ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, "sh-rtc alarm", dev); if (unlikely(ret)) { dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n", @@ -200,6 +251,7 @@ static void sh_rtc_release(struct device *dev) struct sh_rtc *rtc = dev_get_drvdata(dev); sh_rtc_setpie(dev, 0); + sh_rtc_setaie(dev, 0); free_irq(rtc->periodic_irq, dev); free_irq(rtc->carry_irq, dev); @@ -267,7 +319,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT)); tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT)); tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT)); - tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)); + tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)) - 1; #if defined(CONFIG_CPU_SH4) yr = readw(rtc->regbase + RYRCNT); @@ -295,7 +347,7 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, - tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + 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"); @@ -322,7 +374,7 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT); writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT); writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT); - writeb(BIN2BCD(tm->tm_mon), rtc->regbase + RMONCNT); + writeb(BIN2BCD(tm->tm_mon + 1), rtc->regbase + RMONCNT); #ifdef CONFIG_CPU_SH3 year = tm->tm_year % 100; @@ -344,12 +396,136 @@ static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) return 0; } +static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off) +{ + unsigned int byte; + int value = 0xff; /* return 0xff for ignored values */ + + byte = readb(rtc->regbase + reg_off); + if (byte & AR_ENB) { + byte &= ~AR_ENB; /* strip the enable bit */ + value = BCD2BIN(byte); + } + + return value; +} + +static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + struct rtc_time* tm = &wkalrm->time; + + spin_lock_irq(&rtc->lock); + + tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR); + tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR); + tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR); + tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR); + tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR); + tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR); + if (tm->tm_mon > 0) + tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ + tm->tm_year = 0xffff; + + spin_unlock_irq(&rtc->lock); + + return 0; +} + +static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc, + int value, int reg_off) +{ + /* < 0 for a value that is ignored */ + if (value < 0) + writeb(0, rtc->regbase + reg_off); + else + writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off); +} + +static int sh_rtc_check_alarm(struct rtc_time* tm) +{ + /* + * The original rtc says anything > 0xc0 is "don't care" or "match + * all" - most users use 0xff but rtc-dev uses -1 for the same thing. + * The original rtc doesn't support years - some things use -1 and + * some 0xffff. We use -1 to make out tests easier. + */ + if (tm->tm_year == 0xffff) + tm->tm_year = -1; + if (tm->tm_mon >= 0xff) + tm->tm_mon = -1; + if (tm->tm_mday >= 0xff) + tm->tm_mday = -1; + if (tm->tm_wday >= 0xff) + tm->tm_wday = -1; + if (tm->tm_hour >= 0xff) + tm->tm_hour = -1; + if (tm->tm_min >= 0xff) + tm->tm_min = -1; + if (tm->tm_sec >= 0xff) + tm->tm_sec = -1; + + if (tm->tm_year > 9999 || + tm->tm_mon >= 12 || + tm->tm_mday == 0 || tm->tm_mday >= 32 || + tm->tm_wday >= 7 || + tm->tm_hour >= 24 || + tm->tm_min >= 60 || + tm->tm_sec >= 60) + return -EINVAL; + + return 0; +} + +static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_rtc *rtc = platform_get_drvdata(pdev); + unsigned int rcr1; + struct rtc_time *tm = &wkalrm->time; + int mon, err; + + err = sh_rtc_check_alarm(tm); + if (unlikely(err < 0)) + return err; + + spin_lock_irq(&rtc->lock); + + /* disable alarm interrupt and clear flag */ + rcr1 = readb(rtc->regbase + RCR1); + rcr1 &= ~RCR1_AF; + writeb(rcr1 & ~RCR1_AIE, rtc->regbase + RCR1); + + rtc->rearm_aie = 0; + + /* set alarm time */ + sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR); + sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR); + sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR); + sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR); + sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR); + mon = tm->tm_mon; + if (mon >= 0) + mon += 1; + sh_rtc_write_alarm_value(rtc, mon, RMONAR); + + /* Restore interrupt activation status */ + writeb(rcr1, rtc->regbase + RCR1); + + spin_unlock_irq(&rtc->lock); + + return 0; +} + static struct rtc_class_ops sh_rtc_ops = { .open = sh_rtc_open, .release = sh_rtc_release, .ioctl = sh_rtc_ioctl, .read_time = sh_rtc_read_time, .set_time = sh_rtc_set_time, + .read_alarm = sh_rtc_read_alarm, + .set_alarm = sh_rtc_set_alarm, .proc = sh_rtc_proc, }; @@ -442,7 +618,7 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) } static struct platform_driver sh_rtc_platform_driver = { .driver = { - .name = "sh-rtc", + .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = sh_rtc_probe, @@ -463,5 +639,6 @@ module_init(sh_rtc_init); module_exit(sh_rtc_exit); MODULE_DESCRIPTION("SuperH on-chip RTC driver"); -MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 9a67487d086b..019ae255b0c8 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -372,7 +372,7 @@ static int x1205_validate_client(struct i2c_client *client) }; if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { - dev_err(&client->adapter->dev, + dev_err(&client->dev, "%s: could not read register %x\n", __FUNCTION__, probe_zero_pattern[i]); @@ -380,7 +380,7 @@ static int x1205_validate_client(struct i2c_client *client) } if ((buf & probe_zero_pattern[i+1]) != 0) { - dev_err(&client->adapter->dev, + dev_err(&client->dev, "%s: register=%02x, zero pattern=%d, value=%x\n", __FUNCTION__, probe_zero_pattern[i], i, buf); @@ -400,7 +400,7 @@ static int x1205_validate_client(struct i2c_client *client) }; if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { - dev_err(&client->adapter->dev, + dev_err(&client->dev, "%s: could not read register %x\n", __FUNCTION__, probe_limits_pattern[i].reg); @@ -411,7 +411,7 @@ static int x1205_validate_client(struct i2c_client *client) if (value > probe_limits_pattern[i].max || value < probe_limits_pattern[i].min) { - dev_dbg(&client->adapter->dev, + dev_dbg(&client->dev, "%s: register=%x, lim pattern=%d, value=%d\n", __FUNCTION__, probe_limits_pattern[i].reg, i, value); @@ -506,7 +506,7 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) struct i2c_client *client; struct rtc_device *rtc; - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); + dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__); if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { err = -ENODEV; |