18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * An rtc/i2c driver for the Dallas DS1672
48c2ecf20Sopenharmony_ci * Copyright 2005-06 Tower Technologies
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Alessandro Zummo <a.zummo@towertech.it>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/i2c.h>
108c2ecf20Sopenharmony_ci#include <linux/rtc.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Registers */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define DS1672_REG_CNT_BASE	0
168c2ecf20Sopenharmony_ci#define DS1672_REG_CONTROL	4
178c2ecf20Sopenharmony_ci#define DS1672_REG_TRICKLE	5
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DS1672_REG_CONTROL_EOSC	0x80
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * In the routines that deal directly with the ds1672 hardware, we use
238c2ecf20Sopenharmony_ci * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
248c2ecf20Sopenharmony_ci * Time is set to UTC.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cistatic int ds1672_read_time(struct device *dev, struct rtc_time *tm)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
298c2ecf20Sopenharmony_ci	unsigned long time;
308c2ecf20Sopenharmony_ci	unsigned char addr = DS1672_REG_CONTROL;
318c2ecf20Sopenharmony_ci	unsigned char buf[4];
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	struct i2c_msg msgs[] = {
348c2ecf20Sopenharmony_ci		{/* setup read ptr */
358c2ecf20Sopenharmony_ci			.addr = client->addr,
368c2ecf20Sopenharmony_ci			.len = 1,
378c2ecf20Sopenharmony_ci			.buf = &addr
388c2ecf20Sopenharmony_ci		},
398c2ecf20Sopenharmony_ci		{/* read date */
408c2ecf20Sopenharmony_ci			.addr = client->addr,
418c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
428c2ecf20Sopenharmony_ci			.len = 1,
438c2ecf20Sopenharmony_ci			.buf = buf
448c2ecf20Sopenharmony_ci		},
458c2ecf20Sopenharmony_ci	};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* read control register */
488c2ecf20Sopenharmony_ci	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
498c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "Unable to read the control register\n");
508c2ecf20Sopenharmony_ci		return -EIO;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (buf[0] & DS1672_REG_CONTROL_EOSC) {
548c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "Oscillator not enabled. Set time to enable.\n");
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	addr = DS1672_REG_CNT_BASE;
598c2ecf20Sopenharmony_ci	msgs[1].len = 4;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* read date registers */
628c2ecf20Sopenharmony_ci	if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
638c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%s: read error\n", __func__);
648c2ecf20Sopenharmony_ci		return -EIO;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
688c2ecf20Sopenharmony_ci		"%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
698c2ecf20Sopenharmony_ci		__func__, buf[0], buf[1], buf[2], buf[3]);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
728c2ecf20Sopenharmony_ci	       (buf[1] << 8) | buf[0];
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	rtc_time64_to_tm(time, tm);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s: tm is %ptR\n", __func__, tm);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int ds1672_set_time(struct device *dev, struct rtc_time *tm)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
848c2ecf20Sopenharmony_ci	int xfer;
858c2ecf20Sopenharmony_ci	unsigned char buf[6];
868c2ecf20Sopenharmony_ci	unsigned long secs = rtc_tm_to_time64(tm);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	buf[0] = DS1672_REG_CNT_BASE;
898c2ecf20Sopenharmony_ci	buf[1] = secs & 0x000000FF;
908c2ecf20Sopenharmony_ci	buf[2] = (secs & 0x0000FF00) >> 8;
918c2ecf20Sopenharmony_ci	buf[3] = (secs & 0x00FF0000) >> 16;
928c2ecf20Sopenharmony_ci	buf[4] = (secs & 0xFF000000) >> 24;
938c2ecf20Sopenharmony_ci	buf[5] = 0;		/* set control reg to enable counting */
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	xfer = i2c_master_send(client, buf, 6);
968c2ecf20Sopenharmony_ci	if (xfer != 6) {
978c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%s: send: %d\n", __func__, xfer);
988c2ecf20Sopenharmony_ci		return -EIO;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const struct rtc_class_ops ds1672_rtc_ops = {
1058c2ecf20Sopenharmony_ci	.read_time = ds1672_read_time,
1068c2ecf20Sopenharmony_ci	.set_time = ds1672_set_time,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int ds1672_probe(struct i2c_client *client,
1108c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int err = 0;
1138c2ecf20Sopenharmony_ci	struct rtc_device *rtc;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s\n", __func__);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
1188c2ecf20Sopenharmony_ci		return -ENODEV;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	rtc = devm_rtc_allocate_device(&client->dev);
1218c2ecf20Sopenharmony_ci	if (IS_ERR(rtc))
1228c2ecf20Sopenharmony_ci		return PTR_ERR(rtc);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	rtc->ops = &ds1672_rtc_ops;
1258c2ecf20Sopenharmony_ci	rtc->range_max = U32_MAX;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	err = rtc_register_device(rtc);
1288c2ecf20Sopenharmony_ci	if (err)
1298c2ecf20Sopenharmony_ci		return err;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, rtc);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return 0;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const struct i2c_device_id ds1672_id[] = {
1378c2ecf20Sopenharmony_ci	{ "ds1672", 0 },
1388c2ecf20Sopenharmony_ci	{ }
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ds1672_id);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic const struct of_device_id ds1672_of_match[] = {
1438c2ecf20Sopenharmony_ci	{ .compatible = "dallas,ds1672" },
1448c2ecf20Sopenharmony_ci	{ }
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ds1672_of_match);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic struct i2c_driver ds1672_driver = {
1498c2ecf20Sopenharmony_ci	.driver = {
1508c2ecf20Sopenharmony_ci		   .name = "rtc-ds1672",
1518c2ecf20Sopenharmony_ci		   .of_match_table = of_match_ptr(ds1672_of_match),
1528c2ecf20Sopenharmony_ci	},
1538c2ecf20Sopenharmony_ci	.probe = &ds1672_probe,
1548c2ecf20Sopenharmony_ci	.id_table = ds1672_id,
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cimodule_i2c_driver(ds1672_driver);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
1608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
1618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
162