167561a8eSNobuhiro Iwamatsu // SPDX-License-Identifier: GPL-2.0-only 2bf4994d7SScott Wood /* 3bf4994d7SScott Wood * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C 4bf4994d7SScott Wood * 5bf4994d7SScott Wood * Based on code by Randy Vinson <rvinson@mvista.com>, 6bf4994d7SScott Wood * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>. 7bf4994d7SScott Wood * 8920f91e5SSøren Andersen * Copyright (C) 2014 Rose Technology 9bf4994d7SScott Wood * Copyright (C) 2006-2007 Freescale Semiconductor 1067561a8eSNobuhiro Iwamatsu * Copyright (c) 2005 MontaVista Software, Inc. 11bf4994d7SScott Wood */ 12bf4994d7SScott Wood /* 13bf4994d7SScott Wood * It would be more efficient to use i2c msgs/i2c_transfer directly but, as 14ccf988b6SMauro Carvalho Chehab * recommended in .../Documentation/i2c/writing-clients.rst section 15bf4994d7SScott Wood * "Sending and receiving", using SMBus level communication is preferred. 16bf4994d7SScott Wood */ 17bf4994d7SScott Wood 18a737e835SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19a737e835SJoe Perches 20bf4994d7SScott Wood #include <linux/kernel.h> 21bf4994d7SScott Wood #include <linux/module.h> 22bf4994d7SScott Wood #include <linux/interrupt.h> 23bf4994d7SScott Wood #include <linux/i2c.h> 24bf4994d7SScott Wood #include <linux/rtc.h> 25bf4994d7SScott Wood #include <linux/bcd.h> 26bf4994d7SScott Wood #include <linux/workqueue.h> 275a0e3ad6STejun Heo #include <linux/slab.h> 28bc96ba74SMark Brown #include <linux/pm.h> 29920f91e5SSøren Andersen #ifdef CONFIG_RTC_DRV_DS1374_WDT 30920f91e5SSøren Andersen #include <linux/fs.h> 31920f91e5SSøren Andersen #include <linux/ioctl.h> 32920f91e5SSøren Andersen #include <linux/miscdevice.h> 33920f91e5SSøren Andersen #include <linux/reboot.h> 34920f91e5SSøren Andersen #include <linux/watchdog.h> 35920f91e5SSøren Andersen #endif 36bf4994d7SScott Wood 37bf4994d7SScott Wood #define DS1374_REG_TOD0 0x00 /* Time of Day */ 38bf4994d7SScott Wood #define DS1374_REG_TOD1 0x01 39bf4994d7SScott Wood #define DS1374_REG_TOD2 0x02 40bf4994d7SScott Wood #define DS1374_REG_TOD3 0x03 41bf4994d7SScott Wood #define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */ 42bf4994d7SScott Wood #define DS1374_REG_WDALM1 0x05 43bf4994d7SScott Wood #define DS1374_REG_WDALM2 0x06 44bf4994d7SScott Wood #define DS1374_REG_CR 0x07 /* Control */ 45bf4994d7SScott Wood #define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */ 46d3de4bebSJohnson CH Chen (陳昭勳) #define DS1374_REG_CR_WDSTR 0x08 /* 1=INT, 0=RST */ 47bf4994d7SScott Wood #define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */ 48bf4994d7SScott Wood #define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */ 49bf4994d7SScott Wood #define DS1374_REG_SR 0x08 /* Status */ 50bf4994d7SScott Wood #define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */ 51bf4994d7SScott Wood #define DS1374_REG_SR_AF 0x01 /* Alarm Flag */ 52bf4994d7SScott Wood #define DS1374_REG_TCR 0x09 /* Trickle Charge */ 53bf4994d7SScott Wood 543760f736SJean Delvare static const struct i2c_device_id ds1374_id[] = { 55a47d377eSUwe Kleine-König { "ds1374" }, 563760f736SJean Delvare { } 573760f736SJean Delvare }; 583760f736SJean Delvare MODULE_DEVICE_TABLE(i2c, ds1374_id); 593760f736SJean Delvare 60920f91e5SSøren Andersen #ifdef CONFIG_OF 61920f91e5SSøren Andersen static const struct of_device_id ds1374_of_match[] = { 62920f91e5SSøren Andersen { .compatible = "dallas,ds1374" }, 63920f91e5SSøren Andersen { } 64920f91e5SSøren Andersen }; 65920f91e5SSøren Andersen MODULE_DEVICE_TABLE(of, ds1374_of_match); 66920f91e5SSøren Andersen #endif 67920f91e5SSøren Andersen 68bf4994d7SScott Wood struct ds1374 { 69bf4994d7SScott Wood struct i2c_client *client; 70bf4994d7SScott Wood struct rtc_device *rtc; 71bf4994d7SScott Wood struct work_struct work; 72d3de4bebSJohnson CH Chen (陳昭勳) #ifdef CONFIG_RTC_DRV_DS1374_WDT 73d3de4bebSJohnson CH Chen (陳昭勳) struct watchdog_device wdt; 74d3de4bebSJohnson CH Chen (陳昭勳) #endif 75bf4994d7SScott Wood /* The mutex protects alarm operations, and prevents a race 76bf4994d7SScott Wood * between the enable_irq() in the workqueue and the free_irq() 77bf4994d7SScott Wood * in the remove function. 78bf4994d7SScott Wood */ 79bf4994d7SScott Wood struct mutex mutex; 80bf4994d7SScott Wood int exiting; 81bf4994d7SScott Wood }; 82bf4994d7SScott Wood 83bf4994d7SScott Wood static struct i2c_driver ds1374_driver; 84bf4994d7SScott Wood 85bf4994d7SScott Wood static int ds1374_read_rtc(struct i2c_client *client, u32 *time, 86bf4994d7SScott Wood int reg, int nbytes) 87bf4994d7SScott Wood { 88bf4994d7SScott Wood u8 buf[4]; 89bf4994d7SScott Wood int ret; 90bf4994d7SScott Wood int i; 91bf4994d7SScott Wood 9201835fadSSrikant Ritolia if (WARN_ON(nbytes > 4)) 93bf4994d7SScott Wood return -EINVAL; 94bf4994d7SScott Wood 95bf4994d7SScott Wood ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf); 96bf4994d7SScott Wood 97bf4994d7SScott Wood if (ret < 0) 98bf4994d7SScott Wood return ret; 99bf4994d7SScott Wood if (ret < nbytes) 100bf4994d7SScott Wood return -EIO; 101bf4994d7SScott Wood 102bf4994d7SScott Wood for (i = nbytes - 1, *time = 0; i >= 0; i--) 103bf4994d7SScott Wood *time = (*time << 8) | buf[i]; 104bf4994d7SScott Wood 105bf4994d7SScott Wood return 0; 106bf4994d7SScott Wood } 107bf4994d7SScott Wood 108bf4994d7SScott Wood static int ds1374_write_rtc(struct i2c_client *client, u32 time, 109bf4994d7SScott Wood int reg, int nbytes) 110bf4994d7SScott Wood { 111bf4994d7SScott Wood u8 buf[4]; 112bf4994d7SScott Wood int i; 113bf4994d7SScott Wood 114bf4994d7SScott Wood if (nbytes > 4) { 115bf4994d7SScott Wood WARN_ON(1); 116bf4994d7SScott Wood return -EINVAL; 117bf4994d7SScott Wood } 118bf4994d7SScott Wood 119bf4994d7SScott Wood for (i = 0; i < nbytes; i++) { 120bf4994d7SScott Wood buf[i] = time & 0xff; 121bf4994d7SScott Wood time >>= 8; 122bf4994d7SScott Wood } 123bf4994d7SScott Wood 124bf4994d7SScott Wood return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf); 125bf4994d7SScott Wood } 126bf4994d7SScott Wood 127bf4994d7SScott Wood static int ds1374_check_rtc_status(struct i2c_client *client) 128bf4994d7SScott Wood { 129bf4994d7SScott Wood int ret = 0; 130bf4994d7SScott Wood int control, stat; 131bf4994d7SScott Wood 132bf4994d7SScott Wood stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR); 133bf4994d7SScott Wood if (stat < 0) 134bf4994d7SScott Wood return stat; 135bf4994d7SScott Wood 136bf4994d7SScott Wood if (stat & DS1374_REG_SR_OSF) 137bf4994d7SScott Wood dev_warn(&client->dev, 138adc7b9b6SSachin Kamat "oscillator discontinuity flagged, time unreliable\n"); 139bf4994d7SScott Wood 140bf4994d7SScott Wood stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF); 141bf4994d7SScott Wood 142bf4994d7SScott Wood ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat); 143bf4994d7SScott Wood if (ret < 0) 144bf4994d7SScott Wood return ret; 145bf4994d7SScott Wood 146bf4994d7SScott Wood /* If the alarm is pending, clear it before requesting 147bf4994d7SScott Wood * the interrupt, so an interrupt event isn't reported 148bf4994d7SScott Wood * before everything is initialized. 149bf4994d7SScott Wood */ 150bf4994d7SScott Wood 151bf4994d7SScott Wood control = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 152bf4994d7SScott Wood if (control < 0) 153bf4994d7SScott Wood return control; 154bf4994d7SScott Wood 155bf4994d7SScott Wood control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE); 156bf4994d7SScott Wood return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control); 157bf4994d7SScott Wood } 158bf4994d7SScott Wood 159bf4994d7SScott Wood static int ds1374_read_time(struct device *dev, struct rtc_time *time) 160bf4994d7SScott Wood { 161bf4994d7SScott Wood struct i2c_client *client = to_i2c_client(dev); 162bf4994d7SScott Wood u32 itime; 163bf4994d7SScott Wood int ret; 164bf4994d7SScott Wood 165bf4994d7SScott Wood ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4); 166bf4994d7SScott Wood if (!ret) 167ca824be9SAlexandre Belloni rtc_time64_to_tm(itime, time); 168bf4994d7SScott Wood 169bf4994d7SScott Wood return ret; 170bf4994d7SScott Wood } 171bf4994d7SScott Wood 172bf4994d7SScott Wood static int ds1374_set_time(struct device *dev, struct rtc_time *time) 173bf4994d7SScott Wood { 174bf4994d7SScott Wood struct i2c_client *client = to_i2c_client(dev); 175ca824be9SAlexandre Belloni unsigned long itime = rtc_tm_to_time64(time); 176bf4994d7SScott Wood 177bf4994d7SScott Wood return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4); 178bf4994d7SScott Wood } 179bf4994d7SScott Wood 180920f91e5SSøren Andersen #ifndef CONFIG_RTC_DRV_DS1374_WDT 181bf4994d7SScott Wood /* The ds1374 has a decrementer for an alarm, rather than a comparator. 182bf4994d7SScott Wood * If the time of day is changed, then the alarm will need to be 183bf4994d7SScott Wood * reset. 184bf4994d7SScott Wood */ 185bf4994d7SScott Wood static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) 186bf4994d7SScott Wood { 187bf4994d7SScott Wood struct i2c_client *client = to_i2c_client(dev); 188bf4994d7SScott Wood struct ds1374 *ds1374 = i2c_get_clientdata(client); 189bf4994d7SScott Wood u32 now, cur_alarm; 190bf4994d7SScott Wood int cr, sr; 191bf4994d7SScott Wood int ret = 0; 192bf4994d7SScott Wood 193b42f9317SAnton Vorontsov if (client->irq <= 0) 194bf4994d7SScott Wood return -EINVAL; 195bf4994d7SScott Wood 196bf4994d7SScott Wood mutex_lock(&ds1374->mutex); 197bf4994d7SScott Wood 198bf4994d7SScott Wood cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 199bf4994d7SScott Wood if (ret < 0) 200bf4994d7SScott Wood goto out; 201bf4994d7SScott Wood 202bf4994d7SScott Wood sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR); 203bf4994d7SScott Wood if (ret < 0) 204bf4994d7SScott Wood goto out; 205bf4994d7SScott Wood 206bf4994d7SScott Wood ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4); 207bf4994d7SScott Wood if (ret) 208bf4994d7SScott Wood goto out; 209bf4994d7SScott Wood 210bf4994d7SScott Wood ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3); 211bf4994d7SScott Wood if (ret) 212bf4994d7SScott Wood goto out; 213bf4994d7SScott Wood 214ca824be9SAlexandre Belloni rtc_time64_to_tm(now + cur_alarm, &alarm->time); 215bf4994d7SScott Wood alarm->enabled = !!(cr & DS1374_REG_CR_WACE); 216bf4994d7SScott Wood alarm->pending = !!(sr & DS1374_REG_SR_AF); 217bf4994d7SScott Wood 218bf4994d7SScott Wood out: 219bf4994d7SScott Wood mutex_unlock(&ds1374->mutex); 220bf4994d7SScott Wood return ret; 221bf4994d7SScott Wood } 222bf4994d7SScott Wood 223bf4994d7SScott Wood static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) 224bf4994d7SScott Wood { 225bf4994d7SScott Wood struct i2c_client *client = to_i2c_client(dev); 226bf4994d7SScott Wood struct ds1374 *ds1374 = i2c_get_clientdata(client); 227bf4994d7SScott Wood struct rtc_time now; 228bf4994d7SScott Wood unsigned long new_alarm, itime; 229bf4994d7SScott Wood int cr; 230bf4994d7SScott Wood int ret = 0; 231bf4994d7SScott Wood 232b42f9317SAnton Vorontsov if (client->irq <= 0) 233bf4994d7SScott Wood return -EINVAL; 234bf4994d7SScott Wood 235bf4994d7SScott Wood ret = ds1374_read_time(dev, &now); 236bf4994d7SScott Wood if (ret < 0) 237bf4994d7SScott Wood return ret; 238bf4994d7SScott Wood 239ca824be9SAlexandre Belloni new_alarm = rtc_tm_to_time64(&alarm->time); 240ca824be9SAlexandre Belloni itime = rtc_tm_to_time64(&now); 241bf4994d7SScott Wood 242bf4994d7SScott Wood /* This can happen due to races, in addition to dates that are 243bf4994d7SScott Wood * truly in the past. To avoid requiring the caller to check for 244bf4994d7SScott Wood * races, dates in the past are assumed to be in the recent past 245bf4994d7SScott Wood * (i.e. not something that we'd rather the caller know about via 246bf4994d7SScott Wood * an error), and the alarm is set to go off as soon as possible. 247bf4994d7SScott Wood */ 248fa7af8b1SRoel Kluin if (time_before_eq(new_alarm, itime)) 249bf4994d7SScott Wood new_alarm = 1; 250fa7af8b1SRoel Kluin else 251fa7af8b1SRoel Kluin new_alarm -= itime; 252bf4994d7SScott Wood 253bf4994d7SScott Wood mutex_lock(&ds1374->mutex); 254bf4994d7SScott Wood 255bf4994d7SScott Wood ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 256bf4994d7SScott Wood if (ret < 0) 257bf4994d7SScott Wood goto out; 258bf4994d7SScott Wood 259bf4994d7SScott Wood /* Disable any existing alarm before setting the new one 260bf4994d7SScott Wood * (or lack thereof). */ 261bf4994d7SScott Wood cr &= ~DS1374_REG_CR_WACE; 262bf4994d7SScott Wood 263bf4994d7SScott Wood ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); 264bf4994d7SScott Wood if (ret < 0) 265bf4994d7SScott Wood goto out; 266bf4994d7SScott Wood 267bf4994d7SScott Wood ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3); 268bf4994d7SScott Wood if (ret) 269bf4994d7SScott Wood goto out; 270bf4994d7SScott Wood 271bf4994d7SScott Wood if (alarm->enabled) { 272bf4994d7SScott Wood cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE; 273bf4994d7SScott Wood cr &= ~DS1374_REG_CR_WDALM; 274bf4994d7SScott Wood 275bf4994d7SScott Wood ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); 276bf4994d7SScott Wood } 277bf4994d7SScott Wood 278bf4994d7SScott Wood out: 279bf4994d7SScott Wood mutex_unlock(&ds1374->mutex); 280bf4994d7SScott Wood return ret; 281bf4994d7SScott Wood } 282920f91e5SSøren Andersen #endif 283bf4994d7SScott Wood 284bf4994d7SScott Wood static irqreturn_t ds1374_irq(int irq, void *dev_id) 285bf4994d7SScott Wood { 286bf4994d7SScott Wood struct i2c_client *client = dev_id; 287bf4994d7SScott Wood struct ds1374 *ds1374 = i2c_get_clientdata(client); 288bf4994d7SScott Wood 289bf4994d7SScott Wood disable_irq_nosync(irq); 290bf4994d7SScott Wood schedule_work(&ds1374->work); 291bf4994d7SScott Wood return IRQ_HANDLED; 292bf4994d7SScott Wood } 293bf4994d7SScott Wood 294bf4994d7SScott Wood static void ds1374_work(struct work_struct *work) 295bf4994d7SScott Wood { 296bf4994d7SScott Wood struct ds1374 *ds1374 = container_of(work, struct ds1374, work); 297bf4994d7SScott Wood struct i2c_client *client = ds1374->client; 298bf4994d7SScott Wood int stat, control; 299bf4994d7SScott Wood 300bf4994d7SScott Wood mutex_lock(&ds1374->mutex); 301bf4994d7SScott Wood 302bf4994d7SScott Wood stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR); 303bf4994d7SScott Wood if (stat < 0) 30428df30e6SJiri Slaby goto unlock; 305bf4994d7SScott Wood 306bf4994d7SScott Wood if (stat & DS1374_REG_SR_AF) { 307bf4994d7SScott Wood stat &= ~DS1374_REG_SR_AF; 308bf4994d7SScott Wood i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat); 309bf4994d7SScott Wood 310bf4994d7SScott Wood control = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 311bf4994d7SScott Wood if (control < 0) 312bf4994d7SScott Wood goto out; 313bf4994d7SScott Wood 314bf4994d7SScott Wood control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE); 315bf4994d7SScott Wood i2c_smbus_write_byte_data(client, DS1374_REG_CR, control); 316bf4994d7SScott Wood 317bf4994d7SScott Wood rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF); 318bf4994d7SScott Wood } 319bf4994d7SScott Wood 320bf4994d7SScott Wood out: 321bf4994d7SScott Wood if (!ds1374->exiting) 322bf4994d7SScott Wood enable_irq(client->irq); 32328df30e6SJiri Slaby unlock: 324bf4994d7SScott Wood mutex_unlock(&ds1374->mutex); 325bf4994d7SScott Wood } 326bf4994d7SScott Wood 327920f91e5SSøren Andersen #ifndef CONFIG_RTC_DRV_DS1374_WDT 32816380c15SJohn Stultz static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled) 329bf4994d7SScott Wood { 330bf4994d7SScott Wood struct i2c_client *client = to_i2c_client(dev); 331bf4994d7SScott Wood struct ds1374 *ds1374 = i2c_get_clientdata(client); 33216380c15SJohn Stultz int ret; 333bf4994d7SScott Wood 334bf4994d7SScott Wood mutex_lock(&ds1374->mutex); 335bf4994d7SScott Wood 336bf4994d7SScott Wood ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 337bf4994d7SScott Wood if (ret < 0) 338bf4994d7SScott Wood goto out; 339bf4994d7SScott Wood 34016380c15SJohn Stultz if (enabled) { 341bf4994d7SScott Wood ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE; 342bf4994d7SScott Wood ret &= ~DS1374_REG_CR_WDALM; 34316380c15SJohn Stultz } else { 34416380c15SJohn Stultz ret &= ~DS1374_REG_CR_WACE; 345bf4994d7SScott Wood } 34616380c15SJohn Stultz ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret); 347bf4994d7SScott Wood 348bf4994d7SScott Wood out: 349bf4994d7SScott Wood mutex_unlock(&ds1374->mutex); 350bf4994d7SScott Wood return ret; 351bf4994d7SScott Wood } 352920f91e5SSøren Andersen #endif 353bf4994d7SScott Wood 354bf4994d7SScott Wood static const struct rtc_class_ops ds1374_rtc_ops = { 355bf4994d7SScott Wood .read_time = ds1374_read_time, 356bf4994d7SScott Wood .set_time = ds1374_set_time, 357920f91e5SSøren Andersen #ifndef CONFIG_RTC_DRV_DS1374_WDT 358bf4994d7SScott Wood .read_alarm = ds1374_read_alarm, 359bf4994d7SScott Wood .set_alarm = ds1374_set_alarm, 36016380c15SJohn Stultz .alarm_irq_enable = ds1374_alarm_irq_enable, 361920f91e5SSøren Andersen #endif 362bf4994d7SScott Wood }; 363bf4994d7SScott Wood 364920f91e5SSøren Andersen #ifdef CONFIG_RTC_DRV_DS1374_WDT 365920f91e5SSøren Andersen /* 366920f91e5SSøren Andersen ***************************************************************************** 367920f91e5SSøren Andersen * 368920f91e5SSøren Andersen * Watchdog Driver 369920f91e5SSøren Andersen * 370920f91e5SSøren Andersen ***************************************************************************** 371920f91e5SSøren Andersen */ 372920f91e5SSøren Andersen /* Default margin */ 373d3de4bebSJohnson CH Chen (陳昭勳) #define TIMER_MARGIN_DEFAULT 32 374d3de4bebSJohnson CH Chen (陳昭勳) #define TIMER_MARGIN_MIN 1 375d3de4bebSJohnson CH Chen (陳昭勳) #define TIMER_MARGIN_MAX 4095 /* 24-bit value */ 376920f91e5SSøren Andersen 377d3de4bebSJohnson CH Chen (陳昭勳) static int wdt_margin; 378920f91e5SSøren Andersen module_param(wdt_margin, int, 0); 379920f91e5SSøren Andersen MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)"); 380920f91e5SSøren Andersen 381d3de4bebSJohnson CH Chen (陳昭勳) static bool nowayout = WATCHDOG_NOWAYOUT; 382d3de4bebSJohnson CH Chen (陳昭勳) module_param(nowayout, bool, 0); 383d3de4bebSJohnson CH Chen (陳昭勳) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default =" 384d3de4bebSJohnson CH Chen (陳昭勳) __MODULE_STRING(WATCHDOG_NOWAYOUT)")"); 385d3de4bebSJohnson CH Chen (陳昭勳) 386920f91e5SSøren Andersen static const struct watchdog_info ds1374_wdt_info = { 3873d6cfb36SAlexandre Belloni .identity = "DS1374 Watchdog", 388920f91e5SSøren Andersen .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 389920f91e5SSøren Andersen WDIOF_MAGICCLOSE, 390920f91e5SSøren Andersen }; 391920f91e5SSøren Andersen 392d3de4bebSJohnson CH Chen (陳昭勳) static int ds1374_wdt_settimeout(struct watchdog_device *wdt, unsigned int timeout) 393920f91e5SSøren Andersen { 394d3de4bebSJohnson CH Chen (陳昭勳) struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); 395d3de4bebSJohnson CH Chen (陳昭勳) struct i2c_client *client = ds1374->client; 396d3de4bebSJohnson CH Chen (陳昭勳) int ret, cr; 397920f91e5SSøren Andersen 398d3de4bebSJohnson CH Chen (陳昭勳) wdt->timeout = timeout; 399d3de4bebSJohnson CH Chen (陳昭勳) 400d3de4bebSJohnson CH Chen (陳昭勳) cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 401d3de4bebSJohnson CH Chen (陳昭勳) if (cr < 0) 402d3de4bebSJohnson CH Chen (陳昭勳) return cr; 403920f91e5SSøren Andersen 404920f91e5SSøren Andersen /* Disable any existing watchdog/alarm before setting the new one */ 405920f91e5SSøren Andersen cr &= ~DS1374_REG_CR_WACE; 406920f91e5SSøren Andersen 407d3de4bebSJohnson CH Chen (陳昭勳) ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); 408920f91e5SSøren Andersen if (ret < 0) 409d3de4bebSJohnson CH Chen (陳昭勳) return ret; 410920f91e5SSøren Andersen 411920f91e5SSøren Andersen /* Set new watchdog time */ 412d3de4bebSJohnson CH Chen (陳昭勳) timeout = timeout * 4096; 413d3de4bebSJohnson CH Chen (陳昭勳) ret = ds1374_write_rtc(client, timeout, DS1374_REG_WDALM0, 3); 414d3de4bebSJohnson CH Chen (陳昭勳) if (ret) 415d3de4bebSJohnson CH Chen (陳昭勳) return ret; 416920f91e5SSøren Andersen 417920f91e5SSøren Andersen /* Enable watchdog timer */ 418920f91e5SSøren Andersen cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM; 419d3de4bebSJohnson CH Chen (陳昭勳) cr &= ~DS1374_REG_CR_WDSTR;/* for RST PIN */ 420920f91e5SSøren Andersen cr &= ~DS1374_REG_CR_AIE; 421920f91e5SSøren Andersen 422d3de4bebSJohnson CH Chen (陳昭勳) ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); 423920f91e5SSøren Andersen if (ret < 0) 424d3de4bebSJohnson CH Chen (陳昭勳) return ret; 425920f91e5SSøren Andersen 426920f91e5SSøren Andersen return 0; 427920f91e5SSøren Andersen } 428920f91e5SSøren Andersen 429920f91e5SSøren Andersen /* 430920f91e5SSøren Andersen * Reload the watchdog timer. (ie, pat the watchdog) 431920f91e5SSøren Andersen */ 432d3de4bebSJohnson CH Chen (陳昭勳) static int ds1374_wdt_start(struct watchdog_device *wdt) 433920f91e5SSøren Andersen { 434d3de4bebSJohnson CH Chen (陳昭勳) struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); 435920f91e5SSøren Andersen u32 val; 436920f91e5SSøren Andersen 437d3de4bebSJohnson CH Chen (陳昭勳) return ds1374_read_rtc(ds1374->client, &val, DS1374_REG_WDALM0, 3); 438920f91e5SSøren Andersen } 439920f91e5SSøren Andersen 440d3de4bebSJohnson CH Chen (陳昭勳) static int ds1374_wdt_stop(struct watchdog_device *wdt) 441920f91e5SSøren Andersen { 442d3de4bebSJohnson CH Chen (陳昭勳) struct ds1374 *ds1374 = watchdog_get_drvdata(wdt); 443d3de4bebSJohnson CH Chen (陳昭勳) struct i2c_client *client = ds1374->client; 444920f91e5SSøren Andersen int cr; 445920f91e5SSøren Andersen 446d3de4bebSJohnson CH Chen (陳昭勳) cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR); 447d3de4bebSJohnson CH Chen (陳昭勳) if (cr < 0) 448d3de4bebSJohnson CH Chen (陳昭勳) return cr; 449d3de4bebSJohnson CH Chen (陳昭勳) 450920f91e5SSøren Andersen /* Disable watchdog timer */ 451920f91e5SSøren Andersen cr &= ~DS1374_REG_CR_WACE; 452920f91e5SSøren Andersen 453d3de4bebSJohnson CH Chen (陳昭勳) return i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr); 454920f91e5SSøren Andersen } 455920f91e5SSøren Andersen 456d3de4bebSJohnson CH Chen (陳昭勳) static const struct watchdog_ops ds1374_wdt_ops = { 457920f91e5SSøren Andersen .owner = THIS_MODULE, 458d3de4bebSJohnson CH Chen (陳昭勳) .start = ds1374_wdt_start, 459d3de4bebSJohnson CH Chen (陳昭勳) .stop = ds1374_wdt_stop, 460d3de4bebSJohnson CH Chen (陳昭勳) .set_timeout = ds1374_wdt_settimeout, 461920f91e5SSøren Andersen }; 462920f91e5SSøren Andersen #endif /*CONFIG_RTC_DRV_DS1374_WDT*/ 463920f91e5SSøren Andersen /* 464920f91e5SSøren Andersen ***************************************************************************** 465920f91e5SSøren Andersen * 466920f91e5SSøren Andersen * Driver Interface 467920f91e5SSøren Andersen * 468920f91e5SSøren Andersen ***************************************************************************** 469920f91e5SSøren Andersen */ 4703f4a3322SStephen Kitt static int ds1374_probe(struct i2c_client *client) 471bf4994d7SScott Wood { 472bf4994d7SScott Wood struct ds1374 *ds1374; 473bf4994d7SScott Wood int ret; 474bf4994d7SScott Wood 475d1a96639SSachin Kamat ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL); 476bf4994d7SScott Wood if (!ds1374) 477bf4994d7SScott Wood return -ENOMEM; 478bf4994d7SScott Wood 479c11af813SAlexandre Belloni ds1374->rtc = devm_rtc_allocate_device(&client->dev); 480c11af813SAlexandre Belloni if (IS_ERR(ds1374->rtc)) 481c11af813SAlexandre Belloni return PTR_ERR(ds1374->rtc); 482c11af813SAlexandre Belloni 483bf4994d7SScott Wood ds1374->client = client; 484bf4994d7SScott Wood i2c_set_clientdata(client, ds1374); 485bf4994d7SScott Wood 486bf4994d7SScott Wood INIT_WORK(&ds1374->work, ds1374_work); 487bf4994d7SScott Wood mutex_init(&ds1374->mutex); 488bf4994d7SScott Wood 489bf4994d7SScott Wood ret = ds1374_check_rtc_status(client); 490bf4994d7SScott Wood if (ret) 491d1a96639SSachin Kamat return ret; 492bf4994d7SScott Wood 493b42f9317SAnton Vorontsov if (client->irq > 0) { 494d1a96639SSachin Kamat ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0, 495bf4994d7SScott Wood "ds1374", client); 496bf4994d7SScott Wood if (ret) { 497bf4994d7SScott Wood dev_err(&client->dev, "unable to request IRQ\n"); 498d1a96639SSachin Kamat return ret; 499bf4994d7SScott Wood } 50026b3c01fSAnton Vorontsov 50126b3c01fSAnton Vorontsov device_set_wakeup_capable(&client->dev, 1); 502bf4994d7SScott Wood } 503bf4994d7SScott Wood 504c11af813SAlexandre Belloni ds1374->rtc->ops = &ds1374_rtc_ops; 5054136ff3aSAlexandre Belloni ds1374->rtc->range_max = U32_MAX; 506c11af813SAlexandre Belloni 507fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(ds1374->rtc); 508c11af813SAlexandre Belloni if (ret) 509c11af813SAlexandre Belloni return ret; 510bf4994d7SScott Wood 511920f91e5SSøren Andersen #ifdef CONFIG_RTC_DRV_DS1374_WDT 512d3de4bebSJohnson CH Chen (陳昭勳) ds1374->wdt.info = &ds1374_wdt_info; 513d3de4bebSJohnson CH Chen (陳昭勳) ds1374->wdt.ops = &ds1374_wdt_ops; 514d3de4bebSJohnson CH Chen (陳昭勳) ds1374->wdt.timeout = TIMER_MARGIN_DEFAULT; 515d3de4bebSJohnson CH Chen (陳昭勳) ds1374->wdt.min_timeout = TIMER_MARGIN_MIN; 516d3de4bebSJohnson CH Chen (陳昭勳) ds1374->wdt.max_timeout = TIMER_MARGIN_MAX; 517d3de4bebSJohnson CH Chen (陳昭勳) 518d3de4bebSJohnson CH Chen (陳昭勳) watchdog_init_timeout(&ds1374->wdt, wdt_margin, &client->dev); 519d3de4bebSJohnson CH Chen (陳昭勳) watchdog_set_nowayout(&ds1374->wdt, nowayout); 520d3de4bebSJohnson CH Chen (陳昭勳) watchdog_stop_on_reboot(&ds1374->wdt); 521d3de4bebSJohnson CH Chen (陳昭勳) watchdog_stop_on_unregister(&ds1374->wdt); 522d3de4bebSJohnson CH Chen (陳昭勳) watchdog_set_drvdata(&ds1374->wdt, ds1374); 523d3de4bebSJohnson CH Chen (陳昭勳) ds1374_wdt_settimeout(&ds1374->wdt, ds1374->wdt.timeout); 524d3de4bebSJohnson CH Chen (陳昭勳) 525d3de4bebSJohnson CH Chen (陳昭勳) ret = devm_watchdog_register_device(&client->dev, &ds1374->wdt); 526920f91e5SSøren Andersen if (ret) 527920f91e5SSøren Andersen return ret; 528920f91e5SSøren Andersen #endif 529920f91e5SSøren Andersen 530bf4994d7SScott Wood return 0; 531bf4994d7SScott Wood } 532bf4994d7SScott Wood 533ed5c2f5fSUwe Kleine-König static void ds1374_remove(struct i2c_client *client) 534bf4994d7SScott Wood { 535bf4994d7SScott Wood struct ds1374 *ds1374 = i2c_get_clientdata(client); 536bf4994d7SScott Wood 537b42f9317SAnton Vorontsov if (client->irq > 0) { 538bf4994d7SScott Wood mutex_lock(&ds1374->mutex); 539bf4994d7SScott Wood ds1374->exiting = 1; 540bf4994d7SScott Wood mutex_unlock(&ds1374->mutex); 541bf4994d7SScott Wood 542d1a96639SSachin Kamat devm_free_irq(&client->dev, client->irq, client); 5439db8995bSTejun Heo cancel_work_sync(&ds1374->work); 544bf4994d7SScott Wood } 545bf4994d7SScott Wood } 546bf4994d7SScott Wood 5478b80ef64SJingoo Han #ifdef CONFIG_PM_SLEEP 548bc96ba74SMark Brown static int ds1374_suspend(struct device *dev) 549986e36a5SMarc Pignat { 550bc96ba74SMark Brown struct i2c_client *client = to_i2c_client(dev); 551bc96ba74SMark Brown 5520d9030a2SOctavian Purdila if (client->irq > 0 && device_may_wakeup(&client->dev)) 553986e36a5SMarc Pignat enable_irq_wake(client->irq); 554986e36a5SMarc Pignat return 0; 555986e36a5SMarc Pignat } 556986e36a5SMarc Pignat 557bc96ba74SMark Brown static int ds1374_resume(struct device *dev) 558986e36a5SMarc Pignat { 559bc96ba74SMark Brown struct i2c_client *client = to_i2c_client(dev); 560bc96ba74SMark Brown 5610d9030a2SOctavian Purdila if (client->irq > 0 && device_may_wakeup(&client->dev)) 562986e36a5SMarc Pignat disable_irq_wake(client->irq); 563986e36a5SMarc Pignat return 0; 564986e36a5SMarc Pignat } 5658b80ef64SJingoo Han #endif 566bc96ba74SMark Brown 567bc96ba74SMark Brown static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume); 568bc96ba74SMark Brown 569bf4994d7SScott Wood static struct i2c_driver ds1374_driver = { 570bf4994d7SScott Wood .driver = { 571bf4994d7SScott Wood .name = "rtc-ds1374", 572abac12e1SJavier Martinez Canillas .of_match_table = of_match_ptr(ds1374_of_match), 5738b80ef64SJingoo Han .pm = &ds1374_pm, 574bf4994d7SScott Wood }, 57531b0cecbSUwe Kleine-König .probe = ds1374_probe, 5765a167f45SGreg Kroah-Hartman .remove = ds1374_remove, 5773760f736SJean Delvare .id_table = ds1374_id, 578bf4994d7SScott Wood }; 579bf4994d7SScott Wood 5800abc9201SAxel Lin module_i2c_driver(ds1374_driver); 581bf4994d7SScott Wood 582bf4994d7SScott Wood MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>"); 583bf4994d7SScott Wood MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver"); 584bf4994d7SScott Wood MODULE_LICENSE("GPL"); 585