18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
68c2ecf20Sopenharmony_ci * Copyright (C) 2006 Tower Technologies
78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Paul Mundt
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/rtc.h>
128c2ecf20Sopenharmony_ci#include <linux/bcd.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of_device.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Ricoh has a family of I2C based RTCs, which differ only slightly from
198c2ecf20Sopenharmony_ci * each other.  Differences center on pinout (e.g. how many interrupts,
208c2ecf20Sopenharmony_ci * output clock, etc) and how the control registers are used.  The '372
218c2ecf20Sopenharmony_ci * is significant only because that's the one this driver first supported.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci#define RS5C372_REG_SECS	0
248c2ecf20Sopenharmony_ci#define RS5C372_REG_MINS	1
258c2ecf20Sopenharmony_ci#define RS5C372_REG_HOURS	2
268c2ecf20Sopenharmony_ci#define RS5C372_REG_WDAY	3
278c2ecf20Sopenharmony_ci#define RS5C372_REG_DAY		4
288c2ecf20Sopenharmony_ci#define RS5C372_REG_MONTH	5
298c2ecf20Sopenharmony_ci#define RS5C372_REG_YEAR	6
308c2ecf20Sopenharmony_ci#define RS5C372_REG_TRIM	7
318c2ecf20Sopenharmony_ci#	define RS5C372_TRIM_XSL		0x80
328c2ecf20Sopenharmony_ci#	define RS5C372_TRIM_MASK	0x7F
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_A_MIN	8			/* or ALARM_W */
358c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_A_HOURS	9
368c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_A_WDAY	10
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_B_MIN	11			/* or ALARM_D */
398c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_B_HOURS	12
408c2ecf20Sopenharmony_ci#define RS5C_REG_ALARM_B_WDAY	13			/* (ALARM_B only) */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define RS5C_REG_CTRL1		14
438c2ecf20Sopenharmony_ci#	define RS5C_CTRL1_AALE		(1 << 7)	/* or WALE */
448c2ecf20Sopenharmony_ci#	define RS5C_CTRL1_BALE		(1 << 6)	/* or DALE */
458c2ecf20Sopenharmony_ci#	define RV5C387_CTRL1_24		(1 << 5)
468c2ecf20Sopenharmony_ci#	define RS5C372A_CTRL1_SL1	(1 << 5)
478c2ecf20Sopenharmony_ci#	define RS5C_CTRL1_CT_MASK	(7 << 0)
488c2ecf20Sopenharmony_ci#	define RS5C_CTRL1_CT0		(0 << 0)	/* no periodic irq */
498c2ecf20Sopenharmony_ci#	define RS5C_CTRL1_CT4		(4 << 0)	/* 1 Hz level irq */
508c2ecf20Sopenharmony_ci#define RS5C_REG_CTRL2		15
518c2ecf20Sopenharmony_ci#	define RS5C372_CTRL2_24		(1 << 5)
528c2ecf20Sopenharmony_ci#	define RS5C_CTRL2_XSTP		(1 << 4)	/* only if !R2x2x */
538c2ecf20Sopenharmony_ci#	define R2x2x_CTRL2_VDET		(1 << 6)	/* only if  R2x2x */
548c2ecf20Sopenharmony_ci#	define R2x2x_CTRL2_XSTP		(1 << 5)	/* only if  R2x2x */
558c2ecf20Sopenharmony_ci#	define R2x2x_CTRL2_PON		(1 << 4)	/* only if  R2x2x */
568c2ecf20Sopenharmony_ci#	define RS5C_CTRL2_CTFG		(1 << 2)
578c2ecf20Sopenharmony_ci#	define RS5C_CTRL2_AAFG		(1 << 1)	/* or WAFG */
588c2ecf20Sopenharmony_ci#	define RS5C_CTRL2_BAFG		(1 << 0)	/* or DAFG */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* to read (style 1) or write registers starting at R */
628c2ecf20Sopenharmony_ci#define RS5C_ADDR(R)		(((R) << 4) | 0)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cienum rtc_type {
668c2ecf20Sopenharmony_ci	rtc_undef = 0,
678c2ecf20Sopenharmony_ci	rtc_r2025sd,
688c2ecf20Sopenharmony_ci	rtc_r2221tl,
698c2ecf20Sopenharmony_ci	rtc_rs5c372a,
708c2ecf20Sopenharmony_ci	rtc_rs5c372b,
718c2ecf20Sopenharmony_ci	rtc_rv5c386,
728c2ecf20Sopenharmony_ci	rtc_rv5c387a,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct i2c_device_id rs5c372_id[] = {
768c2ecf20Sopenharmony_ci	{ "r2025sd", rtc_r2025sd },
778c2ecf20Sopenharmony_ci	{ "r2221tl", rtc_r2221tl },
788c2ecf20Sopenharmony_ci	{ "rs5c372a", rtc_rs5c372a },
798c2ecf20Sopenharmony_ci	{ "rs5c372b", rtc_rs5c372b },
808c2ecf20Sopenharmony_ci	{ "rv5c386", rtc_rv5c386 },
818c2ecf20Sopenharmony_ci	{ "rv5c387a", rtc_rv5c387a },
828c2ecf20Sopenharmony_ci	{ }
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, rs5c372_id);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic const struct of_device_id rs5c372_of_match[] = {
878c2ecf20Sopenharmony_ci	{
888c2ecf20Sopenharmony_ci		.compatible = "ricoh,r2025sd",
898c2ecf20Sopenharmony_ci		.data = (void *)rtc_r2025sd
908c2ecf20Sopenharmony_ci	},
918c2ecf20Sopenharmony_ci	{
928c2ecf20Sopenharmony_ci		.compatible = "ricoh,r2221tl",
938c2ecf20Sopenharmony_ci		.data = (void *)rtc_r2221tl
948c2ecf20Sopenharmony_ci	},
958c2ecf20Sopenharmony_ci	{
968c2ecf20Sopenharmony_ci		.compatible = "ricoh,rs5c372a",
978c2ecf20Sopenharmony_ci		.data = (void *)rtc_rs5c372a
988c2ecf20Sopenharmony_ci	},
998c2ecf20Sopenharmony_ci	{
1008c2ecf20Sopenharmony_ci		.compatible = "ricoh,rs5c372b",
1018c2ecf20Sopenharmony_ci		.data = (void *)rtc_rs5c372b
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	{
1048c2ecf20Sopenharmony_ci		.compatible = "ricoh,rv5c386",
1058c2ecf20Sopenharmony_ci		.data = (void *)rtc_rv5c386
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	{
1088c2ecf20Sopenharmony_ci		.compatible = "ricoh,rv5c387a",
1098c2ecf20Sopenharmony_ci		.data = (void *)rtc_rv5c387a
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci	{ }
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rs5c372_of_match);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* REVISIT:  this assumes that:
1168c2ecf20Sopenharmony_ci *  - we're in the 21st century, so it's safe to ignore the century
1178c2ecf20Sopenharmony_ci *    bit for rv5c38[67] (REG_MONTH bit 7);
1188c2ecf20Sopenharmony_ci *  - we should use ALARM_A not ALARM_B (may be wrong on some boards)
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_cistruct rs5c372 {
1218c2ecf20Sopenharmony_ci	struct i2c_client	*client;
1228c2ecf20Sopenharmony_ci	struct rtc_device	*rtc;
1238c2ecf20Sopenharmony_ci	enum rtc_type		type;
1248c2ecf20Sopenharmony_ci	unsigned		time24:1;
1258c2ecf20Sopenharmony_ci	unsigned		has_irq:1;
1268c2ecf20Sopenharmony_ci	unsigned		smbus:1;
1278c2ecf20Sopenharmony_ci	char			buf[17];
1288c2ecf20Sopenharmony_ci	char			*regs;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int rs5c_get_regs(struct rs5c372 *rs5c)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct i2c_client	*client = rs5c->client;
1348c2ecf20Sopenharmony_ci	struct i2c_msg		msgs[] = {
1358c2ecf20Sopenharmony_ci		{
1368c2ecf20Sopenharmony_ci			.addr = client->addr,
1378c2ecf20Sopenharmony_ci			.flags = I2C_M_RD,
1388c2ecf20Sopenharmony_ci			.len = sizeof(rs5c->buf),
1398c2ecf20Sopenharmony_ci			.buf = rs5c->buf
1408c2ecf20Sopenharmony_ci		},
1418c2ecf20Sopenharmony_ci	};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* This implements the third reading method from the datasheet, using
1448c2ecf20Sopenharmony_ci	 * an internal address that's reset after each transaction (by STOP)
1458c2ecf20Sopenharmony_ci	 * to 0x0f ... so we read extra registers, and skip the first one.
1468c2ecf20Sopenharmony_ci	 *
1478c2ecf20Sopenharmony_ci	 * The first method doesn't work with the iop3xx adapter driver, on at
1488c2ecf20Sopenharmony_ci	 * least 80219 chips; this works around that bug.
1498c2ecf20Sopenharmony_ci	 *
1508c2ecf20Sopenharmony_ci	 * The third method on the other hand doesn't work for the SMBus-only
1518c2ecf20Sopenharmony_ci	 * configurations, so we use the the first method there, stripping off
1528c2ecf20Sopenharmony_ci	 * the extra register in the process.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	if (rs5c->smbus) {
1558c2ecf20Sopenharmony_ci		int addr = RS5C_ADDR(RS5C372_REG_SECS);
1568c2ecf20Sopenharmony_ci		int size = sizeof(rs5c->buf) - 1;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		if (i2c_smbus_read_i2c_block_data(client, addr, size,
1598c2ecf20Sopenharmony_ci						  rs5c->buf + 1) != size) {
1608c2ecf20Sopenharmony_ci			dev_warn(&client->dev, "can't read registers\n");
1618c2ecf20Sopenharmony_ci			return -EIO;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	} else {
1648c2ecf20Sopenharmony_ci		if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
1658c2ecf20Sopenharmony_ci			dev_warn(&client->dev, "can't read registers\n");
1668c2ecf20Sopenharmony_ci			return -EIO;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
1718c2ecf20Sopenharmony_ci		"%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n",
1728c2ecf20Sopenharmony_ci		rs5c->regs + 0, rs5c->regs[3],
1738c2ecf20Sopenharmony_ci		rs5c->regs + 4, rs5c->regs[7],
1748c2ecf20Sopenharmony_ci		rs5c->regs + 8, rs5c->regs + 11,
1758c2ecf20Sopenharmony_ci		rs5c->regs[14], rs5c->regs[15]);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	unsigned	hour;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (rs5c->time24)
1858c2ecf20Sopenharmony_ci		return bcd2bin(reg & 0x3f);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	hour = bcd2bin(reg & 0x1f);
1888c2ecf20Sopenharmony_ci	if (hour == 12)
1898c2ecf20Sopenharmony_ci		hour = 0;
1908c2ecf20Sopenharmony_ci	if (reg & 0x20)
1918c2ecf20Sopenharmony_ci		hour += 12;
1928c2ecf20Sopenharmony_ci	return hour;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	if (rs5c->time24)
1988c2ecf20Sopenharmony_ci		return bin2bcd(hour);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (hour > 12)
2018c2ecf20Sopenharmony_ci		return 0x20 | bin2bcd(hour - 12);
2028c2ecf20Sopenharmony_ci	if (hour == 12)
2038c2ecf20Sopenharmony_ci		return 0x20 | bin2bcd(12);
2048c2ecf20Sopenharmony_ci	if (hour == 0)
2058c2ecf20Sopenharmony_ci		return bin2bcd(12);
2068c2ecf20Sopenharmony_ci	return bin2bcd(hour);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2128c2ecf20Sopenharmony_ci	struct rs5c372	*rs5c = i2c_get_clientdata(client);
2138c2ecf20Sopenharmony_ci	int		status = rs5c_get_regs(rs5c);
2148c2ecf20Sopenharmony_ci	unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2];
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (status < 0)
2178c2ecf20Sopenharmony_ci		return status;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	switch (rs5c->type) {
2208c2ecf20Sopenharmony_ci	case rtc_r2025sd:
2218c2ecf20Sopenharmony_ci	case rtc_r2221tl:
2228c2ecf20Sopenharmony_ci		if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
2238c2ecf20Sopenharmony_ci		    (rs5c->type == rtc_r2221tl &&  (ctrl2 & R2x2x_CTRL2_XSTP))) {
2248c2ecf20Sopenharmony_ci			dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
2258c2ecf20Sopenharmony_ci			return -EINVAL;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci		break;
2288c2ecf20Sopenharmony_ci	default:
2298c2ecf20Sopenharmony_ci		if (ctrl2 & RS5C_CTRL2_XSTP) {
2308c2ecf20Sopenharmony_ci			dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
2318c2ecf20Sopenharmony_ci			return -EINVAL;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
2368c2ecf20Sopenharmony_ci	tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
2378c2ecf20Sopenharmony_ci	tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	tm->tm_wday = bcd2bin(rs5c->regs[RS5C372_REG_WDAY] & 0x07);
2408c2ecf20Sopenharmony_ci	tm->tm_mday = bcd2bin(rs5c->regs[RS5C372_REG_DAY] & 0x3f);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* tm->tm_mon is zero-based */
2438c2ecf20Sopenharmony_ci	tm->tm_mon = bcd2bin(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* year is 1900 + tm->tm_year */
2468c2ecf20Sopenharmony_ci	tm->tm_year = bcd2bin(rs5c->regs[RS5C372_REG_YEAR]) + 100;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
2498c2ecf20Sopenharmony_ci		"mday=%d, mon=%d, year=%d, wday=%d\n",
2508c2ecf20Sopenharmony_ci		__func__,
2518c2ecf20Sopenharmony_ci		tm->tm_sec, tm->tm_min, tm->tm_hour,
2528c2ecf20Sopenharmony_ci		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2608c2ecf20Sopenharmony_ci	struct rs5c372	*rs5c = i2c_get_clientdata(client);
2618c2ecf20Sopenharmony_ci	unsigned char	buf[7];
2628c2ecf20Sopenharmony_ci	unsigned char	ctrl2;
2638c2ecf20Sopenharmony_ci	int		addr;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
2668c2ecf20Sopenharmony_ci		"mday=%d, mon=%d, year=%d, wday=%d\n",
2678c2ecf20Sopenharmony_ci		__func__,
2688c2ecf20Sopenharmony_ci		tm->tm_sec, tm->tm_min, tm->tm_hour,
2698c2ecf20Sopenharmony_ci		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	addr   = RS5C_ADDR(RS5C372_REG_SECS);
2728c2ecf20Sopenharmony_ci	buf[0] = bin2bcd(tm->tm_sec);
2738c2ecf20Sopenharmony_ci	buf[1] = bin2bcd(tm->tm_min);
2748c2ecf20Sopenharmony_ci	buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour);
2758c2ecf20Sopenharmony_ci	buf[3] = bin2bcd(tm->tm_wday);
2768c2ecf20Sopenharmony_ci	buf[4] = bin2bcd(tm->tm_mday);
2778c2ecf20Sopenharmony_ci	buf[5] = bin2bcd(tm->tm_mon + 1);
2788c2ecf20Sopenharmony_ci	buf[6] = bin2bcd(tm->tm_year - 100);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
2818c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: write error in line %i\n",
2828c2ecf20Sopenharmony_ci			__func__, __LINE__);
2838c2ecf20Sopenharmony_ci		return -EIO;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	addr = RS5C_ADDR(RS5C_REG_CTRL2);
2878c2ecf20Sopenharmony_ci	ctrl2 = i2c_smbus_read_byte_data(client, addr);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* clear rtc warning bits */
2908c2ecf20Sopenharmony_ci	switch (rs5c->type) {
2918c2ecf20Sopenharmony_ci	case rtc_r2025sd:
2928c2ecf20Sopenharmony_ci	case rtc_r2221tl:
2938c2ecf20Sopenharmony_ci		ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
2948c2ecf20Sopenharmony_ci		if (rs5c->type == rtc_r2025sd)
2958c2ecf20Sopenharmony_ci			ctrl2 |= R2x2x_CTRL2_XSTP;
2968c2ecf20Sopenharmony_ci		else
2978c2ecf20Sopenharmony_ci			ctrl2 &= ~R2x2x_CTRL2_XSTP;
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	default:
3008c2ecf20Sopenharmony_ci		ctrl2 &= ~RS5C_CTRL2_XSTP;
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) {
3058c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: write error in line %i\n",
3068c2ecf20Sopenharmony_ci			__func__, __LINE__);
3078c2ecf20Sopenharmony_ci		return -EIO;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_RTC_INTF_PROC)
3148c2ecf20Sopenharmony_ci#define	NEED_TRIM
3158c2ecf20Sopenharmony_ci#endif
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS)
3188c2ecf20Sopenharmony_ci#define	NEED_TRIM
3198c2ecf20Sopenharmony_ci#endif
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci#ifdef	NEED_TRIM
3228c2ecf20Sopenharmony_cistatic int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
3258c2ecf20Sopenharmony_ci	u8 tmp = rs5c372->regs[RS5C372_REG_TRIM];
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (osc)
3288c2ecf20Sopenharmony_ci		*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (trim) {
3318c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "%s: raw trim=%x\n", __func__, tmp);
3328c2ecf20Sopenharmony_ci		tmp &= RS5C372_TRIM_MASK;
3338c2ecf20Sopenharmony_ci		if (tmp & 0x3e) {
3348c2ecf20Sopenharmony_ci			int t = tmp & 0x3f;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci			if (tmp & 0x40)
3378c2ecf20Sopenharmony_ci				t = (~t | (s8)0xc0) + 1;
3388c2ecf20Sopenharmony_ci			else
3398c2ecf20Sopenharmony_ci				t = t - 1;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci			tmp = t * 2;
3428c2ecf20Sopenharmony_ci		} else
3438c2ecf20Sopenharmony_ci			tmp = 0;
3448c2ecf20Sopenharmony_ci		*trim = tmp;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci#endif
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int rs5c_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct i2c_client	*client = to_i2c_client(dev);
3548c2ecf20Sopenharmony_ci	struct rs5c372		*rs5c = i2c_get_clientdata(client);
3558c2ecf20Sopenharmony_ci	unsigned char		buf;
3568c2ecf20Sopenharmony_ci	int			status, addr;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	buf = rs5c->regs[RS5C_REG_CTRL1];
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (!rs5c->has_irq)
3618c2ecf20Sopenharmony_ci		return -EINVAL;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	status = rs5c_get_regs(rs5c);
3648c2ecf20Sopenharmony_ci	if (status < 0)
3658c2ecf20Sopenharmony_ci		return status;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	addr = RS5C_ADDR(RS5C_REG_CTRL1);
3688c2ecf20Sopenharmony_ci	if (enabled)
3698c2ecf20Sopenharmony_ci		buf |= RS5C_CTRL1_AALE;
3708c2ecf20Sopenharmony_ci	else
3718c2ecf20Sopenharmony_ci		buf &= ~RS5C_CTRL1_AALE;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, addr, buf) < 0) {
3748c2ecf20Sopenharmony_ci		dev_warn(dev, "can't update alarm\n");
3758c2ecf20Sopenharmony_ci		status = -EIO;
3768c2ecf20Sopenharmony_ci	} else
3778c2ecf20Sopenharmony_ci		rs5c->regs[RS5C_REG_CTRL1] = buf;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return status;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/* NOTE:  Since RTC_WKALM_{RD,SET} were originally defined for EFI,
3848c2ecf20Sopenharmony_ci * which only exposes a polled programming interface; and since
3858c2ecf20Sopenharmony_ci * these calls map directly to those EFI requests; we don't demand
3868c2ecf20Sopenharmony_ci * we have an IRQ for this chip when we go through this API.
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci * The older x86_pc derived RTC_ALM_{READ,SET} calls require irqs
3898c2ecf20Sopenharmony_ci * though, managed through RTC_AIE_{ON,OFF} requests.
3908c2ecf20Sopenharmony_ci */
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct i2c_client	*client = to_i2c_client(dev);
3958c2ecf20Sopenharmony_ci	struct rs5c372		*rs5c = i2c_get_clientdata(client);
3968c2ecf20Sopenharmony_ci	int			status;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	status = rs5c_get_regs(rs5c);
3998c2ecf20Sopenharmony_ci	if (status < 0)
4008c2ecf20Sopenharmony_ci		return status;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* report alarm time */
4038c2ecf20Sopenharmony_ci	t->time.tm_sec = 0;
4048c2ecf20Sopenharmony_ci	t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
4058c2ecf20Sopenharmony_ci	t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* ... and status */
4088c2ecf20Sopenharmony_ci	t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
4098c2ecf20Sopenharmony_ci	t->pending = !!(rs5c->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_AAFG);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct i2c_client	*client = to_i2c_client(dev);
4178c2ecf20Sopenharmony_ci	struct rs5c372		*rs5c = i2c_get_clientdata(client);
4188c2ecf20Sopenharmony_ci	int			status, addr, i;
4198c2ecf20Sopenharmony_ci	unsigned char		buf[3];
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* only handle up to 24 hours in the future, like RTC_ALM_SET */
4228c2ecf20Sopenharmony_ci	if (t->time.tm_mday != -1
4238c2ecf20Sopenharmony_ci			|| t->time.tm_mon != -1
4248c2ecf20Sopenharmony_ci			|| t->time.tm_year != -1)
4258c2ecf20Sopenharmony_ci		return -EINVAL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* REVISIT: round up tm_sec */
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* if needed, disable irq (clears pending status) */
4308c2ecf20Sopenharmony_ci	status = rs5c_get_regs(rs5c);
4318c2ecf20Sopenharmony_ci	if (status < 0)
4328c2ecf20Sopenharmony_ci		return status;
4338c2ecf20Sopenharmony_ci	if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) {
4348c2ecf20Sopenharmony_ci		addr = RS5C_ADDR(RS5C_REG_CTRL1);
4358c2ecf20Sopenharmony_ci		buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
4368c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) {
4378c2ecf20Sopenharmony_ci			dev_dbg(dev, "can't disable alarm\n");
4388c2ecf20Sopenharmony_ci			return -EIO;
4398c2ecf20Sopenharmony_ci		}
4408c2ecf20Sopenharmony_ci		rs5c->regs[RS5C_REG_CTRL1] = buf[0];
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* set alarm */
4448c2ecf20Sopenharmony_ci	buf[0] = bin2bcd(t->time.tm_min);
4458c2ecf20Sopenharmony_ci	buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
4468c2ecf20Sopenharmony_ci	buf[2] = 0x7f;	/* any/all days */
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(buf); i++) {
4498c2ecf20Sopenharmony_ci		addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
4508c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) {
4518c2ecf20Sopenharmony_ci			dev_dbg(dev, "can't set alarm time\n");
4528c2ecf20Sopenharmony_ci			return -EIO;
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* ... and maybe enable its irq */
4578c2ecf20Sopenharmony_ci	if (t->enabled) {
4588c2ecf20Sopenharmony_ci		addr = RS5C_ADDR(RS5C_REG_CTRL1);
4598c2ecf20Sopenharmony_ci		buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
4608c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0)
4618c2ecf20Sopenharmony_ci			dev_warn(dev, "can't enable alarm\n");
4628c2ecf20Sopenharmony_ci		rs5c->regs[RS5C_REG_CTRL1] = buf[0];
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_RTC_INTF_PROC)
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	int err, osc, trim;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim);
4758c2ecf20Sopenharmony_ci	if (err == 0) {
4768c2ecf20Sopenharmony_ci		seq_printf(seq, "crystal\t\t: %d.%03d KHz\n",
4778c2ecf20Sopenharmony_ci				osc / 1000, osc % 1000);
4788c2ecf20Sopenharmony_ci		seq_printf(seq, "trim\t\t: %d\n", trim);
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci#else
4858c2ecf20Sopenharmony_ci#define	rs5c372_rtc_proc	NULL
4868c2ecf20Sopenharmony_ci#endif
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic const struct rtc_class_ops rs5c372_rtc_ops = {
4898c2ecf20Sopenharmony_ci	.proc		= rs5c372_rtc_proc,
4908c2ecf20Sopenharmony_ci	.read_time	= rs5c372_rtc_read_time,
4918c2ecf20Sopenharmony_ci	.set_time	= rs5c372_rtc_set_time,
4928c2ecf20Sopenharmony_ci	.read_alarm	= rs5c_read_alarm,
4938c2ecf20Sopenharmony_ci	.set_alarm	= rs5c_set_alarm,
4948c2ecf20Sopenharmony_ci	.alarm_irq_enable = rs5c_rtc_alarm_irq_enable,
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS)
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic ssize_t rs5c372_sysfs_show_trim(struct device *dev,
5008c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	int err, trim;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim);
5058c2ecf20Sopenharmony_ci	if (err)
5068c2ecf20Sopenharmony_ci		return err;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", trim);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_cistatic DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic ssize_t rs5c372_sysfs_show_osc(struct device *dev,
5138c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	int err, osc;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL);
5188c2ecf20Sopenharmony_ci	if (err)
5198c2ecf20Sopenharmony_ci		return err;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_cistatic DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic int rs5c_sysfs_register(struct device *dev)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	int err;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	err = device_create_file(dev, &dev_attr_trim);
5308c2ecf20Sopenharmony_ci	if (err)
5318c2ecf20Sopenharmony_ci		return err;
5328c2ecf20Sopenharmony_ci	err = device_create_file(dev, &dev_attr_osc);
5338c2ecf20Sopenharmony_ci	if (err)
5348c2ecf20Sopenharmony_ci		device_remove_file(dev, &dev_attr_trim);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return err;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void rs5c_sysfs_unregister(struct device *dev)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_trim);
5428c2ecf20Sopenharmony_ci	device_remove_file(dev, &dev_attr_osc);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci#else
5468c2ecf20Sopenharmony_cistatic int rs5c_sysfs_register(struct device *dev)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	return 0;
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic void rs5c_sysfs_unregister(struct device *dev)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	/* nothing */
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci#endif	/* SYSFS */
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic struct i2c_driver rs5c372_driver;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	unsigned char buf[2];
5628c2ecf20Sopenharmony_ci	int addr, i, ret = 0;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	addr   = RS5C_ADDR(RS5C_REG_CTRL1);
5658c2ecf20Sopenharmony_ci	buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
5668c2ecf20Sopenharmony_ci	buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	switch (rs5c372->type) {
5698c2ecf20Sopenharmony_ci	case rtc_r2025sd:
5708c2ecf20Sopenharmony_ci		if (buf[1] & R2x2x_CTRL2_XSTP)
5718c2ecf20Sopenharmony_ci			return ret;
5728c2ecf20Sopenharmony_ci		break;
5738c2ecf20Sopenharmony_ci	case rtc_r2221tl:
5748c2ecf20Sopenharmony_ci		if (!(buf[1] & R2x2x_CTRL2_XSTP))
5758c2ecf20Sopenharmony_ci			return ret;
5768c2ecf20Sopenharmony_ci		break;
5778c2ecf20Sopenharmony_ci	default:
5788c2ecf20Sopenharmony_ci		if (!(buf[1] & RS5C_CTRL2_XSTP))
5798c2ecf20Sopenharmony_ci			return ret;
5808c2ecf20Sopenharmony_ci		break;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* use 24hr mode */
5848c2ecf20Sopenharmony_ci	switch (rs5c372->type) {
5858c2ecf20Sopenharmony_ci	case rtc_rs5c372a:
5868c2ecf20Sopenharmony_ci	case rtc_rs5c372b:
5878c2ecf20Sopenharmony_ci		buf[1] |= RS5C372_CTRL2_24;
5888c2ecf20Sopenharmony_ci		rs5c372->time24 = 1;
5898c2ecf20Sopenharmony_ci		break;
5908c2ecf20Sopenharmony_ci	case rtc_r2025sd:
5918c2ecf20Sopenharmony_ci	case rtc_r2221tl:
5928c2ecf20Sopenharmony_ci	case rtc_rv5c386:
5938c2ecf20Sopenharmony_ci	case rtc_rv5c387a:
5948c2ecf20Sopenharmony_ci		buf[0] |= RV5C387_CTRL1_24;
5958c2ecf20Sopenharmony_ci		rs5c372->time24 = 1;
5968c2ecf20Sopenharmony_ci		break;
5978c2ecf20Sopenharmony_ci	default:
5988c2ecf20Sopenharmony_ci		/* impossible */
5998c2ecf20Sopenharmony_ci		break;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(buf); i++) {
6038c2ecf20Sopenharmony_ci		addr = RS5C_ADDR(RS5C_REG_CTRL1 + i);
6048c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]);
6058c2ecf20Sopenharmony_ci		if (unlikely(ret < 0))
6068c2ecf20Sopenharmony_ci			return ret;
6078c2ecf20Sopenharmony_ci	}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	rs5c372->regs[RS5C_REG_CTRL1] = buf[0];
6108c2ecf20Sopenharmony_ci	rs5c372->regs[RS5C_REG_CTRL2] = buf[1];
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	return 0;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int rs5c372_probe(struct i2c_client *client,
6168c2ecf20Sopenharmony_ci			 const struct i2c_device_id *id)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	int err = 0;
6198c2ecf20Sopenharmony_ci	int smbus_mode = 0;
6208c2ecf20Sopenharmony_ci	struct rs5c372 *rs5c372;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "%s\n", __func__);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
6258c2ecf20Sopenharmony_ci			I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) {
6268c2ecf20Sopenharmony_ci		/*
6278c2ecf20Sopenharmony_ci		 * If we don't have any master mode adapter, try breaking
6288c2ecf20Sopenharmony_ci		 * it down in to the barest of capabilities.
6298c2ecf20Sopenharmony_ci		 */
6308c2ecf20Sopenharmony_ci		if (i2c_check_functionality(client->adapter,
6318c2ecf20Sopenharmony_ci				I2C_FUNC_SMBUS_BYTE_DATA |
6328c2ecf20Sopenharmony_ci				I2C_FUNC_SMBUS_I2C_BLOCK))
6338c2ecf20Sopenharmony_ci			smbus_mode = 1;
6348c2ecf20Sopenharmony_ci		else {
6358c2ecf20Sopenharmony_ci			/* Still no good, give up */
6368c2ecf20Sopenharmony_ci			err = -ENODEV;
6378c2ecf20Sopenharmony_ci			goto exit;
6388c2ecf20Sopenharmony_ci		}
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	rs5c372 = devm_kzalloc(&client->dev, sizeof(struct rs5c372),
6428c2ecf20Sopenharmony_ci				GFP_KERNEL);
6438c2ecf20Sopenharmony_ci	if (!rs5c372) {
6448c2ecf20Sopenharmony_ci		err = -ENOMEM;
6458c2ecf20Sopenharmony_ci		goto exit;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	rs5c372->client = client;
6498c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, rs5c372);
6508c2ecf20Sopenharmony_ci	if (client->dev.of_node)
6518c2ecf20Sopenharmony_ci		rs5c372->type = (enum rtc_type)
6528c2ecf20Sopenharmony_ci			of_device_get_match_data(&client->dev);
6538c2ecf20Sopenharmony_ci	else
6548c2ecf20Sopenharmony_ci		rs5c372->type = id->driver_data;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* we read registers 0x0f then 0x00-0x0f; skip the first one */
6578c2ecf20Sopenharmony_ci	rs5c372->regs = &rs5c372->buf[1];
6588c2ecf20Sopenharmony_ci	rs5c372->smbus = smbus_mode;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	err = rs5c_get_regs(rs5c372);
6618c2ecf20Sopenharmony_ci	if (err < 0)
6628c2ecf20Sopenharmony_ci		goto exit;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/* clock may be set for am/pm or 24 hr time */
6658c2ecf20Sopenharmony_ci	switch (rs5c372->type) {
6668c2ecf20Sopenharmony_ci	case rtc_rs5c372a:
6678c2ecf20Sopenharmony_ci	case rtc_rs5c372b:
6688c2ecf20Sopenharmony_ci		/* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b.
6698c2ecf20Sopenharmony_ci		 * so does periodic irq, except some 327a modes.
6708c2ecf20Sopenharmony_ci		 */
6718c2ecf20Sopenharmony_ci		if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24)
6728c2ecf20Sopenharmony_ci			rs5c372->time24 = 1;
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	case rtc_r2025sd:
6758c2ecf20Sopenharmony_ci	case rtc_r2221tl:
6768c2ecf20Sopenharmony_ci	case rtc_rv5c386:
6778c2ecf20Sopenharmony_ci	case rtc_rv5c387a:
6788c2ecf20Sopenharmony_ci		if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
6798c2ecf20Sopenharmony_ci			rs5c372->time24 = 1;
6808c2ecf20Sopenharmony_ci		/* alarm uses ALARM_W; and nINTRB for alarm and periodic
6818c2ecf20Sopenharmony_ci		 * irq, on both 386 and 387
6828c2ecf20Sopenharmony_ci		 */
6838c2ecf20Sopenharmony_ci		break;
6848c2ecf20Sopenharmony_ci	default:
6858c2ecf20Sopenharmony_ci		dev_err(&client->dev, "unknown RTC type\n");
6868c2ecf20Sopenharmony_ci		goto exit;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	/* if the oscillator lost power and no other software (like
6908c2ecf20Sopenharmony_ci	 * the bootloader) set it up, do it here.
6918c2ecf20Sopenharmony_ci	 *
6928c2ecf20Sopenharmony_ci	 * The R2025S/D does this a little differently than the other
6938c2ecf20Sopenharmony_ci	 * parts, so we special case that..
6948c2ecf20Sopenharmony_ci	 */
6958c2ecf20Sopenharmony_ci	err = rs5c_oscillator_setup(rs5c372);
6968c2ecf20Sopenharmony_ci	if (unlikely(err < 0)) {
6978c2ecf20Sopenharmony_ci		dev_err(&client->dev, "setup error\n");
6988c2ecf20Sopenharmony_ci		goto exit;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	dev_info(&client->dev, "%s found, %s\n",
7028c2ecf20Sopenharmony_ci			({ char *s; switch (rs5c372->type) {
7038c2ecf20Sopenharmony_ci			case rtc_r2025sd:	s = "r2025sd"; break;
7048c2ecf20Sopenharmony_ci			case rtc_r2221tl:	s = "r2221tl"; break;
7058c2ecf20Sopenharmony_ci			case rtc_rs5c372a:	s = "rs5c372a"; break;
7068c2ecf20Sopenharmony_ci			case rtc_rs5c372b:	s = "rs5c372b"; break;
7078c2ecf20Sopenharmony_ci			case rtc_rv5c386:	s = "rv5c386"; break;
7088c2ecf20Sopenharmony_ci			case rtc_rv5c387a:	s = "rv5c387a"; break;
7098c2ecf20Sopenharmony_ci			default:		s = "chip"; break;
7108c2ecf20Sopenharmony_ci			}; s;}),
7118c2ecf20Sopenharmony_ci			rs5c372->time24 ? "24hr" : "am/pm"
7128c2ecf20Sopenharmony_ci			);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* REVISIT use client->irq to register alarm irq ... */
7158c2ecf20Sopenharmony_ci	rs5c372->rtc = devm_rtc_device_register(&client->dev,
7168c2ecf20Sopenharmony_ci					rs5c372_driver.driver.name,
7178c2ecf20Sopenharmony_ci					&rs5c372_rtc_ops, THIS_MODULE);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (IS_ERR(rs5c372->rtc)) {
7208c2ecf20Sopenharmony_ci		err = PTR_ERR(rs5c372->rtc);
7218c2ecf20Sopenharmony_ci		goto exit;
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	err = rs5c_sysfs_register(&client->dev);
7258c2ecf20Sopenharmony_ci	if (err)
7268c2ecf20Sopenharmony_ci		goto exit;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return 0;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ciexit:
7318c2ecf20Sopenharmony_ci	return err;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int rs5c372_remove(struct i2c_client *client)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	rs5c_sysfs_unregister(&client->dev);
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic struct i2c_driver rs5c372_driver = {
7418c2ecf20Sopenharmony_ci	.driver		= {
7428c2ecf20Sopenharmony_ci		.name	= "rtc-rs5c372",
7438c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(rs5c372_of_match),
7448c2ecf20Sopenharmony_ci	},
7458c2ecf20Sopenharmony_ci	.probe		= rs5c372_probe,
7468c2ecf20Sopenharmony_ci	.remove		= rs5c372_remove,
7478c2ecf20Sopenharmony_ci	.id_table	= rs5c372_id,
7488c2ecf20Sopenharmony_ci};
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cimodule_i2c_driver(rs5c372_driver);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ciMODULE_AUTHOR(
7538c2ecf20Sopenharmony_ci		"Pavel Mironchik <pmironchik@optifacio.net>, "
7548c2ecf20Sopenharmony_ci		"Alessandro Zummo <a.zummo@towertech.it>, "
7558c2ecf20Sopenharmony_ci		"Paul Mundt <lethal@linux-sh.org>");
7568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
7578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
758