18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Dallas DS1302 RTC Support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2002 David McCullough
68c2ecf20Sopenharmony_ci *  Copyright (C) 2003 - 2007 Paul Mundt
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/bcd.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/rtc.h>
168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define	RTC_CMD_READ	0x81		/* Read command */
198c2ecf20Sopenharmony_ci#define	RTC_CMD_WRITE	0x80		/* Write command */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define	RTC_CMD_WRITE_ENABLE	0x00		/* Write enable */
228c2ecf20Sopenharmony_ci#define	RTC_CMD_WRITE_DISABLE	0x80		/* Write disable */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define RTC_ADDR_RAM0	0x20		/* Address of RAM0 */
258c2ecf20Sopenharmony_ci#define RTC_ADDR_TCR	0x08		/* Address of trickle charge register */
268c2ecf20Sopenharmony_ci#define RTC_CLCK_BURST	0x1F		/* Address of clock burst */
278c2ecf20Sopenharmony_ci#define	RTC_CLCK_LEN	0x08		/* Size of clock burst */
288c2ecf20Sopenharmony_ci#define	RTC_ADDR_CTRL	0x07		/* Address of control register */
298c2ecf20Sopenharmony_ci#define	RTC_ADDR_YEAR	0x06		/* Address of year register */
308c2ecf20Sopenharmony_ci#define	RTC_ADDR_DAY	0x05		/* Address of day of week register */
318c2ecf20Sopenharmony_ci#define	RTC_ADDR_MON	0x04		/* Address of month register */
328c2ecf20Sopenharmony_ci#define	RTC_ADDR_DATE	0x03		/* Address of day of month register */
338c2ecf20Sopenharmony_ci#define	RTC_ADDR_HOUR	0x02		/* Address of hour register */
348c2ecf20Sopenharmony_ci#define	RTC_ADDR_MIN	0x01		/* Address of minute register */
358c2ecf20Sopenharmony_ci#define	RTC_ADDR_SEC	0x00		/* Address of second register */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct spi_device	*spi = dev_get_drvdata(dev);
408c2ecf20Sopenharmony_ci	u8		buf[1 + RTC_CLCK_LEN];
418c2ecf20Sopenharmony_ci	u8		*bp;
428c2ecf20Sopenharmony_ci	int		status;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Enable writing */
458c2ecf20Sopenharmony_ci	bp = buf;
468c2ecf20Sopenharmony_ci	*bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
478c2ecf20Sopenharmony_ci	*bp++ = RTC_CMD_WRITE_ENABLE;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	status = spi_write_then_read(spi, buf, 2,
508c2ecf20Sopenharmony_ci			NULL, 0);
518c2ecf20Sopenharmony_ci	if (status)
528c2ecf20Sopenharmony_ci		return status;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* Write registers starting at the first time/date address. */
558c2ecf20Sopenharmony_ci	bp = buf;
568c2ecf20Sopenharmony_ci	*bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_sec);
598c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_min);
608c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_hour);
618c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_mday);
628c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_mon + 1);
638c2ecf20Sopenharmony_ci	*bp++ = time->tm_wday + 1;
648c2ecf20Sopenharmony_ci	*bp++ = bin2bcd(time->tm_year % 100);
658c2ecf20Sopenharmony_ci	*bp++ = RTC_CMD_WRITE_DISABLE;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* use write-then-read since dma from stack is nonportable */
688c2ecf20Sopenharmony_ci	return spi_write_then_read(spi, buf, sizeof(buf),
698c2ecf20Sopenharmony_ci			NULL, 0);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct spi_device	*spi = dev_get_drvdata(dev);
758c2ecf20Sopenharmony_ci	u8		addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ;
768c2ecf20Sopenharmony_ci	u8		buf[RTC_CLCK_LEN - 1];
778c2ecf20Sopenharmony_ci	int		status;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Use write-then-read to get all the date/time registers
808c2ecf20Sopenharmony_ci	 * since dma from stack is nonportable
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	status = spi_write_then_read(spi, &addr, sizeof(addr),
838c2ecf20Sopenharmony_ci			buf, sizeof(buf));
848c2ecf20Sopenharmony_ci	if (status < 0)
858c2ecf20Sopenharmony_ci		return status;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/* Decode the registers */
888c2ecf20Sopenharmony_ci	time->tm_sec = bcd2bin(buf[RTC_ADDR_SEC]);
898c2ecf20Sopenharmony_ci	time->tm_min = bcd2bin(buf[RTC_ADDR_MIN]);
908c2ecf20Sopenharmony_ci	time->tm_hour = bcd2bin(buf[RTC_ADDR_HOUR]);
918c2ecf20Sopenharmony_ci	time->tm_wday = buf[RTC_ADDR_DAY] - 1;
928c2ecf20Sopenharmony_ci	time->tm_mday = bcd2bin(buf[RTC_ADDR_DATE]);
938c2ecf20Sopenharmony_ci	time->tm_mon = bcd2bin(buf[RTC_ADDR_MON]) - 1;
948c2ecf20Sopenharmony_ci	time->tm_year = bcd2bin(buf[RTC_ADDR_YEAR]) + 100;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const struct rtc_class_ops ds1302_rtc_ops = {
1008c2ecf20Sopenharmony_ci	.read_time	= ds1302_rtc_get_time,
1018c2ecf20Sopenharmony_ci	.set_time	= ds1302_rtc_set_time,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int ds1302_probe(struct spi_device *spi)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct rtc_device	*rtc;
1078c2ecf20Sopenharmony_ci	u8		addr;
1088c2ecf20Sopenharmony_ci	u8		buf[4];
1098c2ecf20Sopenharmony_ci	u8		*bp;
1108c2ecf20Sopenharmony_ci	int		status;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Sanity check board setup data.  This may be hooked up
1138c2ecf20Sopenharmony_ci	 * in 3wire mode, but we don't care.  Note that unless
1148c2ecf20Sopenharmony_ci	 * there's an inverter in place, this needs SPI_CS_HIGH!
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	if (spi->bits_per_word && (spi->bits_per_word != 8)) {
1178c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "bad word length\n");
1188c2ecf20Sopenharmony_ci		return -EINVAL;
1198c2ecf20Sopenharmony_ci	} else if (spi->max_speed_hz > 2000000) {
1208c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "speed is too high\n");
1218c2ecf20Sopenharmony_ci		return -EINVAL;
1228c2ecf20Sopenharmony_ci	} else if (spi->mode & SPI_CPHA) {
1238c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "bad mode\n");
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
1288c2ecf20Sopenharmony_ci	status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
1298c2ecf20Sopenharmony_ci	if (status < 0) {
1308c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "control register read error %d\n",
1318c2ecf20Sopenharmony_ci				status);
1328c2ecf20Sopenharmony_ci		return status;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) {
1368c2ecf20Sopenharmony_ci		status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
1378c2ecf20Sopenharmony_ci		if (status < 0) {
1388c2ecf20Sopenharmony_ci			dev_err(&spi->dev, "control register read error %d\n",
1398c2ecf20Sopenharmony_ci					status);
1408c2ecf20Sopenharmony_ci			return status;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) {
1448c2ecf20Sopenharmony_ci			dev_err(&spi->dev, "junk in control register\n");
1458c2ecf20Sopenharmony_ci			return -ENODEV;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	if (buf[0] == 0) {
1498c2ecf20Sopenharmony_ci		bp = buf;
1508c2ecf20Sopenharmony_ci		*bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE;
1518c2ecf20Sopenharmony_ci		*bp++ = RTC_CMD_WRITE_DISABLE;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		status = spi_write_then_read(spi, buf, 2, NULL, 0);
1548c2ecf20Sopenharmony_ci		if (status < 0) {
1558c2ecf20Sopenharmony_ci			dev_err(&spi->dev, "control register write error %d\n",
1568c2ecf20Sopenharmony_ci					status);
1578c2ecf20Sopenharmony_ci			return status;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ;
1618c2ecf20Sopenharmony_ci		status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1);
1628c2ecf20Sopenharmony_ci		if (status < 0) {
1638c2ecf20Sopenharmony_ci			dev_err(&spi->dev,
1648c2ecf20Sopenharmony_ci					"error %d reading control register\n",
1658c2ecf20Sopenharmony_ci					status);
1668c2ecf20Sopenharmony_ci			return status;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		if (buf[0] != RTC_CMD_WRITE_DISABLE) {
1708c2ecf20Sopenharmony_ci			dev_err(&spi->dev, "failed to detect chip\n");
1718c2ecf20Sopenharmony_ci			return -ENODEV;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, spi);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	rtc = devm_rtc_device_register(&spi->dev, "ds1302",
1788c2ecf20Sopenharmony_ci			&ds1302_rtc_ops, THIS_MODULE);
1798c2ecf20Sopenharmony_ci	if (IS_ERR(rtc)) {
1808c2ecf20Sopenharmony_ci		status = PTR_ERR(rtc);
1818c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "error %d registering rtc\n", status);
1828c2ecf20Sopenharmony_ci		return status;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int ds1302_remove(struct spi_device *spi)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, NULL);
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
1958c2ecf20Sopenharmony_cistatic const struct of_device_id ds1302_dt_ids[] = {
1968c2ecf20Sopenharmony_ci	{ .compatible = "maxim,ds1302", },
1978c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ds1302_dt_ids);
2008c2ecf20Sopenharmony_ci#endif
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic struct spi_driver ds1302_driver = {
2038c2ecf20Sopenharmony_ci	.driver.name	= "rtc-ds1302",
2048c2ecf20Sopenharmony_ci	.driver.of_match_table = of_match_ptr(ds1302_dt_ids),
2058c2ecf20Sopenharmony_ci	.probe		= ds1302_probe,
2068c2ecf20Sopenharmony_ci	.remove		= ds1302_remove,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cimodule_spi_driver(ds1302_driver);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Dallas DS1302 RTC driver");
2128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mundt, David McCullough");
2138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
214