diff options
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/rtc/interface.c | 21 | ||||
-rw-r--r-- | drivers/rtc/rtc-ab8500.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 127 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1374.c | 285 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl12057.c | 83 | ||||
-rw-r--r-- | drivers/rtc/rtc-omap.c | 547 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 55 | ||||
-rw-r--r-- | drivers/rtc/rtc-sirfsoc.c | 66 | ||||
-rw-r--r-- | drivers/rtc/rtc-snvs.c | 39 |
10 files changed, 922 insertions, 311 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b682651b5307..4511ddc1ac31 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -192,6 +192,14 @@ config RTC_DRV_DS1374 This driver can also be built as a module. If so, the module will be called rtc-ds1374. +config RTC_DRV_DS1374_WDT + bool "Dallas/Maxim DS1374 watchdog timer" + depends on RTC_DRV_DS1374 + help + If you say Y here you will get support for the + watchdog timer in the Dallas Semiconductor DS1374 + real-time clock chips. + config RTC_DRV_DS1672 tristate "Dallas/Maxim DS1672" help diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 5b2717f5dafa..45bfc28ee3aa 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -30,6 +30,14 @@ static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) else { memset(tm, 0, sizeof(struct rtc_time)); err = rtc->ops->read_time(rtc->dev.parent, tm); + if (err < 0) { + dev_err(&rtc->dev, "read_time: fail to read\n"); + return err; + } + + err = rtc_valid_tm(tm); + if (err < 0) + dev_err(&rtc->dev, "read_time: rtc_time isn't valid\n"); } return err; } @@ -891,11 +899,24 @@ again: if (next) { struct rtc_wkalrm alarm; int err; + int retry = 3; + alarm.time = rtc_ktime_to_tm(next->expires); alarm.enabled = 1; +reprogram: err = __rtc_set_alarm(rtc, &alarm); if (err == -ETIME) goto again; + else if (err) { + if (retry-- > 0) + goto reprogram; + + timer = container_of(next, struct rtc_timer, node); + timerqueue_del(&rtc->timerqueue, &timer->node); + timer->enabled = 0; + dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err); + goto again; + } } else rtc_alarm_disable(rtc); diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 727e2f5d14d9..866e0ef5122d 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -504,6 +504,8 @@ static int ab8500_rtc_probe(struct platform_device *pdev) return err; } + rtc->uie_unsupported = 1; + return 0; } diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index bb43cf703efc..4ffabb322a9a 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -35,7 +35,7 @@ enum ds_type { ds_1388, ds_3231, m41t00, - mcp7941x, + mcp794xx, rx_8025, last_ds_type /* always last */ /* rs5c372 too? different address... */ @@ -46,7 +46,7 @@ enum ds_type { #define DS1307_REG_SECS 0x00 /* 00-59 */ # define DS1307_BIT_CH 0x80 # define DS1340_BIT_nEOSC 0x80 -# define MCP7941X_BIT_ST 0x80 +# define MCP794XX_BIT_ST 0x80 #define DS1307_REG_MIN 0x01 /* 00-59 */ #define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */ # define DS1307_BIT_12HR 0x40 /* in REG_HOUR */ @@ -54,7 +54,7 @@ enum ds_type { # define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */ # define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */ #define DS1307_REG_WDAY 0x03 /* 01-07 */ -# define MCP7941X_BIT_VBATEN 0x08 +# define MCP794XX_BIT_VBATEN 0x08 #define DS1307_REG_MDAY 0x04 /* 01-31 */ #define DS1307_REG_MONTH 0x05 /* 01-12 */ # define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */ @@ -159,7 +159,7 @@ static struct chip_desc chips[last_ds_type] = { [ds_3231] = { .alarm = 1, }, - [mcp7941x] = { + [mcp794xx] = { .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, @@ -176,7 +176,8 @@ static const struct i2c_device_id ds1307_id[] = { { "ds1340", ds_1340 }, { "ds3231", ds_3231 }, { "m41t00", m41t00 }, - { "mcp7941x", mcp7941x }, + { "mcp7940x", mcp794xx }, + { "mcp7941x", mcp794xx }, { "pt7c4338", ds_1307 }, { "rx8025", rx_8025 }, { } @@ -439,14 +440,14 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN | DS1340_BIT_CENTURY; break; - case mcp7941x: + case mcp794xx: /* * these bits were cleared when preparing the date/time * values and need to be set again before writing the * buffer out to the device. */ - buf[DS1307_REG_SECS] |= MCP7941X_BIT_ST; - buf[DS1307_REG_WDAY] |= MCP7941X_BIT_VBATEN; + buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; break; default: break; @@ -614,26 +615,26 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { /*----------------------------------------------------------------------*/ /* - * Alarm support for mcp7941x devices. + * Alarm support for mcp794xx devices. */ -#define MCP7941X_REG_CONTROL 0x07 -# define MCP7941X_BIT_ALM0_EN 0x10 -# define MCP7941X_BIT_ALM1_EN 0x20 -#define MCP7941X_REG_ALARM0_BASE 0x0a -#define MCP7941X_REG_ALARM0_CTRL 0x0d -#define MCP7941X_REG_ALARM1_BASE 0x11 -#define MCP7941X_REG_ALARM1_CTRL 0x14 -# define MCP7941X_BIT_ALMX_IF (1 << 3) -# define MCP7941X_BIT_ALMX_C0 (1 << 4) -# define MCP7941X_BIT_ALMX_C1 (1 << 5) -# define MCP7941X_BIT_ALMX_C2 (1 << 6) -# define MCP7941X_BIT_ALMX_POL (1 << 7) -# define MCP7941X_MSK_ALMX_MATCH (MCP7941X_BIT_ALMX_C0 | \ - MCP7941X_BIT_ALMX_C1 | \ - MCP7941X_BIT_ALMX_C2) - -static void mcp7941x_work(struct work_struct *work) +#define MCP794XX_REG_CONTROL 0x07 +# define MCP794XX_BIT_ALM0_EN 0x10 +# define MCP794XX_BIT_ALM1_EN 0x20 +#define MCP794XX_REG_ALARM0_BASE 0x0a +#define MCP794XX_REG_ALARM0_CTRL 0x0d +#define MCP794XX_REG_ALARM1_BASE 0x11 +#define MCP794XX_REG_ALARM1_CTRL 0x14 +# define MCP794XX_BIT_ALMX_IF (1 << 3) +# define MCP794XX_BIT_ALMX_C0 (1 << 4) +# define MCP794XX_BIT_ALMX_C1 (1 << 5) +# define MCP794XX_BIT_ALMX_C2 (1 << 6) +# define MCP794XX_BIT_ALMX_POL (1 << 7) +# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ + MCP794XX_BIT_ALMX_C1 | \ + MCP794XX_BIT_ALMX_C2) + +static void mcp794xx_work(struct work_struct *work) { struct ds1307 *ds1307 = container_of(work, struct ds1307, work); struct i2c_client *client = ds1307->client; @@ -642,22 +643,22 @@ static void mcp7941x_work(struct work_struct *work) mutex_lock(&ds1307->rtc->ops_lock); /* Check and clear alarm 0 interrupt flag. */ - reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_ALARM0_CTRL); + reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_ALARM0_CTRL); if (reg < 0) goto out; - if (!(reg & MCP7941X_BIT_ALMX_IF)) + if (!(reg & MCP794XX_BIT_ALMX_IF)) goto out; - reg &= ~MCP7941X_BIT_ALMX_IF; - ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_ALARM0_CTRL, reg); + reg &= ~MCP794XX_BIT_ALMX_IF; + ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_ALARM0_CTRL, reg); if (ret < 0) goto out; /* Disable alarm 0. */ - reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL); + reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL); if (reg < 0) goto out; - reg &= ~MCP7941X_BIT_ALM0_EN; - ret = i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg); + reg &= ~MCP794XX_BIT_ALM0_EN; + ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg); if (ret < 0) goto out; @@ -669,7 +670,7 @@ out: mutex_unlock(&ds1307->rtc->ops_lock); } -static int mcp7941x_read_alarm(struct device *dev, struct rtc_wkalrm *t) +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); @@ -680,11 +681,11 @@ static int mcp7941x_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* Read control and alarm 0 registers. */ - ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs); if (ret < 0) return ret; - t->enabled = !!(regs[0] & MCP7941X_BIT_ALM0_EN); + t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f); @@ -701,14 +702,14 @@ static int mcp7941x_read_alarm(struct device *dev, struct rtc_wkalrm *t) "enabled=%d polarity=%d irq=%d match=%d\n", __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, - !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_POL), - !!(ds1307->regs[6] & MCP7941X_BIT_ALMX_IF), - (ds1307->regs[6] & MCP7941X_MSK_ALMX_MATCH) >> 4); + !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_POL), + !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_IF), + (ds1307->regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); return 0; } -static int mcp7941x_set_alarm(struct device *dev, struct rtc_wkalrm *t) +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); @@ -725,7 +726,7 @@ static int mcp7941x_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control and alarm 0 registers. */ - ret = ds1307->read_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs); if (ret < 0) return ret; @@ -738,23 +739,23 @@ static int mcp7941x_set_alarm(struct device *dev, struct rtc_wkalrm *t) regs[8] = bin2bcd(t->time.tm_mon) + 1; /* Clear the alarm 0 interrupt flag. */ - regs[6] &= ~MCP7941X_BIT_ALMX_IF; + regs[6] &= ~MCP794XX_BIT_ALMX_IF; /* Set alarm match: second, minute, hour, day, date, month. */ - regs[6] |= MCP7941X_MSK_ALMX_MATCH; + regs[6] |= MCP794XX_MSK_ALMX_MATCH; if (t->enabled) - regs[0] |= MCP7941X_BIT_ALM0_EN; + regs[0] |= MCP794XX_BIT_ALM0_EN; else - regs[0] &= ~MCP7941X_BIT_ALM0_EN; + regs[0] &= ~MCP794XX_BIT_ALM0_EN; - ret = ds1307->write_block_data(client, MCP7941X_REG_CONTROL, 10, regs); + ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs); if (ret < 0) return ret; return 0; } -static int mcp7941x_alarm_irq_enable(struct device *dev, unsigned int enabled) +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); @@ -763,24 +764,24 @@ static int mcp7941x_alarm_irq_enable(struct device *dev, unsigned int enabled) if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; - reg = i2c_smbus_read_byte_data(client, MCP7941X_REG_CONTROL); + reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL); if (reg < 0) return reg; if (enabled) - reg |= MCP7941X_BIT_ALM0_EN; + reg |= MCP794XX_BIT_ALM0_EN; else - reg &= ~MCP7941X_BIT_ALM0_EN; + reg &= ~MCP794XX_BIT_ALM0_EN; - return i2c_smbus_write_byte_data(client, MCP7941X_REG_CONTROL, reg); + return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg); } -static const struct rtc_class_ops mcp7941x_rtc_ops = { +static const struct rtc_class_ops mcp794xx_rtc_ops = { .read_time = ds1307_get_time, .set_time = ds1307_set_time, - .read_alarm = mcp7941x_read_alarm, - .set_alarm = mcp7941x_set_alarm, - .alarm_irq_enable = mcp7941x_alarm_irq_enable, + .read_alarm = mcp794xx_read_alarm, + .set_alarm = mcp794xx_set_alarm, + .alarm_irq_enable = mcp794xx_alarm_irq_enable, }; /*----------------------------------------------------------------------*/ @@ -1049,10 +1050,10 @@ static int ds1307_probe(struct i2c_client *client, case ds_1388: ds1307->offset = 1; /* Seconds starts at 1 */ break; - case mcp7941x: - rtc_ops = &mcp7941x_rtc_ops; + case mcp794xx: + rtc_ops = &mcp794xx_rtc_ops; if (ds1307->client->irq > 0 && chip->alarm) { - INIT_WORK(&ds1307->work, mcp7941x_work); + INIT_WORK(&ds1307->work, mcp794xx_work); want_irq = true; } break; @@ -1117,18 +1118,18 @@ read_rtc: dev_warn(&client->dev, "SET TIME!\n"); } break; - case mcp7941x: + case mcp794xx: /* make sure that the backup battery is enabled */ - if (!(ds1307->regs[DS1307_REG_WDAY] & MCP7941X_BIT_VBATEN)) { + if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { i2c_smbus_write_byte_data(client, DS1307_REG_WDAY, ds1307->regs[DS1307_REG_WDAY] - | MCP7941X_BIT_VBATEN); + | MCP794XX_BIT_VBATEN); } /* clock halted? turn it on, so clock can tick. */ - if (!(tmp & MCP7941X_BIT_ST)) { + if (!(tmp & MCP794XX_BIT_ST)) { i2c_smbus_write_byte_data(client, DS1307_REG_SECS, - MCP7941X_BIT_ST); + MCP794XX_BIT_ST); dev_warn(&client->dev, "SET TIME!\n"); goto read_rtc; } diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 9e6e14fb53d7..8605fde394b2 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -4,6 +4,7 @@ * Based on code by Randy Vinson <rvinson@mvista.com>, * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>. * + * Copyright (C) 2014 Rose Technology * Copyright (C) 2006-2007 Freescale Semiconductor * * 2005 (c) MontaVista Software, Inc. This file is licensed under @@ -26,6 +27,13 @@ #include <linux/workqueue.h> #include <linux/slab.h> #include <linux/pm.h> +#ifdef CONFIG_RTC_DRV_DS1374_WDT +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/miscdevice.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> +#endif #define DS1374_REG_TOD0 0x00 /* Time of Day */ #define DS1374_REG_TOD1 0x01 @@ -49,6 +57,14 @@ static const struct i2c_device_id ds1374_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds1374_id); +#ifdef CONFIG_OF +static const struct of_device_id ds1374_of_match[] = { + { .compatible = "dallas,ds1374" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1374_of_match); +#endif + struct ds1374 { struct i2c_client *client; struct rtc_device *rtc; @@ -162,6 +178,7 @@ static int ds1374_set_time(struct device *dev, struct rtc_time *time) return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4); } +#ifndef CONFIG_RTC_DRV_DS1374_WDT /* The ds1374 has a decrementer for an alarm, rather than a comparator. * If the time of day is changed, then the alarm will need to be * reset. @@ -263,6 +280,7 @@ out: mutex_unlock(&ds1374->mutex); return ret; } +#endif static irqreturn_t ds1374_irq(int irq, void *dev_id) { @@ -307,6 +325,7 @@ unlock: mutex_unlock(&ds1374->mutex); } +#ifndef CONFIG_RTC_DRV_DS1374_WDT static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled) { struct i2c_client *client = to_i2c_client(dev); @@ -331,15 +350,260 @@ out: mutex_unlock(&ds1374->mutex); return ret; } +#endif static const struct rtc_class_ops ds1374_rtc_ops = { .read_time = ds1374_read_time, .set_time = ds1374_set_time, +#ifndef CONFIG_RTC_DRV_DS1374_WDT .read_alarm = ds1374_read_alarm, .set_alarm = ds1374_set_alarm, .alarm_irq_enable = ds1374_alarm_irq_enable, +#endif +}; + +#ifdef CONFIG_RTC_DRV_DS1374_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +static struct i2c_client *save_client; +/* Default margin */ +#define WD_TIMO 131762 + +#define DRV_NAME "DS1374 Watchdog" + +static int wdt_margin = WD_TIMO; +static unsigned long wdt_is_open; +module_param(wdt_margin, int, 0); +MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)"); + +static const struct watchdog_info ds1374_wdt_info = { + .identity = "DS1374 WTD", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, }; +static int ds1374_wdt_settimeout(unsigned int timeout) +{ + int ret = -ENOIOCTLCMD; + int cr; + + ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR); + if (ret < 0) + goto out; + + /* Disable any existing watchdog/alarm before setting the new one */ + cr &= ~DS1374_REG_CR_WACE; + + ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr); + if (ret < 0) + goto out; + + /* Set new watchdog time */ + ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3); + if (ret) { + pr_info("rtc-ds1374 - couldn't set new watchdog time\n"); + goto out; + } + + /* Enable watchdog timer */ + cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM; + cr &= ~DS1374_REG_CR_AIE; + + ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr); + if (ret < 0) + goto out; + + return 0; +out: + return ret; +} + + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static void ds1374_wdt_ping(void) +{ + u32 val; + int ret = 0; + + ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3); + if (ret) + pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret); +} + +static void ds1374_wdt_disable(void) +{ + int ret = -ENOIOCTLCMD; + int cr; + + cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR); + /* Disable watchdog timer */ + cr &= ~DS1374_REG_CR_WACE; + + ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr); +} + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int ds1374_wdt_open(struct inode *inode, struct file *file) +{ + struct ds1374 *ds1374 = i2c_get_clientdata(save_client); + + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { + mutex_lock(&ds1374->mutex); + if (test_and_set_bit(0, &wdt_is_open)) { + mutex_unlock(&ds1374->mutex); + return -EBUSY; + } + /* + * Activate + */ + wdt_is_open = 1; + mutex_unlock(&ds1374->mutex); + return nonseekable_open(inode, file); + } + return -ENODEV; +} + +/* + * Close the watchdog device. + */ +static int ds1374_wdt_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) + clear_bit(0, &wdt_is_open); + + return 0; +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t ds1374_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + ds1374_wdt_ping(); + return 1; + } + return 0; +} + +static ssize_t ds1374_wdt_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + return 0; +} + +/* + * Handle commands from user-space. + */ +static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int new_margin, options; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, + &ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_KEEPALIVE: + ds1374_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + + if (new_margin < 1 || new_margin > 16777216) + return -EINVAL; + + wdt_margin = new_margin; + ds1374_wdt_settimeout(new_margin); + ds1374_wdt_ping(); + /* fallthrough */ + case WDIOC_GETTIMEOUT: + return put_user(wdt_margin, (int __user *)arg); + case WDIOC_SETOPTIONS: + if (copy_from_user(&options, (int __user *)arg, sizeof(int))) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + pr_info("rtc-ds1374: disable watchdog\n"); + ds1374_wdt_disable(); + } + + if (options & WDIOS_ENABLECARD) { + pr_info("rtc-ds1374: enable watchdog\n"); + ds1374_wdt_settimeout(wdt_margin); + ds1374_wdt_ping(); + } + + return -EINVAL; + } + return -ENOTTY; +} + +static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct ds1374 *ds1374 = i2c_get_clientdata(save_client); + + mutex_lock(&ds1374->mutex); + ret = ds1374_wdt_ioctl(file, cmd, arg); + mutex_unlock(&ds1374->mutex); + + return ret; +} + +static int ds1374_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + /* Disable Watchdog */ + ds1374_wdt_disable(); + return NOTIFY_DONE; +} + +static const struct file_operations ds1374_wdt_fops = { + .owner = THIS_MODULE, + .read = ds1374_wdt_read, + .unlocked_ioctl = ds1374_wdt_unlocked_ioctl, + .write = ds1374_wdt_write, + .open = ds1374_wdt_open, + .release = ds1374_wdt_release, + .llseek = no_llseek, +}; + +static struct miscdevice ds1374_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ds1374_wdt_fops, +}; + +static struct notifier_block ds1374_wdt_notifier = { + .notifier_call = ds1374_wdt_notify_sys, +}; + +#endif /*CONFIG_RTC_DRV_DS1374_WDT*/ +/* + ***************************************************************************** + * + * Driver Interface + * + ***************************************************************************** + */ static int ds1374_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -378,12 +642,33 @@ static int ds1374_probe(struct i2c_client *client, return PTR_ERR(ds1374->rtc); } +#ifdef CONFIG_RTC_DRV_DS1374_WDT + save_client = client; + ret = misc_register(&ds1374_miscdev); + if (ret) + return ret; + ret = register_reboot_notifier(&ds1374_wdt_notifier); + if (ret) { + misc_deregister(&ds1374_miscdev); + return ret; + } + ds1374_wdt_settimeout(131072); +#endif + return 0; } static int ds1374_remove(struct i2c_client *client) { struct ds1374 *ds1374 = i2c_get_clientdata(client); +#ifdef CONFIG_RTC_DRV_DS1374_WDT + int res; + + res = misc_deregister(&ds1374_miscdev); + if (!res) + ds1374_miscdev.parent = NULL; + unregister_reboot_notifier(&ds1374_wdt_notifier); +#endif if (client->irq > 0) { mutex_lock(&ds1374->mutex); diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index 455b601d731d..6e1fcfb5d7e6 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -41,6 +41,7 @@ #define ISL12057_REG_RTC_DW 0x03 /* Day of the Week */ #define ISL12057_REG_RTC_DT 0x04 /* Date */ #define ISL12057_REG_RTC_MO 0x05 /* Month */ +#define ISL12057_REG_RTC_MO_CEN BIT(7) /* Century bit */ #define ISL12057_REG_RTC_YR 0x06 /* Year */ #define ISL12057_RTC_SEC_LEN 7 @@ -88,7 +89,7 @@ static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs) tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]); if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */ - tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x0f); + tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x1f); if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM) tm->tm_hour += 12; } else { /* 24 hour mode */ @@ -97,26 +98,37 @@ static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs) tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]); tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */ - tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO]) - 1; /* starts at 1 */ + tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */ tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100; + + /* Check if years register has overflown from 99 to 00 */ + if (regs[ISL12057_REG_RTC_MO] & ISL12057_REG_RTC_MO_CEN) + tm->tm_year += 100; } static int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm) { + u8 century_bit; + /* * The clock has an 8 bit wide bcd-coded register for the year. + * It also has a century bit encoded in MO flag which provides + * information about overflow of year register from 99 to 00. * tm_year is an offset from 1900 and we are interested in the - * 2000-2099 range, so any value less than 100 is invalid. + * 2000-2199 range, so any value less than 100 or larger than + * 299 is invalid. */ - if (tm->tm_year < 100) + if (tm->tm_year < 100 || tm->tm_year > 299) return -EINVAL; + century_bit = (tm->tm_year > 199) ? ISL12057_REG_RTC_MO_CEN : 0; + regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec); regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min); regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */ regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday); - regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1); - regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year - 100); + regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1) | century_bit; + regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year % 100); regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1); return 0; @@ -152,17 +164,33 @@ static int isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct isl12057_rtc_data *data = dev_get_drvdata(dev); u8 regs[ISL12057_RTC_SEC_LEN]; + unsigned int sr; int ret; mutex_lock(&data->lock); + ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr); + if (ret) { + dev_err(dev, "%s: unable to read oscillator status flag (%d)\n", + __func__, ret); + goto out; + } else { + if (sr & ISL12057_REG_SR_OSF) { + ret = -ENODATA; + goto out; + } + } + ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); + if (ret) + dev_err(dev, "%s: unable to read RTC time section (%d)\n", + __func__, ret); + +out: mutex_unlock(&data->lock); - if (ret) { - dev_err(dev, "%s: RTC read failed\n", __func__); + if (ret) return ret; - } isl12057_rtc_regs_to_tm(tm, regs); @@ -182,10 +210,24 @@ static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm) mutex_lock(&data->lock); ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs, ISL12057_RTC_SEC_LEN); - mutex_unlock(&data->lock); + if (ret) { + dev_err(dev, "%s: unable to write RTC time section (%d)\n", + __func__, ret); + goto out; + } - if (ret) - dev_err(dev, "%s: RTC write failed\n", __func__); + /* + * Now that RTC time has been updated, let's clear oscillator + * failure flag, if needed. + */ + ret = regmap_update_bits(data->regmap, ISL12057_REG_SR, + ISL12057_REG_SR_OSF, 0); + if (ret < 0) + dev_err(dev, "%s: unable to clear osc. failure bit (%d)\n", + __func__, ret); + +out: + mutex_unlock(&data->lock); return ret; } @@ -203,15 +245,8 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) ret = regmap_update_bits(regmap, ISL12057_REG_INT, ISL12057_REG_INT_EOSC, 0); if (ret < 0) { - dev_err(dev, "Unable to enable oscillator\n"); - return ret; - } - - /* Clear oscillator failure bit if needed */ - ret = regmap_update_bits(regmap, ISL12057_REG_SR, - ISL12057_REG_SR_OSF, 0); - if (ret < 0) { - dev_err(dev, "Unable to clear oscillator failure bit\n"); + dev_err(dev, "%s: unable to enable oscillator (%d)\n", + __func__, ret); return ret; } @@ -219,7 +254,8 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) ret = regmap_update_bits(regmap, ISL12057_REG_SR, ISL12057_REG_SR_A1F, 0); if (ret < 0) { - dev_err(dev, "Unable to clear alarm bit\n"); + dev_err(dev, "%s: unable to clear alarm bit (%d)\n", + __func__, ret); return ret; } @@ -253,7 +289,8 @@ static int isl12057_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); - dev_err(dev, "regmap allocation failed: %d\n", ret); + dev_err(dev, "%s: regmap allocation failed (%d)\n", + __func__, ret); return ret; } diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 21142e6574a9..4f1c6ca97211 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -1,10 +1,11 @@ /* - * TI OMAP1 Real Time Clock interface for Linux + * TI OMAP Real Time Clock interface for Linux * * Copyright (C) 2003 MontaVista Software, Inc. * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com> * * Copyright (C) 2006 David Brownell (new RTC framework) + * Copyright (C) 2014 Johan Hovold <johan@kernel.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,7 +26,8 @@ #include <linux/pm_runtime.h> #include <linux/io.h> -/* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock +/* + * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock * with century-range alarm matching, driven by the 32kHz clock. * * The main user-visible ways it differs from PC RTCs are by omitting @@ -39,10 +41,6 @@ * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment. */ -#define DRIVER_NAME "omap_rtc" - -#define OMAP_RTC_BASE 0xfffb4800 - /* RTC registers */ #define OMAP_RTC_SECONDS_REG 0x00 #define OMAP_RTC_MINUTES_REG 0x04 @@ -72,6 +70,15 @@ #define OMAP_RTC_IRQWAKEEN 0x7c +#define OMAP_RTC_ALARM2_SECONDS_REG 0x80 +#define OMAP_RTC_ALARM2_MINUTES_REG 0x84 +#define OMAP_RTC_ALARM2_HOURS_REG 0x88 +#define OMAP_RTC_ALARM2_DAYS_REG 0x8c +#define OMAP_RTC_ALARM2_MONTHS_REG 0x90 +#define OMAP_RTC_ALARM2_YEARS_REG 0x94 + +#define OMAP_RTC_PMIC_REG 0x98 + /* OMAP_RTC_CTRL_REG bit fields: */ #define OMAP_RTC_CTRL_SPLIT BIT(7) #define OMAP_RTC_CTRL_DISABLE BIT(6) @@ -84,6 +91,7 @@ /* OMAP_RTC_STATUS_REG bit fields: */ #define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM2 BIT(7) #define OMAP_RTC_STATUS_ALARM BIT(6) #define OMAP_RTC_STATUS_1D_EVENT BIT(5) #define OMAP_RTC_STATUS_1H_EVENT BIT(4) @@ -93,6 +101,7 @@ #define OMAP_RTC_STATUS_BUSY BIT(0) /* OMAP_RTC_INTERRUPTS_REG bit fields: */ +#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4) #define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) #define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) @@ -102,61 +111,82 @@ /* OMAP_RTC_IRQWAKEEN bit fields: */ #define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) +/* OMAP_RTC_PMIC bit fields: */ +#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) + /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 #define KICK1_VALUE 0x95a4f1e0 -#define OMAP_RTC_HAS_KICKER BIT(0) - -/* - * Few RTC IP revisions has special WAKE-EN Register to enable Wakeup - * generation for event Alarm. - */ -#define OMAP_RTC_HAS_IRQWAKEEN BIT(1) +struct omap_rtc_device_type { + bool has_32kclk_en; + bool has_kicker; + bool has_irqwakeen; + bool has_pmic_mode; + bool has_power_up_reset; +}; -/* - * Some RTC IP revisions (like those in AM335x and DRA7x) need - * the 32KHz clock to be explicitly enabled. - */ -#define OMAP_RTC_HAS_32KCLK_EN BIT(2) +struct omap_rtc { + struct rtc_device *rtc; + void __iomem *base; + int irq_alarm; + int irq_timer; + u8 interrupts_reg; + bool is_pmic_controller; + const struct omap_rtc_device_type *type; +}; -static void __iomem *rtc_base; +static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) +{ + return readb(rtc->base + reg); +} -#define rtc_read(addr) readb(rtc_base + (addr)) -#define rtc_write(val, addr) writeb(val, rtc_base + (addr)) +static inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg) +{ + return readl(rtc->base + reg); +} -#define rtc_writel(val, addr) writel(val, rtc_base + (addr)) +static inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val) +{ + writeb(val, rtc->base + reg); +} +static inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val) +{ + writel(val, rtc->base + reg); +} -/* we rely on the rtc framework to handle locking (rtc->ops_lock), +/* + * We rely on the rtc framework to handle locking (rtc->ops_lock), * so the only other requirement is that register accesses which * require BUSY to be clear are made with IRQs locally disabled */ -static void rtc_wait_not_busy(void) +static void rtc_wait_not_busy(struct omap_rtc *rtc) { - int count = 0; - u8 status; + int count; + u8 status; /* BUSY may stay active for 1/32768 second (~30 usec) */ for (count = 0; count < 50; count++) { - status = rtc_read(OMAP_RTC_STATUS_REG); - if ((status & (u8)OMAP_RTC_STATUS_BUSY) == 0) + status = rtc_read(rtc, OMAP_RTC_STATUS_REG); + if (!(status & OMAP_RTC_STATUS_BUSY)) break; udelay(1); } /* now we have ~15 usec to read/write various registers */ } -static irqreturn_t rtc_irq(int irq, void *rtc) +static irqreturn_t rtc_irq(int irq, void *dev_id) { - unsigned long events = 0; - u8 irq_data; + struct omap_rtc *rtc = dev_id; + unsigned long events = 0; + u8 irq_data; - irq_data = rtc_read(OMAP_RTC_STATUS_REG); + irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); /* alarm irq? */ if (irq_data & OMAP_RTC_STATUS_ALARM) { - rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); + rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); events |= RTC_IRQF | RTC_AF; } @@ -164,23 +194,21 @@ static irqreturn_t rtc_irq(int irq, void *rtc) if (irq_data & OMAP_RTC_STATUS_1S_EVENT) events |= RTC_IRQF | RTC_UF; - rtc_update_irq(rtc, 1, events); + rtc_update_irq(rtc->rtc, 1, events); return IRQ_HANDLED; } static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { + struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; - struct platform_device *pdev = to_platform_device(dev); - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); local_irq_disable(); - rtc_wait_not_busy(); - reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) - irqwake_reg = rtc_read(OMAP_RTC_IRQWAKEEN); + rtc_wait_not_busy(rtc); + reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + if (rtc->type->has_irqwakeen) + irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; @@ -189,10 +217,10 @@ static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } - rtc_wait_not_busy(); - rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) - rtc_write(irqwake_reg, OMAP_RTC_IRQWAKEEN); + rtc_wait_not_busy(rtc); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); + if (rtc->type->has_irqwakeen) + rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); local_irq_enable(); return 0; @@ -230,39 +258,47 @@ static void bcd2tm(struct rtc_time *tm) tm->tm_year = bcd2bin(tm->tm_year) + 100; } +static void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm) +{ + tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG); + tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG); + tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG); + tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG); + tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG); + tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG); +} static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm) { + struct omap_rtc *rtc = dev_get_drvdata(dev); + /* we don't report wday/yday/isdst ... */ local_irq_disable(); - rtc_wait_not_busy(); - - tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG); - tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG); - tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG); - tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG); - tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG); - tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG); - + rtc_wait_not_busy(rtc); + omap_rtc_read_time_raw(rtc, tm); local_irq_enable(); bcd2tm(tm); + return 0; } static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) { + struct omap_rtc *rtc = dev_get_drvdata(dev); + if (tm2bcd(tm) < 0) return -EINVAL; + local_irq_disable(); - rtc_wait_not_busy(); + rtc_wait_not_busy(rtc); - rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG); - rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG); - rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG); - rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG); - rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG); - rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG); + rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year); + rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon); + rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday); + rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour); + rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min); + rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec); local_irq_enable(); @@ -271,48 +307,50 @@ static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { + struct omap_rtc *rtc = dev_get_drvdata(dev); + u8 interrupts; + local_irq_disable(); - rtc_wait_not_busy(); + rtc_wait_not_busy(rtc); - alm->time.tm_sec = rtc_read(OMAP_RTC_ALARM_SECONDS_REG); - alm->time.tm_min = rtc_read(OMAP_RTC_ALARM_MINUTES_REG); - alm->time.tm_hour = rtc_read(OMAP_RTC_ALARM_HOURS_REG); - alm->time.tm_mday = rtc_read(OMAP_RTC_ALARM_DAYS_REG); - alm->time.tm_mon = rtc_read(OMAP_RTC_ALARM_MONTHS_REG); - alm->time.tm_year = rtc_read(OMAP_RTC_ALARM_YEARS_REG); + alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG); + alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG); + alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG); + alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG); + alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG); + alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG); local_irq_enable(); bcd2tm(&alm->time); - alm->enabled = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG) - & OMAP_RTC_INTERRUPTS_IT_ALARM); + + interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM); return 0; } static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { + struct omap_rtc *rtc = dev_get_drvdata(dev); u8 reg, irqwake_reg = 0; - struct platform_device *pdev = to_platform_device(dev); - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); if (tm2bcd(&alm->time) < 0) return -EINVAL; local_irq_disable(); - rtc_wait_not_busy(); + rtc_wait_not_busy(rtc); - rtc_write(alm->time.tm_year, OMAP_RTC_ALARM_YEARS_REG); - rtc_write(alm->time.tm_mon, OMAP_RTC_ALARM_MONTHS_REG); - rtc_write(alm->time.tm_mday, OMAP_RTC_ALARM_DAYS_REG); - rtc_write(alm->time.tm_hour, OMAP_RTC_ALARM_HOURS_REG); - rtc_write(alm->time.tm_min, OMAP_RTC_ALARM_MINUTES_REG); - rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG); + rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year); + rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon); + rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday); + rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour); + rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min); + rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec); - reg = rtc_read(OMAP_RTC_INTERRUPTS_REG); - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) - irqwake_reg = rtc_read(OMAP_RTC_IRQWAKEEN); + reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + if (rtc->type->has_irqwakeen) + irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); if (alm->enabled) { reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; @@ -321,15 +359,79 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; } - rtc_write(reg, OMAP_RTC_INTERRUPTS_REG); - if (id_entry->driver_data & OMAP_RTC_HAS_IRQWAKEEN) - rtc_write(irqwake_reg, OMAP_RTC_IRQWAKEEN); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); + if (rtc->type->has_irqwakeen) + rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); local_irq_enable(); return 0; } +static struct omap_rtc *omap_rtc_power_off_rtc; + +/* + * omap_rtc_poweroff: RTC-controlled power off + * + * The RTC can be used to control an external PMIC via the pmic_power_en pin, + * which can be configured to transition to OFF on ALARM2 events. + * + * Notes: + * The two-second alarm offset is the shortest offset possible as the alarm + * registers must be set before the next timer update and the offset + * calculation is too heavy for everything to be done within a single access + * period (~15 us). + * + * Called with local interrupts disabled. + */ +static void omap_rtc_power_off(void) +{ + struct omap_rtc *rtc = omap_rtc_power_off_rtc; + struct rtc_time tm; + unsigned long now; + u32 val; + + /* enable pmic_power_en control */ + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN); + + /* set alarm two seconds from now */ + omap_rtc_read_time_raw(rtc, &tm); + bcd2tm(&tm); + rtc_tm_to_time(&tm, &now); + rtc_time_to_tm(now + 2, &tm); + + if (tm2bcd(&tm) < 0) { + dev_err(&rtc->rtc->dev, "power off failed\n"); + return; + } + + rtc_wait_not_busy(rtc); + + rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); + rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); + rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); + rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); + rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); + rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); + + /* + * enable ALARM2 interrupt + * + * NOTE: this fails on AM3352 if rtc_write (writeb) is used + */ + val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, + val | OMAP_RTC_INTERRUPTS_IT_ALARM2); + + /* + * Wait for alarm to trigger (within two seconds) and external PMIC to + * power off the system. Add a 500 ms margin for external latencies + * (e.g. debounce circuits). + */ + mdelay(2500); +} + static struct rtc_class_ops omap_rtc_ops = { .read_time = omap_rtc_read_time, .set_time = omap_rtc_set_time, @@ -338,137 +440,140 @@ static struct rtc_class_ops omap_rtc_ops = { .alarm_irq_enable = omap_rtc_alarm_irq_enable, }; -static int omap_rtc_alarm; -static int omap_rtc_timer; +static const struct omap_rtc_device_type omap_rtc_default_type = { + .has_power_up_reset = true, +}; -#define OMAP_RTC_DATA_AM3352_IDX 1 -#define OMAP_RTC_DATA_DA830_IDX 2 +static const struct omap_rtc_device_type omap_rtc_am3352_type = { + .has_32kclk_en = true, + .has_kicker = true, + .has_irqwakeen = true, + .has_pmic_mode = true, +}; -static struct platform_device_id omap_rtc_devtype[] = { +static const struct omap_rtc_device_type omap_rtc_da830_type = { + .has_kicker = true, +}; + +static const struct platform_device_id omap_rtc_id_table[] = { { - .name = DRIVER_NAME, - }, - [OMAP_RTC_DATA_AM3352_IDX] = { + .name = "omap_rtc", + .driver_data = (kernel_ulong_t)&omap_rtc_default_type, + }, { .name = "am3352-rtc", - .driver_data = OMAP_RTC_HAS_KICKER | OMAP_RTC_HAS_IRQWAKEEN | - OMAP_RTC_HAS_32KCLK_EN, - }, - [OMAP_RTC_DATA_DA830_IDX] = { + .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type, + }, { .name = "da830-rtc", - .driver_data = OMAP_RTC_HAS_KICKER, - }, - {}, + .driver_data = (kernel_ulong_t)&omap_rtc_da830_type, + }, { + /* sentinel */ + } }; -MODULE_DEVICE_TABLE(platform, omap_rtc_devtype); +MODULE_DEVICE_TABLE(platform, omap_rtc_id_table); static const struct of_device_id omap_rtc_of_match[] = { - { .compatible = "ti,da830-rtc", - .data = &omap_rtc_devtype[OMAP_RTC_DATA_DA830_IDX], - }, - { .compatible = "ti,am3352-rtc", - .data = &omap_rtc_devtype[OMAP_RTC_DATA_AM3352_IDX], - }, - {}, + { + .compatible = "ti,am3352-rtc", + .data = &omap_rtc_am3352_type, + }, { + .compatible = "ti,da830-rtc", + .data = &omap_rtc_da830_type, + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, omap_rtc_of_match); static int __init omap_rtc_probe(struct platform_device *pdev) { - struct resource *res; - struct rtc_device *rtc; - u8 reg, new_ctrl; + struct omap_rtc *rtc; + struct resource *res; + u8 reg, mask, new_ctrl; const struct platform_device_id *id_entry; const struct of_device_id *of_id; + int ret; - of_id = of_match_device(omap_rtc_of_match, &pdev->dev); - if (of_id) - pdev->id_entry = of_id->data; + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; - id_entry = platform_get_device_id(pdev); - if (!id_entry) { - dev_err(&pdev->dev, "no matching device entry\n"); - return -ENODEV; + of_id = of_match_device(omap_rtc_of_match, &pdev->dev); + if (of_id) { + rtc->type = of_id->data; + rtc->is_pmic_controller = rtc->type->has_pmic_mode && + of_property_read_bool(pdev->dev.of_node, + "system-power-controller"); + } else { + id_entry = platform_get_device_id(pdev); + rtc->type = (void *)id_entry->driver_data; } - omap_rtc_timer = platform_get_irq(pdev, 0); - if (omap_rtc_timer <= 0) { - pr_debug("%s: no update irq?\n", pdev->name); + rtc->irq_timer = platform_get_irq(pdev, 0); + if (rtc->irq_timer <= 0) return -ENOENT; - } - omap_rtc_alarm = platform_get_irq(pdev, 1); - if (omap_rtc_alarm <= 0) { - pr_debug("%s: no alarm irq?\n", pdev->name); + rtc->irq_alarm = platform_get_irq(pdev, 1); + if (rtc->irq_alarm <= 0) return -ENOENT; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rtc_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(rtc_base)) - return PTR_ERR(rtc_base); + rtc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->base)) + return PTR_ERR(rtc->base); + + platform_set_drvdata(pdev, rtc); /* Enable the clock/module so that we can access the registers */ pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) { - rtc_writel(KICK0_VALUE, OMAP_RTC_KICK0_REG); - rtc_writel(KICK1_VALUE, OMAP_RTC_KICK1_REG); - } - - rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &omap_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - pr_debug("%s: can't register RTC device, err %ld\n", - pdev->name, PTR_ERR(rtc)); - goto fail0; + if (rtc->type->has_kicker) { + rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE); + rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE); } - platform_set_drvdata(pdev, rtc); - /* clear pending irqs, and set 1/second periodic, - * which we'll use instead of update irqs + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used */ - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 0); /* enable RTC functional clock */ - if (id_entry->driver_data & OMAP_RTC_HAS_32KCLK_EN) - rtc_writel(OMAP_RTC_OSC_32KCLK_EN, OMAP_RTC_OSC_REG); + if (rtc->type->has_32kclk_en) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + rtc_writel(rtc, OMAP_RTC_OSC_REG, + reg | OMAP_RTC_OSC_32KCLK_EN); + } /* clear old status */ - reg = rtc_read(OMAP_RTC_STATUS_REG); - if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) { - pr_info("%s: RTC power up reset detected\n", - pdev->name); - rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG); - } - if (reg & (u8) OMAP_RTC_STATUS_ALARM) - rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); + reg = rtc_read(rtc, OMAP_RTC_STATUS_REG); - /* handle periodic and alarm irqs */ - if (devm_request_irq(&pdev->dev, omap_rtc_timer, rtc_irq, 0, - dev_name(&rtc->dev), rtc)) { - pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n", - pdev->name, omap_rtc_timer); - goto fail0; - } - if ((omap_rtc_timer != omap_rtc_alarm) && - (devm_request_irq(&pdev->dev, omap_rtc_alarm, rtc_irq, 0, - dev_name(&rtc->dev), rtc))) { - pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n", - pdev->name, omap_rtc_alarm); - goto fail0; + mask = OMAP_RTC_STATUS_ALARM; + + if (rtc->type->has_pmic_mode) + mask |= OMAP_RTC_STATUS_ALARM2; + + if (rtc->type->has_power_up_reset) { + mask |= OMAP_RTC_STATUS_POWER_UP; + if (reg & OMAP_RTC_STATUS_POWER_UP) + dev_info(&pdev->dev, "RTC power up reset detected\n"); } + if (reg & mask) + rtc_write(rtc, OMAP_RTC_STATUS_REG, reg & mask); + /* On boards with split power, RTC_ON_NOFF won't reset the RTC */ - reg = rtc_read(OMAP_RTC_CTRL_REG); - if (reg & (u8) OMAP_RTC_CTRL_STOP) - pr_info("%s: already running\n", pdev->name); + reg = rtc_read(rtc, OMAP_RTC_CTRL_REG); + if (reg & OMAP_RTC_CTRL_STOP) + dev_info(&pdev->dev, "already running\n"); /* force to 24 hour mode */ - new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP); + new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); new_ctrl |= OMAP_RTC_CTRL_STOP; - /* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: + /* + * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: * * - Device wake-up capability setting should come through chip * init logic. OMAP1 boards should initialize the "wakeup capable" @@ -482,36 +587,70 @@ static int __init omap_rtc_probe(struct platform_device *pdev) * is write-only, and always reads as zero...) */ + if (new_ctrl & OMAP_RTC_CTRL_SPLIT) + dev_info(&pdev->dev, "split power mode\n"); + + if (reg != new_ctrl) + rtc_write(rtc, OMAP_RTC_CTRL_REG, new_ctrl); + device_init_wakeup(&pdev->dev, true); - if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT) - pr_info("%s: split power mode\n", pdev->name); + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &omap_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err; + } - if (reg != new_ctrl) - rtc_write(new_ctrl, OMAP_RTC_CTRL_REG); + /* handle periodic and alarm irqs */ + ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + + if (rtc->irq_timer != rtc->irq_alarm) { + ret = devm_request_irq(&pdev->dev, rtc->irq_alarm, rtc_irq, 0, + dev_name(&rtc->rtc->dev), rtc); + if (ret) + goto err; + } + + if (rtc->is_pmic_controller) { + if (!pm_power_off) { + omap_rtc_power_off_rtc = rtc; + pm_power_off = omap_rtc_power_off; + } + } return 0; -fail0: - if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) - rtc_writel(0, OMAP_RTC_KICK0_REG); +err: + device_init_wakeup(&pdev->dev, false); + if (rtc->type->has_kicker) + rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - return -EIO; + + return ret; } static int __exit omap_rtc_remove(struct platform_device *pdev) { - const struct platform_device_id *id_entry = - platform_get_device_id(pdev); + struct omap_rtc *rtc = platform_get_drvdata(pdev); + + if (pm_power_off == omap_rtc_power_off && + omap_rtc_power_off_rtc == rtc) { + pm_power_off = NULL; + omap_rtc_power_off_rtc = NULL; + } device_init_wakeup(&pdev->dev, 0); /* leave rtc running, but disable irqs */ - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); - if (id_entry->driver_data & OMAP_RTC_HAS_KICKER) - rtc_writel(0, OMAP_RTC_KICK0_REG); + if (rtc->type->has_kicker) + rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); /* Disable the clock/module */ pm_runtime_put_sync(&pdev->dev); @@ -521,20 +660,21 @@ static int __exit omap_rtc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static u8 irqstat; - static int omap_rtc_suspend(struct device *dev) { - irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG); + struct omap_rtc *rtc = dev_get_drvdata(dev); - /* FIXME the RTC alarm is not currently acting as a wakeup event + rtc->interrupts_reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + + /* + * FIXME: the RTC alarm is not currently acting as a wakeup event * source on some platforms, and in fact this enable() call is just * saving a flag that's never used... */ if (device_may_wakeup(dev)) - enable_irq_wake(omap_rtc_alarm); + enable_irq_wake(rtc->irq_alarm); else - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); /* Disable the clock/module */ pm_runtime_put_sync(dev); @@ -544,13 +684,15 @@ static int omap_rtc_suspend(struct device *dev) static int omap_rtc_resume(struct device *dev) { + struct omap_rtc *rtc = dev_get_drvdata(dev); + /* Enable the clock/module so that we can access the registers */ pm_runtime_get_sync(dev); if (device_may_wakeup(dev)) - disable_irq_wake(omap_rtc_alarm); + disable_irq_wake(rtc->irq_alarm); else - rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG); + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, rtc->interrupts_reg); return 0; } @@ -560,23 +702,32 @@ static SIMPLE_DEV_PM_OPS(omap_rtc_pm_ops, omap_rtc_suspend, omap_rtc_resume); static void omap_rtc_shutdown(struct platform_device *pdev) { - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 mask; + + /* + * Keep the ALARM interrupt enabled to allow the system to power up on + * alarm events. + */ + mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); + mask &= OMAP_RTC_INTERRUPTS_IT_ALARM; + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask); } -MODULE_ALIAS("platform:omap_rtc"); static struct platform_driver omap_rtc_driver = { .remove = __exit_p(omap_rtc_remove), .shutdown = omap_rtc_shutdown, .driver = { - .name = DRIVER_NAME, + .name = "omap_rtc", .owner = THIS_MODULE, .pm = &omap_rtc_pm_ops, .of_match_table = omap_rtc_of_match, }, - .id_table = omap_rtc_devtype, + .id_table = omap_rtc_id_table, }; module_platform_driver_probe(omap_rtc_driver, omap_rtc_probe); +MODULE_ALIAS("platform:omap_rtc"); MODULE_AUTHOR("George G. Davis (and others)"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index c2ef0a22ee94..96fb32e7d6f8 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -28,6 +28,7 @@ #define PCF8563_REG_ST2 0x01 #define PCF8563_BIT_AIE (1 << 1) #define PCF8563_BIT_AF (1 << 3) +#define PCF8563_BITS_ST2_N (7 << 5) #define PCF8563_REG_SC 0x02 /* datetime */ #define PCF8563_REG_MN 0x03 @@ -41,6 +42,13 @@ #define PCF8563_REG_CLKO 0x0D /* clock out */ #define PCF8563_REG_TMRC 0x0E /* timer control */ +#define PCF8563_TMRC_ENABLE BIT(7) +#define PCF8563_TMRC_4096 0 +#define PCF8563_TMRC_64 1 +#define PCF8563_TMRC_1 2 +#define PCF8563_TMRC_1_60 3 +#define PCF8563_TMRC_MASK 3 + #define PCF8563_REG_TMR 0x0F /* timer */ #define PCF8563_SC_LV 0x80 /* low voltage */ @@ -118,22 +126,21 @@ static int pcf8563_write_block_data(struct i2c_client *client, static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on) { - unsigned char buf[2]; + unsigned char buf; int err; - err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, buf + 1); + err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf); if (err < 0) return err; if (on) - buf[1] |= PCF8563_BIT_AIE; + buf |= PCF8563_BIT_AIE; else - buf[1] &= ~PCF8563_BIT_AIE; + buf &= ~PCF8563_BIT_AIE; - buf[1] &= ~PCF8563_BIT_AF; - buf[0] = PCF8563_REG_ST2; + buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N); - err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, buf + 1); + err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf); if (err < 0) { dev_err(&client->dev, "%s: write error\n", __func__); return -EIO; @@ -336,8 +343,8 @@ static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm) __func__, buf[0], buf[1], buf[2], buf[3]); tm->time.tm_min = bcd2bin(buf[0] & 0x7F); - tm->time.tm_hour = bcd2bin(buf[1] & 0x7F); - tm->time.tm_mday = bcd2bin(buf[2] & 0x1F); + tm->time.tm_hour = bcd2bin(buf[1] & 0x3F); + tm->time.tm_mday = bcd2bin(buf[2] & 0x3F); tm->time.tm_wday = bcd2bin(buf[3] & 0x7); tm->time.tm_mon = -1; tm->time.tm_year = -1; @@ -361,6 +368,14 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) struct i2c_client *client = to_i2c_client(dev); unsigned char buf[4]; int err; + unsigned long alarm_time; + + /* The alarm has no seconds, round up to nearest minute */ + if (tm->time.tm_sec) { + rtc_tm_to_time(&tm->time, &alarm_time); + alarm_time += 60-tm->time.tm_sec; + rtc_time_to_tm(alarm_time, &tm->time); + } dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d " "enabled=%d pending=%d\n", __func__, @@ -381,6 +396,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm) static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) { + dev_dbg(dev, "%s: en=%d\n", __func__, enabled); return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); } @@ -398,6 +414,8 @@ static int pcf8563_probe(struct i2c_client *client, { struct pcf8563 *pcf8563; int err; + unsigned char buf; + unsigned char alm_pending; dev_dbg(&client->dev, "%s\n", __func__); @@ -415,6 +433,22 @@ static int pcf8563_probe(struct i2c_client *client, pcf8563->client = client; device_set_wakeup_capable(&client->dev, 1); + /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */ + buf = PCF8563_TMRC_1_60; + err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); + return err; + } + + err = pcf8563_get_alarm_mode(client, NULL, &alm_pending); + if (err < 0) { + dev_err(&client->dev, "%s: read error\n", __func__); + return err; + } + if (alm_pending) + pcf8563_set_alarm_mode(client, 0); + pcf8563->rtc = devm_rtc_device_register(&client->dev, pcf8563_driver.driver.name, &pcf8563_rtc_ops, THIS_MODULE); @@ -435,6 +469,9 @@ static int pcf8563_probe(struct i2c_client *client, } + /* the pcf8563 alarm only supports a minute accuracy */ + pcf8563->rtc->uie_unsupported = 1; + return 0; } diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c index 76e38007ba90..d2ac6688e5c7 100644 --- a/drivers/rtc/rtc-sirfsoc.c +++ b/drivers/rtc/rtc-sirfsoc.c @@ -47,6 +47,7 @@ struct sirfsoc_rtc_drv { unsigned irq_wake; /* Overflow for every 8 years extra time */ u32 overflow_rtc; + spinlock_t lock; #ifdef CONFIG_PM u32 saved_counter; u32 saved_overflow_rtc; @@ -61,7 +62,7 @@ static int sirfsoc_rtc_read_alarm(struct device *dev, rtcdrv = dev_get_drvdata(dev); - local_irq_disable(); + spin_lock_irq(&rtcdrv->lock); rtc_count = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); @@ -84,7 +85,8 @@ static int sirfsoc_rtc_read_alarm(struct device *dev, if (sirfsoc_rtc_iobrg_readl( rtcdrv->rtc_base + RTC_STATUS) & SIRFSOC_RTC_AL0E) alrm->enabled = 1; - local_irq_enable(); + + spin_unlock_irq(&rtcdrv->lock); return 0; } @@ -99,7 +101,7 @@ static int sirfsoc_rtc_set_alarm(struct device *dev, if (alrm->enabled) { rtc_tm_to_time(&(alrm->time), &rtc_alarm); - local_irq_disable(); + spin_lock_irq(&rtcdrv->lock); rtc_status_reg = sirfsoc_rtc_iobrg_readl( rtcdrv->rtc_base + RTC_STATUS); @@ -123,14 +125,15 @@ static int sirfsoc_rtc_set_alarm(struct device *dev, rtc_status_reg |= SIRFSOC_RTC_AL0E; sirfsoc_rtc_iobrg_writel( rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS); - local_irq_enable(); + + spin_unlock_irq(&rtcdrv->lock); } else { /* * if this function was called with enabled=0 * then it could mean that the application is * trying to cancel an ongoing alarm */ - local_irq_disable(); + spin_lock_irq(&rtcdrv->lock); rtc_status_reg = sirfsoc_rtc_iobrg_readl( rtcdrv->rtc_base + RTC_STATUS); @@ -146,7 +149,7 @@ static int sirfsoc_rtc_set_alarm(struct device *dev, rtcdrv->rtc_base + RTC_STATUS); } - local_irq_enable(); + spin_unlock_irq(&rtcdrv->lock); } return 0; @@ -209,12 +212,38 @@ static int sirfsoc_rtc_ioctl(struct device *dev, unsigned int cmd, } } +static int sirfsoc_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + unsigned long rtc_status_reg = 0x0; + struct sirfsoc_rtc_drv *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + + spin_lock_irq(&rtcdrv->lock); + + rtc_status_reg = sirfsoc_rtc_iobrg_readl( + rtcdrv->rtc_base + RTC_STATUS); + if (enabled) + rtc_status_reg |= SIRFSOC_RTC_AL0E; + else + rtc_status_reg &= ~SIRFSOC_RTC_AL0E; + + sirfsoc_rtc_iobrg_writel(rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS); + + spin_unlock_irq(&rtcdrv->lock); + + return 0; + +} + static const struct rtc_class_ops sirfsoc_rtc_ops = { .read_time = sirfsoc_rtc_read_time, .set_time = sirfsoc_rtc_set_time, .read_alarm = sirfsoc_rtc_read_alarm, .set_alarm = sirfsoc_rtc_set_alarm, - .ioctl = sirfsoc_rtc_ioctl + .ioctl = sirfsoc_rtc_ioctl, + .alarm_irq_enable = sirfsoc_rtc_alarm_irq_enable }; static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata) @@ -223,6 +252,8 @@ static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata) unsigned long rtc_status_reg = 0x0; unsigned long events = 0x0; + spin_lock(&rtcdrv->lock); + rtc_status_reg = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_STATUS); /* this bit will be set ONLY if an alarm was active * and it expired NOW @@ -240,6 +271,9 @@ static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata) rtc_status_reg &= ~(SIRFSOC_RTC_AL0E); } sirfsoc_rtc_iobrg_writel(rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS); + + spin_unlock(&rtcdrv->lock); + /* this should wake up any apps polling/waiting on the read * after setting the alarm */ @@ -267,6 +301,8 @@ static int sirfsoc_rtc_probe(struct platform_device *pdev) if (rtcdrv == NULL) return -ENOMEM; + spin_lock_init(&rtcdrv->lock); + err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base); if (err) { dev_err(&pdev->dev, "unable to find base address of rtc node in dtb\n"); @@ -286,14 +322,6 @@ static int sirfsoc_rtc_probe(struct platform_device *pdev) rtc_div = ((32768 / RTC_HZ) / 2) - 1; sirfsoc_rtc_iobrg_writel(rtc_div, rtcdrv->rtc_base + RTC_DIV); - rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, - &sirfsoc_rtc_ops, THIS_MODULE); - if (IS_ERR(rtcdrv->rtc)) { - err = PTR_ERR(rtcdrv->rtc); - dev_err(&pdev->dev, "can't register RTC device\n"); - return err; - } - /* 0x3 -> RTC_CLK */ sirfsoc_rtc_iobrg_writel(SIRFSOC_RTC_CLK, rtcdrv->rtc_base + RTC_CLOCK_SWITCH); @@ -308,6 +336,14 @@ static int sirfsoc_rtc_probe(struct platform_device *pdev) rtcdrv->overflow_rtc = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE); + rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &sirfsoc_rtc_ops, THIS_MODULE); + if (IS_ERR(rtcdrv->rtc)) { + err = PTR_ERR(rtcdrv->rtc); + dev_err(&pdev->dev, "can't register RTC device\n"); + return err; + } + rtcdrv->irq = platform_get_irq(pdev, 0); err = devm_request_irq( &pdev->dev, diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index fa384fe28988..2cd8ffe5c698 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -17,6 +17,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/rtc.h> +#include <linux/clk.h> /* These register offsets are relative to LP (Low Power) range */ #define SNVS_LPCR 0x04 @@ -39,6 +40,7 @@ struct snvs_rtc_data { void __iomem *ioaddr; int irq; spinlock_t lock; + struct clk *clk; }; static u32 rtc_read_lp_counter(void __iomem *ioaddr) @@ -260,6 +262,18 @@ static int snvs_rtc_probe(struct platform_device *pdev) if (data->irq < 0) return data->irq; + data->clk = devm_clk_get(&pdev->dev, "snvs-rtc"); + if (IS_ERR(data->clk)) { + data->clk = NULL; + } else { + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the snvs clock\n"); + return ret; + } + } + platform_set_drvdata(pdev, data); spin_lock_init(&data->lock); @@ -280,7 +294,7 @@ static int snvs_rtc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "failed to request irq %d: %d\n", data->irq, ret); - return ret; + goto error_rtc_device_register; } data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, @@ -288,10 +302,16 @@ static int snvs_rtc_probe(struct platform_device *pdev) if (IS_ERR(data->rtc)) { ret = PTR_ERR(data->rtc); dev_err(&pdev->dev, "failed to register rtc: %d\n", ret); - return ret; + goto error_rtc_device_register; } return 0; + +error_rtc_device_register: + if (data->clk) + clk_disable_unprepare(data->clk); + + return ret; } #ifdef CONFIG_PM_SLEEP @@ -302,21 +322,34 @@ static int snvs_rtc_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(data->irq); + if (data->clk) + clk_disable_unprepare(data->clk); + return 0; } static int snvs_rtc_resume(struct device *dev) { struct snvs_rtc_data *data = dev_get_drvdata(dev); + int ret; if (device_may_wakeup(dev)) disable_irq_wake(data->irq); + if (data->clk) { + ret = clk_prepare_enable(data->clk); + if (ret) + return ret; + } + return 0; } #endif -static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume); +static const struct dev_pm_ops snvs_rtc_pm_ops = { + .suspend_noirq = snvs_rtc_suspend, + .resume_noirq = snvs_rtc_resume, +}; static const struct of_device_id snvs_dt_ids[] = { { .compatible = "fsl,sec-v4.0-mon-rtc-lp", }, |