2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2006-06-25 20:48:17 +08:00
|
|
|
/*
|
|
|
|
* rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 James Chapman (ds1337 core)
|
|
|
|
* Copyright (C) 2006 David Brownell
|
2009-04-01 06:24:58 +08:00
|
|
|
* Copyright (C) 2009 Matthias Fuchs (rx8025 support)
|
2013-04-30 07:19:26 +08:00
|
|
|
* Copyright (C) 2012 Bertrand Achard (nvram access fixes)
|
2006-06-25 20:48:17 +08:00
|
|
|
*/
|
|
|
|
|
2016-11-30 10:57:31 +08:00
|
|
|
#include <linux/acpi.h>
|
2015-06-24 00:15:12 +08:00
|
|
|
#include <linux/bcd.h>
|
|
|
|
#include <linux/i2c.h>
|
2006-06-25 20:48:17 +08:00
|
|
|
#include <linux/init.h>
|
2015-06-24 00:15:12 +08:00
|
|
|
#include <linux/module.h>
|
2017-03-03 22:29:15 +08:00
|
|
|
#include <linux/of_device.h>
|
2015-06-24 00:15:12 +08:00
|
|
|
#include <linux/rtc/ds1307.h>
|
|
|
|
#include <linux/rtc.h>
|
2006-06-25 20:48:17 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/string.h>
|
2016-01-24 23:22:16 +08:00
|
|
|
#include <linux/hwmon.h>
|
|
|
|
#include <linux/hwmon-sysfs.h>
|
2016-01-31 22:10:10 +08:00
|
|
|
#include <linux/clk-provider.h>
|
2017-03-11 01:52:34 +08:00
|
|
|
#include <linux/regmap.h>
|
2020-03-30 10:55:00 +08:00
|
|
|
#include <linux/watchdog.h>
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* We can't determine type by probing, but if we expect pre-Linux code
|
2006-06-25 20:48:17 +08:00
|
|
|
* to have set the chip up as a clock (turning on the oscillator and
|
|
|
|
* setting the date and time), Linux can ignore the non-clock features.
|
|
|
|
* That's a natural job for a factory or repair bench.
|
|
|
|
*/
|
|
|
|
enum ds_type {
|
2007-07-17 19:04:55 +08:00
|
|
|
ds_1307,
|
2017-06-08 18:36:54 +08:00
|
|
|
ds_1308,
|
2007-07-17 19:04:55 +08:00
|
|
|
ds_1337,
|
|
|
|
ds_1338,
|
|
|
|
ds_1339,
|
|
|
|
ds_1340,
|
rtc: ds1307: add basic support for ds1341 chip
This adds support for reading and writing date/time from/to ds1341 chip.
ds1341 chip has other features - alarms, input clock (can be used instead
of intercal oscillator for better accuracy), output clock ("square wave
generation"). However, not all of that is available at the same time.
Same chip pins, CLKIN/nINTA and SQW/nINTB, can be used either for
input/output clocks, or for alarm interrupts. Role of these pins on
particular board depends on hardware wiring.
We can add device tree properties that describe if each of pins is wired
as clock, or as interrupt, or left unconnected, and enable support for
corresponding functionality based on that. But that is cumbersome, requires
hardware for testing, and has to deal with bit enabling/disabling output
clock also affects which pins alarm interrupts are routed to.
Another factor is that there are hardware setups (i.e. ZII RDU2) that
power DS1341 from SuperCap, which makes power saving critical. For such
setups, kernel driver should leave register bits that control mentioned
pins in the state configured by bootloader.
Given all that, it was decided to limit support to "only date/time" for
now. That is enough for common use case. Full (and cumbersome)
implementation can be added later if ever needed.
Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Aleksander Morgado <aleksander@aleksander.es>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2017-08-24 14:32:11 +08:00
|
|
|
ds_1341,
|
2009-06-18 07:26:08 +08:00
|
|
|
ds_1388,
|
2009-06-18 07:26:10 +08:00
|
|
|
ds_3231,
|
2017-03-24 07:54:57 +08:00
|
|
|
m41t0,
|
2007-07-17 19:04:55 +08:00
|
|
|
m41t00,
|
2018-05-17 05:08:40 +08:00
|
|
|
m41t11,
|
2014-12-11 07:53:57 +08:00
|
|
|
mcp794xx,
|
2009-04-01 06:24:58 +08:00
|
|
|
rx_8025,
|
2017-06-19 04:55:28 +08:00
|
|
|
rx_8130,
|
2012-03-24 06:02:36 +08:00
|
|
|
last_ds_type /* always last */
|
2012-03-24 06:02:37 +08:00
|
|
|
/* rs5c372 too? different address... */
|
2006-06-25 20:48:17 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* RTC registers don't differ much, except for the century flag */
|
|
|
|
#define DS1307_REG_SECS 0x00 /* 00-59 */
|
|
|
|
# define DS1307_BIT_CH 0x80
|
2007-07-17 19:05:06 +08:00
|
|
|
# define DS1340_BIT_nEOSC 0x80
|
2014-12-11 07:53:57 +08:00
|
|
|
# define MCP794XX_BIT_ST 0x80
|
2006-06-25 20:48:17 +08:00
|
|
|
#define DS1307_REG_MIN 0x01 /* 00-59 */
|
2017-03-24 07:54:57 +08:00
|
|
|
# define M41T0_BIT_OF 0x80
|
2006-06-25 20:48:17 +08:00
|
|
|
#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
|
2007-07-17 19:05:10 +08:00
|
|
|
# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */
|
|
|
|
# define DS1307_BIT_PM 0x20 /* in REG_HOUR */
|
2006-06-25 20:48:17 +08:00
|
|
|
# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
|
|
|
|
# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
|
|
|
|
#define DS1307_REG_WDAY 0x03 /* 01-07 */
|
2014-12-11 07:53:57 +08:00
|
|
|
# define MCP794XX_BIT_VBATEN 0x08
|
2006-06-25 20:48:17 +08:00
|
|
|
#define DS1307_REG_MDAY 0x04 /* 01-31 */
|
|
|
|
#define DS1307_REG_MONTH 0x05 /* 01-12 */
|
|
|
|
# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
|
|
|
|
#define DS1307_REG_YEAR 0x06 /* 00-99 */
|
|
|
|
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* Other registers (control, status, alarms, trickle charge, NVRAM, etc)
|
2007-07-17 19:04:55 +08:00
|
|
|
* start at 7, and they differ a LOT. Only control and status matter for
|
|
|
|
* basic RTC date and time functionality; be careful using them.
|
2006-06-25 20:48:17 +08:00
|
|
|
*/
|
2007-07-17 19:04:55 +08:00
|
|
|
#define DS1307_REG_CONTROL 0x07 /* or ds1338 */
|
2006-06-25 20:48:17 +08:00
|
|
|
# define DS1307_BIT_OUT 0x80
|
2007-07-17 19:05:06 +08:00
|
|
|
# define DS1338_BIT_OSF 0x20
|
2006-06-25 20:48:17 +08:00
|
|
|
# define DS1307_BIT_SQWE 0x10
|
|
|
|
# define DS1307_BIT_RS1 0x02
|
|
|
|
# define DS1307_BIT_RS0 0x01
|
|
|
|
#define DS1337_REG_CONTROL 0x0e
|
|
|
|
# define DS1337_BIT_nEOSC 0x80
|
2008-10-16 13:02:58 +08:00
|
|
|
# define DS1339_BIT_BBSQI 0x20
|
2009-06-18 07:26:10 +08:00
|
|
|
# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */
|
2006-06-25 20:48:17 +08:00
|
|
|
# define DS1337_BIT_RS2 0x10
|
|
|
|
# define DS1337_BIT_RS1 0x08
|
|
|
|
# define DS1337_BIT_INTCN 0x04
|
|
|
|
# define DS1337_BIT_A2IE 0x02
|
|
|
|
# define DS1337_BIT_A1IE 0x01
|
2007-07-17 19:04:55 +08:00
|
|
|
#define DS1340_REG_CONTROL 0x07
|
|
|
|
# define DS1340_BIT_OUT 0x80
|
|
|
|
# define DS1340_BIT_FT 0x40
|
|
|
|
# define DS1340_BIT_CALIB_SIGN 0x20
|
|
|
|
# define DS1340_M_CALIBRATION 0x1f
|
2007-07-17 19:05:06 +08:00
|
|
|
#define DS1340_REG_FLAG 0x09
|
|
|
|
# define DS1340_BIT_OSF 0x80
|
2006-06-25 20:48:17 +08:00
|
|
|
#define DS1337_REG_STATUS 0x0f
|
|
|
|
# define DS1337_BIT_OSF 0x80
|
2016-01-31 22:10:10 +08:00
|
|
|
# define DS3231_BIT_EN32KHZ 0x08
|
2006-06-25 20:48:17 +08:00
|
|
|
# define DS1337_BIT_A2I 0x02
|
|
|
|
# define DS1337_BIT_A1I 0x01
|
2008-10-16 13:02:58 +08:00
|
|
|
#define DS1339_REG_ALARM1_SECS 0x07
|
2012-05-30 06:07:38 +08:00
|
|
|
|
|
|
|
#define DS13XX_TRICKLE_CHARGER_MAGIC 0xa0
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2009-04-01 06:24:58 +08:00
|
|
|
#define RX8025_REG_CTRL1 0x0e
|
|
|
|
# define RX8025_BIT_2412 0x20
|
|
|
|
#define RX8025_REG_CTRL2 0x0f
|
|
|
|
# define RX8025_BIT_PON 0x10
|
|
|
|
# define RX8025_BIT_VDET 0x40
|
|
|
|
# define RX8025_BIT_XST 0x20
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2019-01-25 22:35:56 +08:00
|
|
|
#define RX8130_REG_ALARM_MIN 0x17
|
|
|
|
#define RX8130_REG_ALARM_HOUR 0x18
|
|
|
|
#define RX8130_REG_ALARM_WEEK_OR_DAY 0x19
|
|
|
|
#define RX8130_REG_EXTENSION 0x1c
|
2019-01-25 22:35:54 +08:00
|
|
|
#define RX8130_REG_EXTENSION_WADA BIT(3)
|
2019-01-25 22:35:56 +08:00
|
|
|
#define RX8130_REG_FLAG 0x1d
|
|
|
|
#define RX8130_REG_FLAG_VLF BIT(1)
|
2019-01-25 22:35:54 +08:00
|
|
|
#define RX8130_REG_FLAG_AF BIT(3)
|
2019-01-25 22:35:56 +08:00
|
|
|
#define RX8130_REG_CONTROL0 0x1e
|
2019-01-25 22:35:54 +08:00
|
|
|
#define RX8130_REG_CONTROL0_AIE BIT(3)
|
|
|
|
|
|
|
|
#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 BIT(3)
|
|
|
|
# define MCP794XX_BIT_ALMX_C0 BIT(4)
|
|
|
|
# define MCP794XX_BIT_ALMX_C1 BIT(5)
|
|
|
|
# define MCP794XX_BIT_ALMX_C2 BIT(6)
|
|
|
|
# define MCP794XX_BIT_ALMX_POL BIT(7)
|
|
|
|
# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \
|
|
|
|
MCP794XX_BIT_ALMX_C1 | \
|
|
|
|
MCP794XX_BIT_ALMX_C2)
|
|
|
|
|
2018-07-26 01:26:04 +08:00
|
|
|
#define M41TXX_REG_CONTROL 0x07
|
|
|
|
# define M41TXX_BIT_OUT BIT(7)
|
|
|
|
# define M41TXX_BIT_FT BIT(6)
|
|
|
|
# define M41TXX_BIT_CALIB_SIGN BIT(5)
|
|
|
|
# define M41TXX_M_CALIBRATION GENMASK(4, 0)
|
|
|
|
|
2020-03-30 10:55:00 +08:00
|
|
|
#define DS1388_REG_WDOG_HUN_SECS 0x08
|
|
|
|
#define DS1388_REG_WDOG_SECS 0x09
|
2020-02-07 11:18:11 +08:00
|
|
|
#define DS1388_REG_FLAG 0x0b
|
2020-03-30 10:55:00 +08:00
|
|
|
# define DS1388_BIT_WF BIT(6)
|
2020-02-07 11:18:11 +08:00
|
|
|
# define DS1388_BIT_OSF BIT(7)
|
2020-03-30 10:55:00 +08:00
|
|
|
#define DS1388_REG_CONTROL 0x0c
|
|
|
|
# define DS1388_BIT_RST BIT(0)
|
|
|
|
# define DS1388_BIT_WDE BIT(1)
|
2020-08-17 07:57:31 +08:00
|
|
|
# define DS1388_BIT_nEOSC BIT(7)
|
2020-03-30 10:55:00 +08:00
|
|
|
|
2018-07-26 01:26:04 +08:00
|
|
|
/* negative offset step is -2.034ppm */
|
|
|
|
#define M41TXX_NEG_OFFSET_STEP_PPB 2034
|
|
|
|
/* positive offset step is +4.068ppm */
|
|
|
|
#define M41TXX_POS_OFFSET_STEP_PPB 4068
|
|
|
|
/* Min and max values supported with 'offset' interface by M41TXX */
|
|
|
|
#define M41TXX_MIN_OFFSET ((-31) * M41TXX_NEG_OFFSET_STEP_PPB)
|
|
|
|
#define M41TXX_MAX_OFFSET ((31) * M41TXX_POS_OFFSET_STEP_PPB)
|
|
|
|
|
2006-06-25 20:48:17 +08:00
|
|
|
struct ds1307 {
|
|
|
|
enum ds_type type;
|
2008-10-16 13:02:58 +08:00
|
|
|
unsigned long flags;
|
|
|
|
#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
|
|
|
|
#define HAS_ALARM 1 /* bit 1 == irq claimed */
|
2017-03-11 01:52:34 +08:00
|
|
|
struct device *dev;
|
|
|
|
struct regmap *regmap;
|
|
|
|
const char *name;
|
2006-06-25 20:48:17 +08:00
|
|
|
struct rtc_device *rtc;
|
2016-01-31 22:10:10 +08:00
|
|
|
#ifdef CONFIG_COMMON_CLK
|
|
|
|
struct clk_hw clks[2];
|
|
|
|
#endif
|
2006-06-25 20:48:17 +08:00
|
|
|
};
|
|
|
|
|
2007-07-17 19:04:55 +08:00
|
|
|
struct chip_desc {
|
|
|
|
unsigned alarm:1;
|
2012-03-24 06:02:38 +08:00
|
|
|
u16 nvram_offset;
|
|
|
|
u16 nvram_size;
|
2017-07-12 13:49:47 +08:00
|
|
|
u8 offset; /* register's offset */
|
2017-06-05 23:57:33 +08:00
|
|
|
u8 century_reg;
|
|
|
|
u8 century_enable_bit;
|
|
|
|
u8 century_bit;
|
2017-07-12 13:49:22 +08:00
|
|
|
u8 bbsqi_bit;
|
2017-07-12 13:49:41 +08:00
|
|
|
irq_handler_t irq_handler;
|
2017-07-12 13:49:44 +08:00
|
|
|
const struct rtc_class_ops *rtc_ops;
|
2012-05-30 06:07:38 +08:00
|
|
|
u16 trickle_charger_reg;
|
2017-09-05 04:46:04 +08:00
|
|
|
u8 (*do_trickle_setup)(struct ds1307 *, u32,
|
2017-03-11 01:52:34 +08:00
|
|
|
bool);
|
2007-07-17 19:04:55 +08:00
|
|
|
};
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static const struct chip_desc chips[last_ds_type];
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2006-06-25 20:48:17 +08:00
|
|
|
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
2017-03-11 01:52:34 +08:00
|
|
|
int tmp, ret;
|
2017-06-05 23:57:33 +08:00
|
|
|
const struct chip_desc *chip = &chips[ds1307->type];
|
2017-09-05 04:46:02 +08:00
|
|
|
u8 regs[7];
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2019-01-25 22:35:57 +08:00
|
|
|
if (ds1307->type == rx_8130) {
|
|
|
|
unsigned int regflag;
|
|
|
|
ret = regmap_read(ds1307->regmap, RX8130_REG_FLAG, ®flag);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "%s error %d\n", "read", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (regflag & RX8130_REG_FLAG_VLF) {
|
|
|
|
dev_warn_once(dev, "oscillator failed, set time!\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-17 19:04:55 +08:00
|
|
|
/* read the RTC date and time registers all at once */
|
2017-09-05 04:46:02 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
|
|
|
|
sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "%s error %d\n", "read", ret);
|
|
|
|
return ret;
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
dev_dbg(dev, "%s: %7ph\n", "read", regs);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-03-24 07:54:57 +08:00
|
|
|
/* if oscillator fail bit is set, no data can be trusted */
|
|
|
|
if (ds1307->type == m41t0 &&
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[DS1307_REG_MIN] & M41T0_BIT_OF) {
|
2017-03-24 07:54:57 +08:00
|
|
|
dev_warn_once(dev, "oscillator failed, set time!\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-04-11 06:16:29 +08:00
|
|
|
tmp = regs[DS1307_REG_SECS];
|
|
|
|
switch (ds1307->type) {
|
|
|
|
case ds_1307:
|
|
|
|
case m41t0:
|
|
|
|
case m41t00:
|
|
|
|
case m41t11:
|
|
|
|
if (tmp & DS1307_BIT_CH)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case ds_1308:
|
|
|
|
case ds_1338:
|
|
|
|
if (tmp & DS1307_BIT_CH)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &tmp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (tmp & DS1338_BIT_OSF)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case ds_1340:
|
|
|
|
if (tmp & DS1340_BIT_nEOSC)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ret = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (tmp & DS1340_BIT_OSF)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2020-02-07 11:18:11 +08:00
|
|
|
case ds_1388:
|
|
|
|
ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
if (tmp & DS1388_BIT_OSF)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2019-04-11 06:16:29 +08:00
|
|
|
case mcp794xx:
|
|
|
|
if (!(tmp & MCP794XX_BIT_ST))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
|
|
|
|
t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
|
|
|
|
tmp = regs[DS1307_REG_HOUR] & 0x3f;
|
2008-10-19 11:28:41 +08:00
|
|
|
t->tm_hour = bcd2bin(tmp);
|
2017-09-05 04:46:02 +08:00
|
|
|
t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
|
|
|
|
t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
|
|
|
|
tmp = regs[DS1307_REG_MONTH] & 0x1f;
|
2008-10-19 11:28:41 +08:00
|
|
|
t->tm_mon = bcd2bin(tmp) - 1;
|
2017-09-05 04:46:02 +08:00
|
|
|
t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100;
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[chip->century_reg] & chip->century_bit &&
|
2017-06-05 23:57:33 +08:00
|
|
|
IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY))
|
|
|
|
t->tm_year += 100;
|
2016-07-13 08:26:08 +08:00
|
|
|
|
2006-06-25 20:48:17 +08:00
|
|
|
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
|
|
|
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
|
|
|
|
"read", t->tm_sec, t->tm_min,
|
|
|
|
t->tm_hour, t->tm_mday,
|
|
|
|
t->tm_mon, t->tm_year, t->tm_wday);
|
|
|
|
|
2018-02-19 23:23:56 +08:00
|
|
|
return 0;
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ds1307_set_time(struct device *dev, struct rtc_time *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
2017-06-05 23:57:33 +08:00
|
|
|
const struct chip_desc *chip = &chips[ds1307->type];
|
2006-06-25 20:48:17 +08:00
|
|
|
int result;
|
|
|
|
int tmp;
|
2017-09-05 04:46:02 +08:00
|
|
|
u8 regs[7];
|
2006-06-25 20:48:17 +08:00
|
|
|
|
|
|
|
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
|
|
|
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
|
2006-10-04 16:41:53 +08:00
|
|
|
"write", t->tm_sec, t->tm_min,
|
|
|
|
t->tm_hour, t->tm_mday,
|
|
|
|
t->tm_mon, t->tm_year, t->tm_wday);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2016-07-13 08:26:08 +08:00
|
|
|
if (t->tm_year < 100)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-06-05 23:57:33 +08:00
|
|
|
#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
|
|
|
|
if (t->tm_year > (chip->century_bit ? 299 : 199))
|
|
|
|
return -EINVAL;
|
2016-07-13 08:26:08 +08:00
|
|
|
#else
|
2017-06-05 23:57:33 +08:00
|
|
|
if (t->tm_year > 199)
|
2016-07-13 08:26:08 +08:00
|
|
|
return -EINVAL;
|
|
|
|
#endif
|
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
|
|
|
|
regs[DS1307_REG_MIN] = bin2bcd(t->tm_min);
|
|
|
|
regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
|
|
|
|
regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
|
|
|
|
regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
|
|
|
|
regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
|
|
|
/* assume 20YY not 19YY */
|
|
|
|
tmp = t->tm_year - 100;
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[DS1307_REG_YEAR] = bin2bcd(tmp);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-06-05 23:57:33 +08:00
|
|
|
if (chip->century_enable_bit)
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[chip->century_reg] |= chip->century_enable_bit;
|
2017-06-05 23:57:33 +08:00
|
|
|
if (t->tm_year > 199 && chip->century_bit)
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[chip->century_reg] |= chip->century_bit;
|
2017-06-05 23:57:33 +08:00
|
|
|
|
2019-04-11 06:16:29 +08:00
|
|
|
switch (ds1307->type) {
|
|
|
|
case ds_1308:
|
|
|
|
case ds_1338:
|
|
|
|
regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL,
|
|
|
|
DS1338_BIT_OSF, 0);
|
|
|
|
break;
|
|
|
|
case ds_1340:
|
|
|
|
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
|
|
|
|
DS1340_BIT_OSF, 0);
|
|
|
|
break;
|
2020-08-18 09:35:43 +08:00
|
|
|
case ds_1388:
|
|
|
|
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
|
|
|
|
DS1388_BIT_OSF, 0);
|
|
|
|
break;
|
2019-04-11 06:16:29 +08:00
|
|
|
case mcp794xx:
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* these bits were cleared when preparing the date/time
|
|
|
|
* values and need to be set again before writing the
|
2017-09-05 04:46:02 +08:00
|
|
|
* regsfer out to the device.
|
2012-03-24 06:02:37 +08:00
|
|
|
*/
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
|
|
|
|
regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
|
2019-04-11 06:16:29 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2007-07-17 19:05:06 +08:00
|
|
|
}
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
dev_dbg(dev, "%s: %7ph\n", "write", regs);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
result = regmap_bulk_write(ds1307->regmap, chip->offset, regs,
|
|
|
|
sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (result) {
|
2009-01-08 10:07:13 +08:00
|
|
|
dev_err(dev, "%s error %d\n", "write", result);
|
|
|
|
return result;
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
2019-01-25 22:35:57 +08:00
|
|
|
|
|
|
|
if (ds1307->type == rx_8130) {
|
|
|
|
/* clear Voltage Loss Flag as data is available now */
|
|
|
|
result = regmap_write(ds1307->regmap, RX8130_REG_FLAG,
|
|
|
|
~(u8)RX8130_REG_FLAG_VLF);
|
|
|
|
if (result) {
|
|
|
|
dev_err(dev, "%s error %d\n", "write", result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-06-25 20:48:17 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 10:07:16 +08:00
|
|
|
static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
2008-10-16 13:02:58 +08:00
|
|
|
{
|
2017-03-11 01:52:34 +08:00
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
2008-10-16 13:02:58 +08:00
|
|
|
int ret;
|
2017-09-05 04:46:02 +08:00
|
|
|
u8 regs[9];
|
2008-10-16 13:02:58 +08:00
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* read all ALARM1, ALARM2, and status registers at once */
|
2017-03-11 01:52:34 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs, sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (ret) {
|
2008-10-16 13:02:58 +08:00
|
|
|
dev_err(dev, "%s error %d\n", "alarm read", ret);
|
2017-03-11 01:52:34 +08:00
|
|
|
return ret;
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
|
|
|
|
2015-11-24 21:51:23 +08:00
|
|
|
dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read",
|
2017-09-05 04:46:02 +08:00
|
|
|
®s[0], ®s[4], ®s[7]);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* report alarm time (ALARM1); assume 24 hour and day-of-month modes,
|
2008-10-16 13:02:58 +08:00
|
|
|
* and that all four fields are checked matches
|
|
|
|
*/
|
2017-09-05 04:46:02 +08:00
|
|
|
t->time.tm_sec = bcd2bin(regs[0] & 0x7f);
|
|
|
|
t->time.tm_min = bcd2bin(regs[1] & 0x7f);
|
|
|
|
t->time.tm_hour = bcd2bin(regs[2] & 0x3f);
|
|
|
|
t->time.tm_mday = bcd2bin(regs[3] & 0x3f);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
|
|
|
/* ... and status */
|
2017-09-05 04:46:02 +08:00
|
|
|
t->enabled = !!(regs[7] & DS1337_BIT_A1IE);
|
|
|
|
t->pending = !!(regs[8] & DS1337_BIT_A1I);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
|
|
|
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
|
|
|
"hours=%d, mday=%d, enabled=%d, pending=%d\n",
|
|
|
|
"alarm read", t->time.tm_sec, t->time.tm_min,
|
|
|
|
t->time.tm_hour, t->time.tm_mday,
|
|
|
|
t->enabled, t->pending);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 10:07:16 +08:00
|
|
|
static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
2008-10-16 13:02:58 +08:00
|
|
|
{
|
2017-03-11 01:52:34 +08:00
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
2017-09-05 04:46:02 +08:00
|
|
|
unsigned char regs[9];
|
2008-10-16 13:02:58 +08:00
|
|
|
u8 control, status;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
|
|
|
"hours=%d, mday=%d, enabled=%d, pending=%d\n",
|
|
|
|
"alarm set", t->time.tm_sec, t->time.tm_min,
|
|
|
|
t->time.tm_hour, t->time.tm_mday,
|
|
|
|
t->enabled, t->pending);
|
|
|
|
|
|
|
|
/* read current status of both alarms and the chip */
|
2017-09-05 04:46:02 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
|
|
|
|
sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (ret) {
|
2008-10-16 13:02:58 +08:00
|
|
|
dev_err(dev, "%s error %d\n", "alarm write", ret);
|
2017-03-11 01:52:34 +08:00
|
|
|
return ret;
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
2017-09-05 04:46:02 +08:00
|
|
|
control = regs[7];
|
|
|
|
status = regs[8];
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2015-11-24 21:51:23 +08:00
|
|
|
dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)",
|
2017-09-05 04:46:02 +08:00
|
|
|
®s[0], ®s[4], control, status);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
|
|
|
/* set ALARM1, using 24 hour and day-of-month modes */
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[0] = bin2bcd(t->time.tm_sec);
|
|
|
|
regs[1] = bin2bcd(t->time.tm_min);
|
|
|
|
regs[2] = bin2bcd(t->time.tm_hour);
|
|
|
|
regs[3] = bin2bcd(t->time.tm_mday);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
|
|
|
/* set ALARM2 to non-garbage */
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[4] = 0;
|
|
|
|
regs[5] = 0;
|
|
|
|
regs[6] = 0;
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2016-04-10 19:23:05 +08:00
|
|
|
/* disable alarms */
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
|
|
|
|
regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs,
|
|
|
|
sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (ret) {
|
2008-10-16 13:02:58 +08:00
|
|
|
dev_err(dev, "can't set alarm time\n");
|
2009-01-08 10:07:13 +08:00
|
|
|
return ret;
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
|
|
|
|
2016-04-10 19:23:05 +08:00
|
|
|
/* optionally enable ALARM1 */
|
|
|
|
if (t->enabled) {
|
|
|
|
dev_dbg(dev, "alarm IRQ armed\n");
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
|
|
|
|
regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]);
|
2016-04-10 19:23:05 +08:00
|
|
|
}
|
|
|
|
|
2008-10-16 13:02:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-03 09:02:41 +08:00
|
|
|
static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
2008-10-16 13:02:58 +08:00
|
|
|
{
|
2017-03-11 01:52:34 +08:00
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2011-02-03 09:02:41 +08:00
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -ENOTTY;
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2017-06-05 23:57:29 +08:00
|
|
|
return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
|
|
|
|
DS1337_BIT_A1IE,
|
|
|
|
enabled ? DS1337_BIT_A1IE : 0);
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode)
|
|
|
|
{
|
|
|
|
u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
|
|
|
|
DS1307_TRICKLE_CHARGER_NO_DIODE;
|
2007-11-15 08:58:32 +08:00
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
switch (ohms) {
|
|
|
|
case 250:
|
|
|
|
setup |= DS1307_TRICKLE_CHARGER_250_OHM;
|
|
|
|
break;
|
|
|
|
case 2000:
|
|
|
|
setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
|
|
|
|
break;
|
|
|
|
case 4000:
|
|
|
|
setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_warn(ds1307->dev,
|
|
|
|
"Unsupported ohm value %u in dt\n", ohms);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return setup;
|
|
|
|
}
|
2017-06-19 04:55:28 +08:00
|
|
|
|
|
|
|
static irqreturn_t rx8130_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_id;
|
|
|
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
|
|
|
u8 ctl[3];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(lock);
|
|
|
|
|
|
|
|
/* Read control registers. */
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
|
|
|
sizeof(ctl));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
if (!(ctl[1] & RX8130_REG_FLAG_AF))
|
|
|
|
goto out;
|
|
|
|
ctl[1] &= ~RX8130_REG_FLAG_AF;
|
|
|
|
ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
|
|
|
|
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
|
|
|
sizeof(ctl));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(lock);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
u8 ald[3], ctl[3];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Read alarm registers. */
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
|
|
|
|
sizeof(ald));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Read control registers. */
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
|
|
|
sizeof(ctl));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE);
|
|
|
|
t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF);
|
|
|
|
|
|
|
|
/* Report alarm 0 time assuming 24-hour and day-of-month modes. */
|
|
|
|
t->time.tm_sec = -1;
|
|
|
|
t->time.tm_min = bcd2bin(ald[0] & 0x7f);
|
|
|
|
t->time.tm_hour = bcd2bin(ald[1] & 0x7f);
|
|
|
|
t->time.tm_wday = -1;
|
|
|
|
t->time.tm_mday = bcd2bin(ald[2] & 0x7f);
|
|
|
|
t->time.tm_mon = -1;
|
|
|
|
t->time.tm_year = -1;
|
|
|
|
t->time.tm_yday = -1;
|
|
|
|
t->time.tm_isdst = -1;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d enabled=%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);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
u8 ald[3], ctl[3];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
|
|
|
|
"enabled=%d pending=%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, t->pending);
|
|
|
|
|
|
|
|
/* Read control registers. */
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
|
|
|
sizeof(ctl));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2019-01-25 22:35:58 +08:00
|
|
|
ctl[0] &= RX8130_REG_EXTENSION_WADA;
|
|
|
|
ctl[1] &= ~RX8130_REG_FLAG_AF;
|
2017-06-19 04:55:28 +08:00
|
|
|
ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
|
|
|
|
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
|
|
|
sizeof(ctl));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Hardware alarm precision is 1 minute! */
|
|
|
|
ald[0] = bin2bcd(t->time.tm_min);
|
|
|
|
ald[1] = bin2bcd(t->time.tm_hour);
|
|
|
|
ald[2] = bin2bcd(t->time.tm_mday);
|
|
|
|
|
2017-09-05 04:46:03 +08:00
|
|
|
ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald,
|
|
|
|
sizeof(ald));
|
2017-06-19 04:55:28 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!t->enabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ctl[2] |= RX8130_REG_CONTROL0_AIE;
|
|
|
|
|
2019-01-25 22:35:58 +08:00
|
|
|
return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]);
|
2017-06-19 04:55:28 +08:00
|
|
|
}
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
int ret, reg;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
reg |= RX8130_REG_CONTROL0_AIE;
|
|
|
|
else
|
|
|
|
reg &= ~RX8130_REG_CONTROL0_AIE;
|
|
|
|
|
|
|
|
return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_id;
|
|
|
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
|
|
|
int reg, ret;
|
|
|
|
|
|
|
|
mutex_lock(lock);
|
|
|
|
|
|
|
|
/* Check and clear alarm 0 interrupt flag. */
|
|
|
|
ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
if (!(reg & MCP794XX_BIT_ALMX_IF))
|
|
|
|
goto out;
|
|
|
|
reg &= ~MCP794XX_BIT_ALMX_IF;
|
|
|
|
ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Disable alarm 0. */
|
|
|
|
ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
|
|
|
|
MCP794XX_BIT_ALM0_EN, 0);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(lock);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
u8 regs[10];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Read control and alarm 0 registers. */
|
|
|
|
ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
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(regs[3] & 0x7f);
|
|
|
|
t->time.tm_min = bcd2bin(regs[4] & 0x7f);
|
|
|
|
t->time.tm_hour = bcd2bin(regs[5] & 0x3f);
|
|
|
|
t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1;
|
|
|
|
t->time.tm_mday = bcd2bin(regs[7] & 0x3f);
|
|
|
|
t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1;
|
|
|
|
t->time.tm_year = -1;
|
|
|
|
t->time.tm_yday = -1;
|
|
|
|
t->time.tm_isdst = -1;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
|
|
|
|
"enabled=%d polarity=%d irq=%d match=%lu\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,
|
|
|
|
!!(regs[6] & MCP794XX_BIT_ALMX_POL),
|
|
|
|
!!(regs[6] & MCP794XX_BIT_ALMX_IF),
|
|
|
|
(regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We may have a random RTC weekday, therefore calculate alarm weekday based
|
|
|
|
* on current weekday we read from the RTC timekeeping regs
|
|
|
|
*/
|
|
|
|
static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm)
|
|
|
|
{
|
|
|
|
struct rtc_time tm_now;
|
|
|
|
int days_now, days_alarm, ret;
|
|
|
|
|
|
|
|
ret = ds1307_get_time(dev, &tm_now);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60);
|
|
|
|
days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60);
|
|
|
|
|
|
|
|
return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
unsigned char regs[10];
|
|
|
|
int wday, ret;
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
wday = mcp794xx_alm_weekday(dev, &t->time);
|
|
|
|
if (wday < 0)
|
|
|
|
return wday;
|
|
|
|
|
|
|
|
dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
|
|
|
|
"enabled=%d pending=%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, t->pending);
|
|
|
|
|
|
|
|
/* Read control and alarm 0 registers. */
|
|
|
|
ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Set alarm 0, using 24-hour and day-of-month modes. */
|
|
|
|
regs[3] = bin2bcd(t->time.tm_sec);
|
|
|
|
regs[4] = bin2bcd(t->time.tm_min);
|
|
|
|
regs[5] = bin2bcd(t->time.tm_hour);
|
|
|
|
regs[6] = wday;
|
|
|
|
regs[7] = bin2bcd(t->time.tm_mday);
|
|
|
|
regs[8] = bin2bcd(t->time.tm_mon + 1);
|
|
|
|
|
|
|
|
/* Clear the alarm 0 interrupt flag. */
|
|
|
|
regs[6] &= ~MCP794XX_BIT_ALMX_IF;
|
|
|
|
/* Set alarm match: second, minute, hour, day, date, month. */
|
|
|
|
regs[6] |= MCP794XX_MSK_ALMX_MATCH;
|
|
|
|
/* Disable interrupt. We will not enable until completely programmed */
|
|
|
|
regs[0] &= ~MCP794XX_BIT_ALM0_EN;
|
|
|
|
|
|
|
|
ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!t->enabled)
|
|
|
|
return 0;
|
|
|
|
regs[0] |= MCP794XX_BIT_ALM0_EN;
|
|
|
|
return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
2017-06-19 04:55:28 +08:00
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL,
|
|
|
|
MCP794XX_BIT_ALM0_EN,
|
|
|
|
enabled ? MCP794XX_BIT_ALM0_EN : 0);
|
|
|
|
}
|
2017-06-19 04:55:28 +08:00
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static int m41txx_rtc_read_offset(struct device *dev, long *offset)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
unsigned int ctrl_reg;
|
|
|
|
u8 val;
|
|
|
|
|
|
|
|
regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);
|
|
|
|
|
|
|
|
val = ctrl_reg & M41TXX_M_CALIBRATION;
|
|
|
|
|
|
|
|
/* check if positive */
|
|
|
|
if (ctrl_reg & M41TXX_BIT_CALIB_SIGN)
|
|
|
|
*offset = (val * M41TXX_POS_OFFSET_STEP_PPB);
|
2017-06-19 04:55:28 +08:00
|
|
|
else
|
2019-01-25 22:35:55 +08:00
|
|
|
*offset = -(val * M41TXX_NEG_OFFSET_STEP_PPB);
|
2017-06-19 04:55:28 +08:00
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int m41txx_rtc_set_offset(struct device *dev, long offset)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
unsigned int ctrl_reg;
|
|
|
|
|
|
|
|
if ((offset < M41TXX_MIN_OFFSET) || (offset > M41TXX_MAX_OFFSET))
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
if (offset >= 0) {
|
|
|
|
ctrl_reg = DIV_ROUND_CLOSEST(offset,
|
|
|
|
M41TXX_POS_OFFSET_STEP_PPB);
|
|
|
|
ctrl_reg |= M41TXX_BIT_CALIB_SIGN;
|
|
|
|
} else {
|
|
|
|
ctrl_reg = DIV_ROUND_CLOSEST(abs(offset),
|
|
|
|
M41TXX_NEG_OFFSET_STEP_PPB);
|
|
|
|
}
|
|
|
|
|
|
|
|
return regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL,
|
|
|
|
M41TXX_M_CALIBRATION | M41TXX_BIT_CALIB_SIGN,
|
|
|
|
ctrl_reg);
|
2017-06-19 04:55:28 +08:00
|
|
|
}
|
|
|
|
|
2020-03-30 10:55:00 +08:00
|
|
|
#ifdef CONFIG_WATCHDOG_CORE
|
|
|
|
static int ds1388_wdt_start(struct watchdog_device *wdt_dev)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
|
|
|
|
u8 regs[2];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
|
|
|
|
DS1388_BIT_WF, 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
|
|
|
|
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* watchdog timeouts are measured in seconds. So ignore hundredths of
|
|
|
|
* seconds field.
|
|
|
|
*/
|
|
|
|
regs[0] = 0;
|
|
|
|
regs[1] = bin2bcd(wdt_dev->timeout);
|
|
|
|
|
|
|
|
ret = regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
|
|
|
|
DS1388_BIT_WDE | DS1388_BIT_RST,
|
|
|
|
DS1388_BIT_WDE | DS1388_BIT_RST);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds1388_wdt_stop(struct watchdog_device *wdt_dev)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
|
|
|
|
|
|
|
|
return regmap_update_bits(ds1307->regmap, DS1388_REG_CONTROL,
|
|
|
|
DS1388_BIT_WDE | DS1388_BIT_RST, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds1388_wdt_ping(struct watchdog_device *wdt_dev)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
|
|
|
|
u8 regs[2];
|
|
|
|
|
|
|
|
return regmap_bulk_read(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds1388_wdt_set_timeout(struct watchdog_device *wdt_dev,
|
|
|
|
unsigned int val)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = watchdog_get_drvdata(wdt_dev);
|
|
|
|
u8 regs[2];
|
|
|
|
|
|
|
|
wdt_dev->timeout = val;
|
|
|
|
regs[0] = 0;
|
|
|
|
regs[1] = bin2bcd(wdt_dev->timeout);
|
|
|
|
|
|
|
|
return regmap_bulk_write(ds1307->regmap, DS1388_REG_WDOG_HUN_SECS, regs,
|
|
|
|
sizeof(regs));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static const struct rtc_class_ops rx8130_rtc_ops = {
|
|
|
|
.read_time = ds1307_get_time,
|
|
|
|
.set_time = ds1307_set_time,
|
|
|
|
.read_alarm = rx8130_read_alarm,
|
|
|
|
.set_alarm = rx8130_set_alarm,
|
|
|
|
.alarm_irq_enable = rx8130_alarm_irq_enable,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rtc_class_ops mcp794xx_rtc_ops = {
|
|
|
|
.read_time = ds1307_get_time,
|
|
|
|
.set_time = ds1307_set_time,
|
|
|
|
.read_alarm = mcp794xx_read_alarm,
|
|
|
|
.set_alarm = mcp794xx_set_alarm,
|
|
|
|
.alarm_irq_enable = mcp794xx_alarm_irq_enable,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rtc_class_ops m41txx_rtc_ops = {
|
|
|
|
.read_time = ds1307_get_time,
|
|
|
|
.set_time = ds1307_set_time,
|
|
|
|
.read_alarm = ds1337_read_alarm,
|
|
|
|
.set_alarm = ds1337_set_alarm,
|
|
|
|
.alarm_irq_enable = ds1307_alarm_irq_enable,
|
|
|
|
.read_offset = m41txx_rtc_read_offset,
|
|
|
|
.set_offset = m41txx_rtc_set_offset,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct chip_desc chips[last_ds_type] = {
|
|
|
|
[ds_1307] = {
|
|
|
|
.nvram_offset = 8,
|
|
|
|
.nvram_size = 56,
|
|
|
|
},
|
|
|
|
[ds_1308] = {
|
|
|
|
.nvram_offset = 8,
|
|
|
|
.nvram_size = 56,
|
|
|
|
},
|
|
|
|
[ds_1337] = {
|
|
|
|
.alarm = 1,
|
|
|
|
.century_reg = DS1307_REG_MONTH,
|
|
|
|
.century_bit = DS1337_BIT_CENTURY,
|
|
|
|
},
|
|
|
|
[ds_1338] = {
|
|
|
|
.nvram_offset = 8,
|
|
|
|
.nvram_size = 56,
|
|
|
|
},
|
|
|
|
[ds_1339] = {
|
|
|
|
.alarm = 1,
|
|
|
|
.century_reg = DS1307_REG_MONTH,
|
|
|
|
.century_bit = DS1337_BIT_CENTURY,
|
|
|
|
.bbsqi_bit = DS1339_BIT_BBSQI,
|
|
|
|
.trickle_charger_reg = 0x10,
|
|
|
|
.do_trickle_setup = &do_trickle_setup_ds1339,
|
|
|
|
},
|
|
|
|
[ds_1340] = {
|
|
|
|
.century_reg = DS1307_REG_HOUR,
|
|
|
|
.century_enable_bit = DS1340_BIT_CENTURY_EN,
|
|
|
|
.century_bit = DS1340_BIT_CENTURY,
|
|
|
|
.do_trickle_setup = &do_trickle_setup_ds1339,
|
|
|
|
.trickle_charger_reg = 0x08,
|
|
|
|
},
|
|
|
|
[ds_1341] = {
|
|
|
|
.century_reg = DS1307_REG_MONTH,
|
|
|
|
.century_bit = DS1337_BIT_CENTURY,
|
|
|
|
},
|
|
|
|
[ds_1388] = {
|
|
|
|
.offset = 1,
|
|
|
|
.trickle_charger_reg = 0x0a,
|
|
|
|
},
|
|
|
|
[ds_3231] = {
|
|
|
|
.alarm = 1,
|
|
|
|
.century_reg = DS1307_REG_MONTH,
|
|
|
|
.century_bit = DS1337_BIT_CENTURY,
|
|
|
|
.bbsqi_bit = DS3231_BIT_BBSQW,
|
|
|
|
},
|
|
|
|
[rx_8130] = {
|
|
|
|
.alarm = 1,
|
|
|
|
/* this is battery backed SRAM */
|
|
|
|
.nvram_offset = 0x20,
|
|
|
|
.nvram_size = 4, /* 32bit (4 word x 8 bit) */
|
|
|
|
.offset = 0x10,
|
|
|
|
.irq_handler = rx8130_irq,
|
|
|
|
.rtc_ops = &rx8130_rtc_ops,
|
|
|
|
},
|
|
|
|
[m41t0] = {
|
|
|
|
.rtc_ops = &m41txx_rtc_ops,
|
|
|
|
},
|
|
|
|
[m41t00] = {
|
|
|
|
.rtc_ops = &m41txx_rtc_ops,
|
|
|
|
},
|
|
|
|
[m41t11] = {
|
|
|
|
/* this is battery backed SRAM */
|
|
|
|
.nvram_offset = 8,
|
|
|
|
.nvram_size = 56,
|
|
|
|
.rtc_ops = &m41txx_rtc_ops,
|
|
|
|
},
|
|
|
|
[mcp794xx] = {
|
|
|
|
.alarm = 1,
|
|
|
|
/* this is battery backed SRAM */
|
|
|
|
.nvram_offset = 0x20,
|
|
|
|
.nvram_size = 0x40,
|
|
|
|
.irq_handler = mcp794xx_irq,
|
|
|
|
.rtc_ops = &mcp794xx_rtc_ops,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct i2c_device_id ds1307_id[] = {
|
|
|
|
{ "ds1307", ds_1307 },
|
|
|
|
{ "ds1308", ds_1308 },
|
|
|
|
{ "ds1337", ds_1337 },
|
|
|
|
{ "ds1338", ds_1338 },
|
|
|
|
{ "ds1339", ds_1339 },
|
|
|
|
{ "ds1388", ds_1388 },
|
|
|
|
{ "ds1340", ds_1340 },
|
|
|
|
{ "ds1341", ds_1341 },
|
|
|
|
{ "ds3231", ds_3231 },
|
|
|
|
{ "m41t0", m41t0 },
|
|
|
|
{ "m41t00", m41t00 },
|
|
|
|
{ "m41t11", m41t11 },
|
|
|
|
{ "mcp7940x", mcp794xx },
|
|
|
|
{ "mcp7941x", mcp794xx },
|
|
|
|
{ "pt7c4338", ds_1307 },
|
|
|
|
{ "rx8025", rx_8025 },
|
|
|
|
{ "isl12057", ds_1337 },
|
|
|
|
{ "rx8130", rx_8130 },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, ds1307_id);
|
|
|
|
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static const struct of_device_id ds1307_of_match[] = {
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1307",
|
|
|
|
.data = (void *)ds_1307
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1308",
|
|
|
|
.data = (void *)ds_1308
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1337",
|
|
|
|
.data = (void *)ds_1337
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1338",
|
|
|
|
.data = (void *)ds_1338
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1339",
|
|
|
|
.data = (void *)ds_1339
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1388",
|
|
|
|
.data = (void *)ds_1388
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1340",
|
|
|
|
.data = (void *)ds_1340
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "dallas,ds1341",
|
|
|
|
.data = (void *)ds_1341
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "maxim,ds3231",
|
|
|
|
.data = (void *)ds_3231
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "st,m41t0",
|
|
|
|
.data = (void *)m41t0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "st,m41t00",
|
|
|
|
.data = (void *)m41t00
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "st,m41t11",
|
|
|
|
.data = (void *)m41t11
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "microchip,mcp7940x",
|
|
|
|
.data = (void *)mcp794xx
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "microchip,mcp7941x",
|
|
|
|
.data = (void *)mcp794xx
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "pericom,pt7c4338",
|
|
|
|
.data = (void *)ds_1307
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "epson,rx8025",
|
|
|
|
.data = (void *)rx_8025
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "isil,isl12057",
|
|
|
|
.data = (void *)ds_1337
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.compatible = "epson,rx8130",
|
|
|
|
.data = (void *)rx_8130
|
|
|
|
},
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, ds1307_of_match);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ACPI
|
|
|
|
static const struct acpi_device_id ds1307_acpi_ids[] = {
|
|
|
|
{ .id = "DS1307", .driver_data = ds_1307 },
|
|
|
|
{ .id = "DS1308", .driver_data = ds_1308 },
|
|
|
|
{ .id = "DS1337", .driver_data = ds_1337 },
|
|
|
|
{ .id = "DS1338", .driver_data = ds_1338 },
|
|
|
|
{ .id = "DS1339", .driver_data = ds_1339 },
|
|
|
|
{ .id = "DS1388", .driver_data = ds_1388 },
|
|
|
|
{ .id = "DS1340", .driver_data = ds_1340 },
|
|
|
|
{ .id = "DS1341", .driver_data = ds_1341 },
|
|
|
|
{ .id = "DS3231", .driver_data = ds_3231 },
|
|
|
|
{ .id = "M41T0", .driver_data = m41t0 },
|
|
|
|
{ .id = "M41T00", .driver_data = m41t00 },
|
|
|
|
{ .id = "M41T11", .driver_data = m41t11 },
|
|
|
|
{ .id = "MCP7940X", .driver_data = mcp794xx },
|
|
|
|
{ .id = "MCP7941X", .driver_data = mcp794xx },
|
|
|
|
{ .id = "PT7C4338", .driver_data = ds_1307 },
|
|
|
|
{ .id = "RX8025", .driver_data = rx_8025 },
|
|
|
|
{ .id = "ISL12057", .driver_data = ds_1337 },
|
|
|
|
{ .id = "RX8130", .driver_data = rx_8130 },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
|
|
|
|
#endif
|
2017-06-19 04:55:28 +08:00
|
|
|
|
2014-04-04 05:49:55 +08:00
|
|
|
/*
|
2019-01-25 22:35:55 +08:00
|
|
|
* The ds1337 and ds1339 both have two alarms, but we only use the first
|
|
|
|
* one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
|
|
|
|
* signal; ds1339 chips have only one alarm signal.
|
2014-04-04 05:49:55 +08:00
|
|
|
*/
|
2019-01-25 22:35:55 +08:00
|
|
|
static irqreturn_t ds1307_irq(int irq, void *dev_id)
|
2014-04-04 05:49:55 +08:00
|
|
|
{
|
2019-01-25 22:35:55 +08:00
|
|
|
struct ds1307 *ds1307 = dev_id;
|
|
|
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
|
|
|
int stat, ret;
|
2014-04-04 05:49:55 +08:00
|
|
|
|
2015-06-24 00:15:10 +08:00
|
|
|
mutex_lock(lock);
|
2019-01-25 22:35:55 +08:00
|
|
|
ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat);
|
2017-03-11 01:52:34 +08:00
|
|
|
if (ret)
|
2014-04-04 05:49:55 +08:00
|
|
|
goto out;
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
if (stat & DS1337_BIT_A1I) {
|
|
|
|
stat &= ~DS1337_BIT_A1I;
|
|
|
|
regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat);
|
2014-04-04 05:49:55 +08:00
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
|
|
|
|
DS1337_BIT_A1IE, 0);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
|
|
|
|
}
|
2014-04-04 05:49:55 +08:00
|
|
|
|
|
|
|
out:
|
2015-06-24 00:15:10 +08:00
|
|
|
mutex_unlock(lock);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
2014-04-04 05:49:55 +08:00
|
|
|
}
|
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
/*----------------------------------------------------------------------*/
|
2018-07-26 01:26:04 +08:00
|
|
|
|
2019-01-25 22:35:55 +08:00
|
|
|
static const struct rtc_class_ops ds13xx_rtc_ops = {
|
|
|
|
.read_time = ds1307_get_time,
|
|
|
|
.set_time = ds1307_set_time,
|
|
|
|
.read_alarm = ds1337_read_alarm,
|
|
|
|
.set_alarm = ds1337_set_alarm,
|
|
|
|
.alarm_irq_enable = ds1307_alarm_irq_enable,
|
|
|
|
};
|
2018-07-26 01:26:04 +08:00
|
|
|
|
2018-09-20 22:35:26 +08:00
|
|
|
static ssize_t frequency_test_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
2018-07-26 01:26:05 +08:00
|
|
|
{
|
2018-09-20 22:35:26 +08:00
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev->parent);
|
2018-07-26 01:26:05 +08:00
|
|
|
bool freq_test_en;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = kstrtobool(buf, &freq_test_en);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "Failed to store RTC Frequency Test attribute\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
regmap_update_bits(ds1307->regmap, M41TXX_REG_CONTROL, M41TXX_BIT_FT,
|
|
|
|
freq_test_en ? M41TXX_BIT_FT : 0);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2018-09-20 22:35:26 +08:00
|
|
|
static ssize_t frequency_test_show(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
char *buf)
|
2018-07-26 01:26:05 +08:00
|
|
|
{
|
2018-09-20 22:35:26 +08:00
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev->parent);
|
2018-07-26 01:26:05 +08:00
|
|
|
unsigned int ctrl_reg;
|
|
|
|
|
|
|
|
regmap_read(ds1307->regmap, M41TXX_REG_CONTROL, &ctrl_reg);
|
|
|
|
|
|
|
|
return scnprintf(buf, PAGE_SIZE, (ctrl_reg & M41TXX_BIT_FT) ? "on\n" :
|
|
|
|
"off\n");
|
|
|
|
}
|
|
|
|
|
2018-09-20 22:35:26 +08:00
|
|
|
static DEVICE_ATTR_RW(frequency_test);
|
2018-07-26 01:26:05 +08:00
|
|
|
|
|
|
|
static struct attribute *rtc_freq_test_attrs[] = {
|
2018-09-20 22:35:26 +08:00
|
|
|
&dev_attr_frequency_test.attr,
|
2018-07-26 01:26:05 +08:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group rtc_freq_test_attr_group = {
|
|
|
|
.attrs = rtc_freq_test_attrs,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ds1307_add_frequency_test(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (ds1307->type) {
|
|
|
|
case m41t0:
|
|
|
|
case m41t00:
|
|
|
|
case m41t11:
|
2018-09-20 22:35:26 +08:00
|
|
|
err = rtc_add_group(ds1307->rtc, &rtc_freq_test_attr_group);
|
|
|
|
if (err)
|
2018-07-26 01:26:05 +08:00
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-04 05:49:55 +08:00
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
2017-07-06 17:42:07 +08:00
|
|
|
static int ds1307_nvram_read(void *priv, unsigned int offset, void *val,
|
|
|
|
size_t bytes)
|
2007-11-15 08:58:32 +08:00
|
|
|
{
|
2017-07-06 17:42:07 +08:00
|
|
|
struct ds1307 *ds1307 = priv;
|
2017-07-12 13:49:54 +08:00
|
|
|
const struct chip_desc *chip = &chips[ds1307->type];
|
2007-11-15 08:58:32 +08:00
|
|
|
|
2017-07-12 13:49:54 +08:00
|
|
|
return regmap_bulk_read(ds1307->regmap, chip->nvram_offset + offset,
|
2017-07-06 17:42:07 +08:00
|
|
|
val, bytes);
|
2007-11-15 08:58:32 +08:00
|
|
|
}
|
|
|
|
|
2017-07-06 17:42:07 +08:00
|
|
|
static int ds1307_nvram_write(void *priv, unsigned int offset, void *val,
|
|
|
|
size_t bytes)
|
2007-11-15 08:58:32 +08:00
|
|
|
{
|
2017-07-06 17:42:07 +08:00
|
|
|
struct ds1307 *ds1307 = priv;
|
2017-07-12 13:49:54 +08:00
|
|
|
const struct chip_desc *chip = &chips[ds1307->type];
|
2007-11-15 08:58:32 +08:00
|
|
|
|
2017-07-12 13:49:54 +08:00
|
|
|
return regmap_bulk_write(ds1307->regmap, chip->nvram_offset + offset,
|
2017-07-06 17:42:07 +08:00
|
|
|
val, bytes);
|
2007-11-15 08:58:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
2017-07-12 13:49:28 +08:00
|
|
|
static u8 ds1307_trickle_init(struct ds1307 *ds1307,
|
2017-07-12 13:49:33 +08:00
|
|
|
const struct chip_desc *chip)
|
2014-10-14 06:52:48 +08:00
|
|
|
{
|
2017-09-05 04:46:04 +08:00
|
|
|
u32 ohms;
|
2014-10-14 06:52:48 +08:00
|
|
|
bool diode = true;
|
|
|
|
|
|
|
|
if (!chip->do_trickle_setup)
|
2017-07-12 13:49:28 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms",
|
|
|
|
&ohms))
|
2017-07-12 13:49:28 +08:00
|
|
|
return 0;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
if (device_property_read_bool(ds1307->dev, "trickle-diode-disable"))
|
2014-10-14 06:52:48 +08:00
|
|
|
diode = false;
|
2017-07-12 13:49:28 +08:00
|
|
|
|
|
|
|
return chip->do_trickle_setup(ds1307, ohms, diode);
|
2014-10-14 06:52:48 +08:00
|
|
|
}
|
|
|
|
|
2016-01-24 23:22:16 +08:00
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
2017-09-28 04:41:26 +08:00
|
|
|
#if IS_REACHABLE(CONFIG_HWMON)
|
2016-01-24 23:22:16 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Temperature sensor support for ds3231 devices.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DS3231_REG_TEMPERATURE 0x11
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A user-initiated temperature conversion is not started by this function,
|
|
|
|
* so the temperature is updated once every 64 seconds.
|
|
|
|
*/
|
2016-04-18 08:21:42 +08:00
|
|
|
static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC)
|
2016-01-24 23:22:16 +08:00
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
|
|
|
u8 temp_buf[2];
|
|
|
|
s16 temp;
|
|
|
|
int ret;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
ret = regmap_bulk_read(ds1307->regmap, DS3231_REG_TEMPERATURE,
|
|
|
|
temp_buf, sizeof(temp_buf));
|
|
|
|
if (ret)
|
2016-01-24 23:22:16 +08:00
|
|
|
return ret;
|
|
|
|
/*
|
|
|
|
* Temperature is represented as a 10-bit code with a resolution of
|
|
|
|
* 0.25 degree celsius and encoded in two's complement format.
|
|
|
|
*/
|
|
|
|
temp = (temp_buf[0] << 8) | temp_buf[1];
|
|
|
|
temp >>= 6;
|
|
|
|
*mC = temp * 250;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t ds3231_hwmon_show_temp(struct device *dev,
|
2017-09-05 04:46:06 +08:00
|
|
|
struct device_attribute *attr, char *buf)
|
2016-01-24 23:22:16 +08:00
|
|
|
{
|
|
|
|
int ret;
|
2016-04-18 08:21:42 +08:00
|
|
|
s32 temp;
|
2016-01-24 23:22:16 +08:00
|
|
|
|
|
|
|
ret = ds3231_hwmon_read_temp(dev, &temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return sprintf(buf, "%d\n", temp);
|
|
|
|
}
|
2017-09-05 04:46:08 +08:00
|
|
|
static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp,
|
2017-09-05 04:46:06 +08:00
|
|
|
NULL, 0);
|
2016-01-24 23:22:16 +08:00
|
|
|
|
|
|
|
static struct attribute *ds3231_hwmon_attrs[] = {
|
|
|
|
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(ds3231_hwmon);
|
|
|
|
|
|
|
|
static void ds1307_hwmon_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
if (ds1307->type != ds_3231)
|
|
|
|
return;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name,
|
2017-09-05 04:46:06 +08:00
|
|
|
ds1307,
|
|
|
|
ds3231_hwmon_groups);
|
2016-01-24 23:22:16 +08:00
|
|
|
if (IS_ERR(dev)) {
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev, "unable to register hwmon device %ld\n",
|
|
|
|
PTR_ERR(dev));
|
2016-01-24 23:22:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static void ds1307_hwmon_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-01-31 22:10:10 +08:00
|
|
|
#endif /* CONFIG_RTC_DRV_DS1307_HWMON */
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Square-wave output support for DS3231
|
|
|
|
* Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_COMMON_CLK
|
|
|
|
|
|
|
|
enum {
|
|
|
|
DS3231_CLK_SQW = 0,
|
|
|
|
DS3231_CLK_32KHZ,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define clk_sqw_to_ds1307(clk) \
|
|
|
|
container_of(clk, struct ds1307, clks[DS3231_CLK_SQW])
|
|
|
|
#define clk_32khz_to_ds1307(clk) \
|
|
|
|
container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ])
|
|
|
|
|
|
|
|
static int ds3231_clk_sqw_rates[] = {
|
|
|
|
1,
|
|
|
|
1024,
|
|
|
|
4096,
|
|
|
|
8192,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value)
|
|
|
|
{
|
|
|
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(lock);
|
2017-06-05 23:57:29 +08:00
|
|
|
ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL,
|
|
|
|
mask, value);
|
2016-01-31 22:10:10 +08:00
|
|
|
mutex_unlock(lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw,
|
|
|
|
unsigned long parent_rate)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
2017-03-11 01:52:34 +08:00
|
|
|
int control, ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
int rate_sel = 0;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
if (control & DS1337_BIT_RS1)
|
|
|
|
rate_sel += 1;
|
|
|
|
if (control & DS1337_BIT_RS2)
|
|
|
|
rate_sel += 2;
|
|
|
|
|
|
|
|
return ds3231_clk_sqw_rates[rate_sel];
|
|
|
|
}
|
|
|
|
|
|
|
|
static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
|
2017-09-05 04:46:06 +08:00
|
|
|
unsigned long *prate)
|
2016-01-31 22:10:10 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) {
|
|
|
|
if (ds3231_clk_sqw_rates[i] <= rate)
|
|
|
|
return ds3231_clk_sqw_rates[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
2017-09-05 04:46:06 +08:00
|
|
|
unsigned long parent_rate)
|
2016-01-31 22:10:10 +08:00
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
|
|
|
int control = 0;
|
|
|
|
int rate_sel;
|
|
|
|
|
|
|
|
for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates);
|
|
|
|
rate_sel++) {
|
|
|
|
if (ds3231_clk_sqw_rates[rate_sel] == rate)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (rate_sel & 1)
|
|
|
|
control |= DS1337_BIT_RS1;
|
|
|
|
if (rate_sel & 2)
|
|
|
|
control |= DS1337_BIT_RS2;
|
|
|
|
|
|
|
|
return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2,
|
|
|
|
control);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_sqw_prepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
|
|
|
|
|
|
|
return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ds3231_clk_sqw_unprepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
|
|
|
|
|
|
|
ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw);
|
2017-03-11 01:52:34 +08:00
|
|
|
int control, ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
|
|
|
|
return !(control & DS1337_BIT_INTCN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops ds3231_clk_sqw_ops = {
|
|
|
|
.prepare = ds3231_clk_sqw_prepare,
|
|
|
|
.unprepare = ds3231_clk_sqw_unprepare,
|
|
|
|
.is_prepared = ds3231_clk_sqw_is_prepared,
|
|
|
|
.recalc_rate = ds3231_clk_sqw_recalc_rate,
|
|
|
|
.round_rate = ds3231_clk_sqw_round_rate,
|
|
|
|
.set_rate = ds3231_clk_sqw_set_rate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw,
|
2017-09-05 04:46:06 +08:00
|
|
|
unsigned long parent_rate)
|
2016-01-31 22:10:10 +08:00
|
|
|
{
|
|
|
|
return 32768;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable)
|
|
|
|
{
|
|
|
|
struct mutex *lock = &ds1307->rtc->ops_lock;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mutex_lock(lock);
|
2017-06-05 23:57:29 +08:00
|
|
|
ret = regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS,
|
|
|
|
DS3231_BIT_EN32KHZ,
|
|
|
|
enable ? DS3231_BIT_EN32KHZ : 0);
|
2016-01-31 22:10:10 +08:00
|
|
|
mutex_unlock(lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_32khz_prepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
|
|
|
|
|
|
|
return ds3231_clk_32khz_control(ds1307, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ds3231_clk_32khz_unprepare(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
|
|
|
|
|
|
|
ds3231_clk_32khz_control(ds1307, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw);
|
2017-03-11 01:52:34 +08:00
|
|
|
int status, ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-01-31 22:10:10 +08:00
|
|
|
|
|
|
|
return !!(status & DS3231_BIT_EN32KHZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct clk_ops ds3231_clk_32khz_ops = {
|
|
|
|
.prepare = ds3231_clk_32khz_prepare,
|
|
|
|
.unprepare = ds3231_clk_32khz_unprepare,
|
|
|
|
.is_prepared = ds3231_clk_32khz_is_prepared,
|
|
|
|
.recalc_rate = ds3231_clk_32khz_recalc_rate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct clk_init_data ds3231_clks_init[] = {
|
|
|
|
[DS3231_CLK_SQW] = {
|
|
|
|
.name = "ds3231_clk_sqw",
|
|
|
|
.ops = &ds3231_clk_sqw_ops,
|
|
|
|
},
|
|
|
|
[DS3231_CLK_32KHZ] = {
|
|
|
|
.name = "ds3231_clk_32khz",
|
|
|
|
.ops = &ds3231_clk_32khz_ops,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ds3231_clks_register(struct ds1307 *ds1307)
|
|
|
|
{
|
2017-03-11 01:52:34 +08:00
|
|
|
struct device_node *node = ds1307->dev->of_node;
|
2016-01-31 22:10:10 +08:00
|
|
|
struct clk_onecell_data *onecell;
|
|
|
|
int i;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL);
|
2016-01-31 22:10:10 +08:00
|
|
|
if (!onecell)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
onecell->clk_num = ARRAY_SIZE(ds3231_clks_init);
|
2017-03-11 01:52:34 +08:00
|
|
|
onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num,
|
|
|
|
sizeof(onecell->clks[0]), GFP_KERNEL);
|
2016-01-31 22:10:10 +08:00
|
|
|
if (!onecell->clks)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) {
|
|
|
|
struct clk_init_data init = ds3231_clks_init[i];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interrupt signal due to alarm conditions and square-wave
|
|
|
|
* output share same pin, so don't initialize both.
|
|
|
|
*/
|
|
|
|
if (i == DS3231_CLK_SQW && test_bit(HAS_ALARM, &ds1307->flags))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* optional override of the clockname */
|
|
|
|
of_property_read_string_index(node, "clock-output-names", i,
|
2017-09-05 04:46:06 +08:00
|
|
|
&init.name);
|
2016-01-31 22:10:10 +08:00
|
|
|
ds1307->clks[i].init = &init;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
onecell->clks[i] = devm_clk_register(ds1307->dev,
|
|
|
|
&ds1307->clks[i]);
|
2016-01-31 22:10:10 +08:00
|
|
|
if (IS_ERR(onecell->clks[i]))
|
|
|
|
return PTR_ERR(onecell->clks[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
of_clk_add_provider(node, of_clk_src_onecell_get, onecell);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ds1307_clks_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ds1307->type != ds_3231)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = ds3231_clks_register(ds1307);
|
|
|
|
if (ret) {
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev, "unable to register clock device %d\n",
|
|
|
|
ret);
|
2016-01-31 22:10:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static void ds1307_clks_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_COMMON_CLK */
|
2016-01-24 23:22:16 +08:00
|
|
|
|
2020-03-30 10:55:00 +08:00
|
|
|
#ifdef CONFIG_WATCHDOG_CORE
|
|
|
|
static const struct watchdog_info ds1388_wdt_info = {
|
|
|
|
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
|
|
|
.identity = "DS1388 watchdog",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct watchdog_ops ds1388_wdt_ops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.start = ds1388_wdt_start,
|
|
|
|
.stop = ds1388_wdt_stop,
|
|
|
|
.ping = ds1388_wdt_ping,
|
|
|
|
.set_timeout = ds1388_wdt_set_timeout,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
static void ds1307_wdt_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
struct watchdog_device *wdt;
|
2020-07-27 11:46:14 +08:00
|
|
|
int err;
|
|
|
|
int val;
|
2020-03-30 10:55:00 +08:00
|
|
|
|
|
|
|
if (ds1307->type != ds_1388)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wdt = devm_kzalloc(ds1307->dev, sizeof(*wdt), GFP_KERNEL);
|
2020-04-03 19:04:37 +08:00
|
|
|
if (!wdt)
|
|
|
|
return;
|
2020-03-30 10:55:00 +08:00
|
|
|
|
2020-07-27 11:46:14 +08:00
|
|
|
err = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &val);
|
|
|
|
if (!err && val & DS1388_BIT_WF)
|
|
|
|
wdt->bootstatus = WDIOF_CARDRESET;
|
|
|
|
|
2020-03-30 10:55:00 +08:00
|
|
|
wdt->info = &ds1388_wdt_info;
|
|
|
|
wdt->ops = &ds1388_wdt_ops;
|
|
|
|
wdt->timeout = 99;
|
|
|
|
wdt->max_timeout = 99;
|
|
|
|
wdt->min_timeout = 1;
|
|
|
|
|
|
|
|
watchdog_init_timeout(wdt, 0, ds1307->dev);
|
|
|
|
watchdog_set_drvdata(wdt, ds1307);
|
|
|
|
devm_watchdog_register_device(ds1307->dev, wdt);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void ds1307_wdt_register(struct ds1307 *ds1307)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_WATCHDOG_CORE */
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
static const struct regmap_config regmap_config = {
|
|
|
|
.reg_bits = 8,
|
|
|
|
.val_bits = 8,
|
|
|
|
};
|
|
|
|
|
2012-12-22 05:09:38 +08:00
|
|
|
static int ds1307_probe(struct i2c_client *client,
|
|
|
|
const struct i2c_device_id *id)
|
2006-06-25 20:48:17 +08:00
|
|
|
{
|
|
|
|
struct ds1307 *ds1307;
|
|
|
|
int err = -ENODEV;
|
2017-08-30 03:52:56 +08:00
|
|
|
int tmp;
|
2017-07-12 13:49:33 +08:00
|
|
|
const struct chip_desc *chip;
|
2017-07-12 13:49:37 +08:00
|
|
|
bool want_irq;
|
2016-01-22 01:10:16 +08:00
|
|
|
bool ds1307_can_wakeup_device = false;
|
2017-09-05 04:46:02 +08:00
|
|
|
unsigned char regs[8];
|
2013-11-13 07:10:41 +08:00
|
|
|
struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev);
|
2017-07-12 13:49:28 +08:00
|
|
|
u8 trickle_charger_setup = 0;
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2013-07-04 06:07:05 +08:00
|
|
|
ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL);
|
2012-03-24 06:02:37 +08:00
|
|
|
if (!ds1307)
|
2007-07-17 19:05:10 +08:00
|
|
|
return -ENOMEM;
|
2007-07-17 19:04:55 +08:00
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_set_drvdata(&client->dev, ds1307);
|
|
|
|
ds1307->dev = &client->dev;
|
|
|
|
ds1307->name = client->name;
|
|
|
|
|
|
|
|
ds1307->regmap = devm_regmap_init_i2c(client, ®map_config);
|
|
|
|
if (IS_ERR(ds1307->regmap)) {
|
|
|
|
dev_err(ds1307->dev, "regmap allocation failed\n");
|
|
|
|
return PTR_ERR(ds1307->regmap);
|
|
|
|
}
|
2009-06-18 07:26:08 +08:00
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
i2c_set_clientdata(client, ds1307);
|
2017-03-03 22:29:15 +08:00
|
|
|
|
|
|
|
if (client->dev.of_node) {
|
|
|
|
ds1307->type = (enum ds_type)
|
|
|
|
of_device_get_match_data(&client->dev);
|
|
|
|
chip = &chips[ds1307->type];
|
|
|
|
} else if (id) {
|
2016-11-30 10:57:31 +08:00
|
|
|
chip = &chips[id->driver_data];
|
|
|
|
ds1307->type = id->driver_data;
|
|
|
|
} else {
|
|
|
|
const struct acpi_device_id *acpi_id;
|
|
|
|
|
|
|
|
acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids),
|
2017-03-11 01:52:34 +08:00
|
|
|
ds1307->dev);
|
2016-11-30 10:57:31 +08:00
|
|
|
if (!acpi_id)
|
|
|
|
return -ENODEV;
|
|
|
|
chip = &chips[acpi_id->driver_data];
|
|
|
|
ds1307->type = acpi_id->driver_data;
|
|
|
|
}
|
2009-06-18 07:26:08 +08:00
|
|
|
|
2017-07-12 13:49:37 +08:00
|
|
|
want_irq = client->irq > 0 && chip->alarm;
|
|
|
|
|
2016-11-30 10:57:31 +08:00
|
|
|
if (!pdata)
|
2017-07-12 13:49:28 +08:00
|
|
|
trickle_charger_setup = ds1307_trickle_init(ds1307, chip);
|
2016-11-30 10:57:31 +08:00
|
|
|
else if (pdata->trickle_charger_setup)
|
2017-07-12 13:49:28 +08:00
|
|
|
trickle_charger_setup = pdata->trickle_charger_setup;
|
2014-10-14 06:52:48 +08:00
|
|
|
|
2017-07-12 13:49:28 +08:00
|
|
|
if (trickle_charger_setup && chip->trickle_charger_reg) {
|
|
|
|
trickle_charger_setup |= DS13XX_TRICKLE_CHARGER_MAGIC;
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_dbg(ds1307->dev,
|
|
|
|
"writing trickle charger info 0x%x to 0x%x\n",
|
2017-07-12 13:49:28 +08:00
|
|
|
trickle_charger_setup, chip->trickle_charger_reg);
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap, chip->trickle_charger_reg,
|
2017-07-12 13:49:28 +08:00
|
|
|
trickle_charger_setup);
|
2014-10-14 06:52:48 +08:00
|
|
|
}
|
2012-05-30 06:07:38 +08:00
|
|
|
|
2016-01-22 01:10:16 +08:00
|
|
|
#ifdef CONFIG_OF
|
|
|
|
/*
|
|
|
|
* For devices with no IRQ directly connected to the SoC, the RTC chip
|
|
|
|
* can be forced as a wakeup source by stating that explicitly in
|
|
|
|
* the device's .dts file using the "wakeup-source" boolean property.
|
|
|
|
* If the "wakeup-source" property is set, don't request an IRQ.
|
|
|
|
* This will guarantee the 'wakealarm' sysfs entry is available on the device,
|
|
|
|
* if supported by the RTC.
|
|
|
|
*/
|
2017-07-12 13:49:37 +08:00
|
|
|
if (chip->alarm && of_property_read_bool(client->dev.of_node,
|
|
|
|
"wakeup-source"))
|
2016-07-13 08:36:41 +08:00
|
|
|
ds1307_can_wakeup_device = true;
|
2016-01-22 01:10:16 +08:00
|
|
|
#endif
|
|
|
|
|
2007-07-17 19:04:55 +08:00
|
|
|
switch (ds1307->type) {
|
|
|
|
case ds_1337:
|
|
|
|
case ds_1339:
|
rtc: ds1307: add basic support for ds1341 chip
This adds support for reading and writing date/time from/to ds1341 chip.
ds1341 chip has other features - alarms, input clock (can be used instead
of intercal oscillator for better accuracy), output clock ("square wave
generation"). However, not all of that is available at the same time.
Same chip pins, CLKIN/nINTA and SQW/nINTB, can be used either for
input/output clocks, or for alarm interrupts. Role of these pins on
particular board depends on hardware wiring.
We can add device tree properties that describe if each of pins is wired
as clock, or as interrupt, or left unconnected, and enable support for
corresponding functionality based on that. But that is cumbersome, requires
hardware for testing, and has to deal with bit enabling/disabling output
clock also affects which pins alarm interrupts are routed to.
Another factor is that there are hardware setups (i.e. ZII RDU2) that
power DS1341 from SuperCap, which makes power saving critical. For such
setups, kernel driver should leave register bits that control mentioned
pins in the state configured by bootloader.
Given all that, it was decided to limit support to "only date/time" for
now. That is enough for common use case. Full (and cumbersome)
implementation can be added later if ever needed.
Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Aleksander Morgado <aleksander@aleksander.es>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2017-08-24 14:32:11 +08:00
|
|
|
case ds_1341:
|
2009-06-18 07:26:10 +08:00
|
|
|
case ds_3231:
|
2007-07-17 19:05:06 +08:00
|
|
|
/* get registers that the "rtc" read below won't read... */
|
2017-03-11 01:52:34 +08:00
|
|
|
err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs, 2);
|
2017-03-11 01:52:34 +08:00
|
|
|
if (err) {
|
|
|
|
dev_dbg(ds1307->dev, "read error %d\n", err);
|
2013-07-04 06:07:05 +08:00
|
|
|
goto exit;
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
|
|
|
|
2007-07-17 19:05:06 +08:00
|
|
|
/* oscillator off? turn it on, so clock can tick. */
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[0] & DS1337_BIT_nEOSC)
|
|
|
|
regs[0] &= ~DS1337_BIT_nEOSC;
|
2008-10-16 13:02:58 +08:00
|
|
|
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
2016-01-22 01:10:16 +08:00
|
|
|
* Using IRQ or defined as wakeup-source?
|
|
|
|
* Disable the square wave and both alarms.
|
2009-06-18 07:26:10 +08:00
|
|
|
* For some variants, be sure alarms can trigger when we're
|
|
|
|
* running on Vbackup (BBSQI/BBSQW)
|
2008-10-16 13:02:58 +08:00
|
|
|
*/
|
2017-07-12 13:49:37 +08:00
|
|
|
if (want_irq || ds1307_can_wakeup_device) {
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[0] |= DS1337_BIT_INTCN | chip->bbsqi_bit;
|
|
|
|
regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap, DS1337_REG_CONTROL,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[0]);
|
2007-07-17 19:05:06 +08:00
|
|
|
|
|
|
|
/* oscillator fault? clear flag, and warn */
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[1] & DS1337_BIT_OSF) {
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap, DS1337_REG_STATUS,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[1] & ~DS1337_BIT_OSF);
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev, "SET TIME!\n");
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
2007-07-17 19:04:55 +08:00
|
|
|
break;
|
2009-04-01 06:24:58 +08:00
|
|
|
|
|
|
|
case rx_8025:
|
2017-03-11 01:52:34 +08:00
|
|
|
err = regmap_bulk_read(ds1307->regmap,
|
2017-09-05 04:46:02 +08:00
|
|
|
RX8025_REG_CTRL1 << 4 | 0x08, regs, 2);
|
2017-03-11 01:52:34 +08:00
|
|
|
if (err) {
|
|
|
|
dev_dbg(ds1307->dev, "read error %d\n", err);
|
2013-07-04 06:07:05 +08:00
|
|
|
goto exit;
|
2009-04-01 06:24:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* oscillator off? turn it on, so clock can tick. */
|
2017-09-05 04:46:02 +08:00
|
|
|
if (!(regs[1] & RX8025_BIT_XST)) {
|
|
|
|
regs[1] |= RX8025_BIT_XST;
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap,
|
|
|
|
RX8025_REG_CTRL2 << 4 | 0x08,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[1]);
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev,
|
2009-04-01 06:24:58 +08:00
|
|
|
"oscillator stop detected - SET TIME!\n");
|
|
|
|
}
|
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[1] & RX8025_BIT_PON) {
|
|
|
|
regs[1] &= ~RX8025_BIT_PON;
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap,
|
|
|
|
RX8025_REG_CTRL2 << 4 | 0x08,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[1]);
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev, "power-on detected\n");
|
2009-04-01 06:24:58 +08:00
|
|
|
}
|
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[1] & RX8025_BIT_VDET) {
|
|
|
|
regs[1] &= ~RX8025_BIT_VDET;
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap,
|
|
|
|
RX8025_REG_CTRL2 << 4 | 0x08,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[1]);
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_warn(ds1307->dev, "voltage drop detected\n");
|
2009-04-01 06:24:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure we are running in 24hour mode */
|
2017-09-05 04:46:02 +08:00
|
|
|
if (!(regs[0] & RX8025_BIT_2412)) {
|
2009-04-01 06:24:58 +08:00
|
|
|
u8 hour;
|
|
|
|
|
|
|
|
/* switch to 24 hour mode */
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap,
|
|
|
|
RX8025_REG_CTRL1 << 4 | 0x08,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs[0] | RX8025_BIT_2412);
|
2017-03-11 01:52:34 +08:00
|
|
|
|
|
|
|
err = regmap_bulk_read(ds1307->regmap,
|
|
|
|
RX8025_REG_CTRL1 << 4 | 0x08,
|
2017-09-05 04:46:02 +08:00
|
|
|
regs, 2);
|
2017-03-11 01:52:34 +08:00
|
|
|
if (err) {
|
|
|
|
dev_dbg(ds1307->dev, "read error %d\n", err);
|
2013-07-04 06:07:05 +08:00
|
|
|
goto exit;
|
2009-04-01 06:24:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* correct hour */
|
2017-09-05 04:46:02 +08:00
|
|
|
hour = bcd2bin(regs[DS1307_REG_HOUR]);
|
2009-04-01 06:24:58 +08:00
|
|
|
if (hour == 12)
|
|
|
|
hour = 0;
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
|
2009-04-01 06:24:58 +08:00
|
|
|
hour += 12;
|
|
|
|
|
2017-03-11 01:52:34 +08:00
|
|
|
regmap_write(ds1307->regmap,
|
|
|
|
DS1307_REG_HOUR << 4 | 0x08, hour);
|
2009-04-01 06:24:58 +08:00
|
|
|
}
|
|
|
|
break;
|
2020-08-17 07:57:31 +08:00
|
|
|
case ds_1388:
|
|
|
|
err = regmap_read(ds1307->regmap, DS1388_REG_CONTROL, &tmp);
|
|
|
|
if (err) {
|
|
|
|
dev_dbg(ds1307->dev, "read error %d\n", err);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* oscillator off? turn it on, so clock can tick. */
|
|
|
|
if (tmp & DS1388_BIT_nEOSC) {
|
|
|
|
tmp &= ~DS1388_BIT_nEOSC;
|
|
|
|
regmap_write(ds1307->regmap, DS1388_REG_CONTROL, tmp);
|
|
|
|
}
|
|
|
|
break;
|
2007-07-17 19:04:55 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2006-06-25 20:48:17 +08:00
|
|
|
|
|
|
|
/* read RTC registers */
|
2017-09-05 04:46:02 +08:00
|
|
|
err = regmap_bulk_read(ds1307->regmap, chip->offset, regs,
|
|
|
|
sizeof(regs));
|
2017-03-11 01:52:34 +08:00
|
|
|
if (err) {
|
|
|
|
dev_dbg(ds1307->dev, "read error %d\n", err);
|
2013-07-04 06:07:05 +08:00
|
|
|
goto exit;
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
|
|
|
|
2019-04-11 06:16:29 +08:00
|
|
|
if (ds1307->type == mcp794xx &&
|
|
|
|
!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
|
|
|
|
regmap_write(ds1307->regmap, DS1307_REG_WDAY,
|
|
|
|
regs[DS1307_REG_WDAY] |
|
|
|
|
MCP794XX_BIT_VBATEN);
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
2007-07-17 19:04:55 +08:00
|
|
|
|
2017-09-05 04:46:02 +08:00
|
|
|
tmp = regs[DS1307_REG_HOUR];
|
2007-07-17 19:05:10 +08:00
|
|
|
switch (ds1307->type) {
|
|
|
|
case ds_1340:
|
2017-03-24 07:54:57 +08:00
|
|
|
case m41t0:
|
2007-07-17 19:05:10 +08:00
|
|
|
case m41t00:
|
2018-05-17 05:08:40 +08:00
|
|
|
case m41t11:
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* NOTE: ignores century bits; fix before deploying
|
2007-07-17 19:05:10 +08:00
|
|
|
* systems that will run through year 2100.
|
|
|
|
*/
|
|
|
|
break;
|
2009-04-01 06:24:58 +08:00
|
|
|
case rx_8025:
|
|
|
|
break;
|
2007-07-17 19:05:10 +08:00
|
|
|
default:
|
|
|
|
if (!(tmp & DS1307_BIT_12HR))
|
|
|
|
break;
|
|
|
|
|
2012-03-24 06:02:37 +08:00
|
|
|
/*
|
|
|
|
* Be sure we're in 24 hour mode. Multi-master systems
|
2007-07-17 19:05:10 +08:00
|
|
|
* take note...
|
|
|
|
*/
|
2008-10-19 11:28:41 +08:00
|
|
|
tmp = bcd2bin(tmp & 0x1f);
|
2007-07-17 19:05:10 +08:00
|
|
|
if (tmp == 12)
|
|
|
|
tmp = 0;
|
2017-09-05 04:46:02 +08:00
|
|
|
if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
|
2007-07-17 19:05:10 +08:00
|
|
|
tmp += 12;
|
2017-07-12 13:49:47 +08:00
|
|
|
regmap_write(ds1307->regmap, chip->offset + DS1307_REG_HOUR,
|
2017-03-11 01:52:34 +08:00
|
|
|
bin2bcd(tmp));
|
2006-06-25 20:48:17 +08:00
|
|
|
}
|
|
|
|
|
2017-07-12 13:49:37 +08:00
|
|
|
if (want_irq || ds1307_can_wakeup_device) {
|
2017-03-11 01:52:34 +08:00
|
|
|
device_set_wakeup_capable(ds1307->dev, true);
|
2015-11-26 22:37:13 +08:00
|
|
|
set_bit(HAS_ALARM, &ds1307->flags);
|
|
|
|
}
|
2017-07-06 17:42:06 +08:00
|
|
|
|
|
|
|
ds1307->rtc = devm_rtc_allocate_device(ds1307->dev);
|
2017-09-05 04:46:07 +08:00
|
|
|
if (IS_ERR(ds1307->rtc))
|
2014-04-04 05:49:36 +08:00
|
|
|
return PTR_ERR(ds1307->rtc);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
2017-07-12 13:49:37 +08:00
|
|
|
if (ds1307_can_wakeup_device && !want_irq) {
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_info(ds1307->dev,
|
|
|
|
"'wakeup-source' is set, request for an IRQ is disabled!\n");
|
2016-01-22 01:10:16 +08:00
|
|
|
/* We cannot support UIE mode if we do not have an IRQ line */
|
|
|
|
ds1307->rtc->uie_unsupported = 1;
|
|
|
|
}
|
|
|
|
|
2008-10-16 13:02:58 +08:00
|
|
|
if (want_irq) {
|
2017-07-12 13:49:41 +08:00
|
|
|
err = devm_request_threaded_irq(ds1307->dev, client->irq, NULL,
|
|
|
|
chip->irq_handler ?: ds1307_irq,
|
2015-06-24 00:15:11 +08:00
|
|
|
IRQF_SHARED | IRQF_ONESHOT,
|
2017-06-02 20:13:21 +08:00
|
|
|
ds1307->name, ds1307);
|
2008-10-16 13:02:58 +08:00
|
|
|
if (err) {
|
2014-04-04 05:49:36 +08:00
|
|
|
client->irq = 0;
|
2017-03-11 01:52:34 +08:00
|
|
|
device_set_wakeup_capable(ds1307->dev, false);
|
2015-11-26 22:37:13 +08:00
|
|
|
clear_bit(HAS_ALARM, &ds1307->flags);
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_err(ds1307->dev, "unable to request IRQ!\n");
|
2017-09-05 04:46:07 +08:00
|
|
|
} else {
|
2017-03-11 01:52:34 +08:00
|
|
|
dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq);
|
2017-09-05 04:46:07 +08:00
|
|
|
}
|
2008-10-16 13:02:58 +08:00
|
|
|
}
|
|
|
|
|
2018-02-13 06:47:22 +08:00
|
|
|
ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops;
|
2018-09-20 22:35:26 +08:00
|
|
|
err = ds1307_add_frequency_test(ds1307);
|
2018-02-13 06:47:22 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2018-09-20 22:35:26 +08:00
|
|
|
err = rtc_register_device(ds1307->rtc);
|
2018-07-26 01:26:05 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2012-03-24 06:02:38 +08:00
|
|
|
if (chip->nvram_size) {
|
2018-02-13 06:47:23 +08:00
|
|
|
struct nvmem_config nvmem_cfg = {
|
|
|
|
.name = "ds1307_nvram",
|
|
|
|
.word_size = 1,
|
|
|
|
.stride = 1,
|
|
|
|
.size = chip->nvram_size,
|
|
|
|
.reg_read = ds1307_nvram_read,
|
|
|
|
.reg_write = ds1307_nvram_write,
|
|
|
|
.priv = ds1307,
|
|
|
|
};
|
2017-07-06 17:42:07 +08:00
|
|
|
|
|
|
|
ds1307->rtc->nvram_old_abi = true;
|
2018-02-13 06:47:23 +08:00
|
|
|
rtc_nvmem_register(ds1307->rtc, &nvmem_cfg);
|
2007-11-15 08:58:32 +08:00
|
|
|
}
|
|
|
|
|
2016-01-24 23:22:16 +08:00
|
|
|
ds1307_hwmon_register(ds1307);
|
2016-01-31 22:10:10 +08:00
|
|
|
ds1307_clks_register(ds1307);
|
2020-03-30 10:55:00 +08:00
|
|
|
ds1307_wdt_register(ds1307);
|
2016-01-24 23:22:16 +08:00
|
|
|
|
2006-06-25 20:48:17 +08:00
|
|
|
return 0;
|
|
|
|
|
2013-07-04 06:07:05 +08:00
|
|
|
exit:
|
2006-06-25 20:48:17 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct i2c_driver ds1307_driver = {
|
|
|
|
.driver = {
|
2007-07-17 19:05:10 +08:00
|
|
|
.name = "rtc-ds1307",
|
2017-03-03 22:29:15 +08:00
|
|
|
.of_match_table = of_match_ptr(ds1307_of_match),
|
2016-11-30 10:57:31 +08:00
|
|
|
.acpi_match_table = ACPI_PTR(ds1307_acpi_ids),
|
2006-06-25 20:48:17 +08:00
|
|
|
},
|
2007-07-17 19:05:10 +08:00
|
|
|
.probe = ds1307_probe,
|
2008-04-30 05:11:40 +08:00
|
|
|
.id_table = ds1307_id,
|
2006-06-25 20:48:17 +08:00
|
|
|
};
|
|
|
|
|
2012-03-24 06:02:31 +08:00
|
|
|
module_i2c_driver(ds1307_driver);
|
2006-06-25 20:48:17 +08:00
|
|
|
|
|
|
|
MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
|
|
|
|
MODULE_LICENSE("GPL");
|