cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rtc-isl12022.c (6930B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * An I2C driver for the Intersil ISL 12022
      4 *
      5 * Author: Roman Fietze <roman.fietze@telemotive.de>
      6 *
      7 * Based on the Philips PCF8563 RTC
      8 * by Alessandro Zummo <a.zummo@towertech.it>.
      9 */
     10
     11#include <linux/i2c.h>
     12#include <linux/bcd.h>
     13#include <linux/rtc.h>
     14#include <linux/slab.h>
     15#include <linux/module.h>
     16#include <linux/err.h>
     17#include <linux/of.h>
     18#include <linux/of_device.h>
     19
     20/* ISL register offsets */
     21#define ISL12022_REG_SC		0x00
     22#define ISL12022_REG_MN		0x01
     23#define ISL12022_REG_HR		0x02
     24#define ISL12022_REG_DT		0x03
     25#define ISL12022_REG_MO		0x04
     26#define ISL12022_REG_YR		0x05
     27#define ISL12022_REG_DW		0x06
     28
     29#define ISL12022_REG_SR		0x07
     30#define ISL12022_REG_INT	0x08
     31
     32/* ISL register bits */
     33#define ISL12022_HR_MIL		(1 << 7)	/* military or 24 hour time */
     34
     35#define ISL12022_SR_LBAT85	(1 << 2)
     36#define ISL12022_SR_LBAT75	(1 << 1)
     37
     38#define ISL12022_INT_WRTC	(1 << 6)
     39
     40
     41static struct i2c_driver isl12022_driver;
     42
     43struct isl12022 {
     44	struct rtc_device *rtc;
     45
     46	bool write_enabled;	/* true if write enable is set */
     47};
     48
     49
     50static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
     51			      uint8_t *data, size_t n)
     52{
     53	struct i2c_msg msgs[] = {
     54		{
     55			.addr	= client->addr,
     56			.flags	= 0,
     57			.len	= 1,
     58			.buf	= data
     59		},		/* setup read ptr */
     60		{
     61			.addr	= client->addr,
     62			.flags	= I2C_M_RD,
     63			.len	= n,
     64			.buf	= data
     65		}
     66	};
     67
     68	int ret;
     69
     70	data[0] = reg;
     71	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
     72	if (ret != ARRAY_SIZE(msgs)) {
     73		dev_err(&client->dev, "%s: read error, ret=%d\n",
     74			__func__, ret);
     75		return -EIO;
     76	}
     77
     78	return 0;
     79}
     80
     81
     82static int isl12022_write_reg(struct i2c_client *client,
     83			      uint8_t reg, uint8_t val)
     84{
     85	uint8_t data[2] = { reg, val };
     86	int err;
     87
     88	err = i2c_master_send(client, data, sizeof(data));
     89	if (err != sizeof(data)) {
     90		dev_err(&client->dev,
     91			"%s: err=%d addr=%02x, data=%02x\n",
     92			__func__, err, data[0], data[1]);
     93		return -EIO;
     94	}
     95
     96	return 0;
     97}
     98
     99
    100/*
    101 * In the routines that deal directly with the isl12022 hardware, we use
    102 * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
    103 */
    104static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
    105{
    106	struct i2c_client *client = to_i2c_client(dev);
    107	uint8_t buf[ISL12022_REG_INT + 1];
    108	int ret;
    109
    110	ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
    111	if (ret)
    112		return ret;
    113
    114	if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
    115		dev_warn(&client->dev,
    116			 "voltage dropped below %u%%, "
    117			 "date and time is not reliable.\n",
    118			 buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
    119	}
    120
    121	dev_dbg(&client->dev,
    122		"%s: raw data is sec=%02x, min=%02x, hr=%02x, "
    123		"mday=%02x, mon=%02x, year=%02x, wday=%02x, "
    124		"sr=%02x, int=%02x",
    125		__func__,
    126		buf[ISL12022_REG_SC],
    127		buf[ISL12022_REG_MN],
    128		buf[ISL12022_REG_HR],
    129		buf[ISL12022_REG_DT],
    130		buf[ISL12022_REG_MO],
    131		buf[ISL12022_REG_YR],
    132		buf[ISL12022_REG_DW],
    133		buf[ISL12022_REG_SR],
    134		buf[ISL12022_REG_INT]);
    135
    136	tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
    137	tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
    138	tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
    139	tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
    140	tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
    141	tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
    142	tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
    143
    144	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
    145		"mday=%d, mon=%d, year=%d, wday=%d\n",
    146		__func__,
    147		tm->tm_sec, tm->tm_min, tm->tm_hour,
    148		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
    149
    150	return 0;
    151}
    152
    153static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
    154{
    155	struct i2c_client *client = to_i2c_client(dev);
    156	struct isl12022 *isl12022 = i2c_get_clientdata(client);
    157	size_t i;
    158	int ret;
    159	uint8_t buf[ISL12022_REG_DW + 1];
    160
    161	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
    162		"mday=%d, mon=%d, year=%d, wday=%d\n",
    163		__func__,
    164		tm->tm_sec, tm->tm_min, tm->tm_hour,
    165		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
    166
    167	if (!isl12022->write_enabled) {
    168
    169		ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
    170		if (ret)
    171			return ret;
    172
    173		/* Check if WRTC (write rtc enable) is set factory default is
    174		 * 0 (not set) */
    175		if (!(buf[0] & ISL12022_INT_WRTC)) {
    176			dev_info(&client->dev,
    177				 "init write enable and 24 hour format\n");
    178
    179			/* Set the write enable bit. */
    180			ret = isl12022_write_reg(client,
    181						 ISL12022_REG_INT,
    182						 buf[0] | ISL12022_INT_WRTC);
    183			if (ret)
    184				return ret;
    185
    186			/* Write to any RTC register to start RTC, we use the
    187			 * HR register, setting the MIL bit to use the 24 hour
    188			 * format. */
    189			ret = isl12022_read_regs(client, ISL12022_REG_HR,
    190						 buf, 1);
    191			if (ret)
    192				return ret;
    193
    194			ret = isl12022_write_reg(client,
    195						 ISL12022_REG_HR,
    196						 buf[0] | ISL12022_HR_MIL);
    197			if (ret)
    198				return ret;
    199		}
    200
    201		isl12022->write_enabled = true;
    202	}
    203
    204	/* hours, minutes and seconds */
    205	buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
    206	buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
    207	buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
    208
    209	buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
    210
    211	/* month, 1 - 12 */
    212	buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
    213
    214	/* year and century */
    215	buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
    216
    217	buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
    218
    219	/* write register's data */
    220	for (i = 0; i < ARRAY_SIZE(buf); i++) {
    221		ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
    222					 buf[ISL12022_REG_SC + i]);
    223		if (ret)
    224			return -EIO;
    225	}
    226
    227	return 0;
    228}
    229
    230static const struct rtc_class_ops isl12022_rtc_ops = {
    231	.read_time	= isl12022_rtc_read_time,
    232	.set_time	= isl12022_rtc_set_time,
    233};
    234
    235static int isl12022_probe(struct i2c_client *client,
    236			  const struct i2c_device_id *id)
    237{
    238	struct isl12022 *isl12022;
    239
    240	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
    241		return -ENODEV;
    242
    243	isl12022 = devm_kzalloc(&client->dev, sizeof(struct isl12022),
    244				GFP_KERNEL);
    245	if (!isl12022)
    246		return -ENOMEM;
    247
    248	i2c_set_clientdata(client, isl12022);
    249
    250	isl12022->rtc = devm_rtc_device_register(&client->dev,
    251					isl12022_driver.driver.name,
    252					&isl12022_rtc_ops, THIS_MODULE);
    253	return PTR_ERR_OR_ZERO(isl12022->rtc);
    254}
    255
    256#ifdef CONFIG_OF
    257static const struct of_device_id isl12022_dt_match[] = {
    258	{ .compatible = "isl,isl12022" }, /* for backward compat., don't use */
    259	{ .compatible = "isil,isl12022" },
    260	{ },
    261};
    262MODULE_DEVICE_TABLE(of, isl12022_dt_match);
    263#endif
    264
    265static const struct i2c_device_id isl12022_id[] = {
    266	{ "isl12022", 0 },
    267	{ }
    268};
    269MODULE_DEVICE_TABLE(i2c, isl12022_id);
    270
    271static struct i2c_driver isl12022_driver = {
    272	.driver		= {
    273		.name	= "rtc-isl12022",
    274#ifdef CONFIG_OF
    275		.of_match_table = of_match_ptr(isl12022_dt_match),
    276#endif
    277	},
    278	.probe		= isl12022_probe,
    279	.id_table	= isl12022_id,
    280};
    281
    282module_i2c_driver(isl12022_driver);
    283
    284MODULE_AUTHOR("roman.fietze@telemotive.de");
    285MODULE_DESCRIPTION("ISL 12022 RTC driver");
    286MODULE_LICENSE("GPL");