xref: /openbmc/linux/drivers/rtc/rtc-rx8010.c (revision 31b0cecb)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ed13d89bSAkshay Bhat /*
3ed13d89bSAkshay Bhat  * Driver for the Epson RTC module RX-8010 SJ
4ed13d89bSAkshay Bhat  *
5ed13d89bSAkshay Bhat  * Copyright(C) Timesys Corporation 2015
6ed13d89bSAkshay Bhat  * Copyright(C) General Electric Company 2015
7ed13d89bSAkshay Bhat  */
8ed13d89bSAkshay Bhat 
9ed13d89bSAkshay Bhat #include <linux/bcd.h>
10ed13d89bSAkshay Bhat #include <linux/bitops.h>
11ed13d89bSAkshay Bhat #include <linux/i2c.h>
12ed13d89bSAkshay Bhat #include <linux/kernel.h>
13ed13d89bSAkshay Bhat #include <linux/module.h>
149868bc1cSBartosz Golaszewski #include <linux/regmap.h>
15ed13d89bSAkshay Bhat #include <linux/rtc.h>
16ed13d89bSAkshay Bhat 
17ed13d89bSAkshay Bhat #define RX8010_SEC		0x10
18ed13d89bSAkshay Bhat #define RX8010_MIN		0x11
19ed13d89bSAkshay Bhat #define RX8010_HOUR		0x12
20ed13d89bSAkshay Bhat #define RX8010_WDAY		0x13
21ed13d89bSAkshay Bhat #define RX8010_MDAY		0x14
22ed13d89bSAkshay Bhat #define RX8010_MONTH		0x15
23ed13d89bSAkshay Bhat #define RX8010_YEAR		0x16
24ed13d89bSAkshay Bhat #define RX8010_RESV17		0x17
25ed13d89bSAkshay Bhat #define RX8010_ALMIN		0x18
26ed13d89bSAkshay Bhat #define RX8010_ALHOUR		0x19
27ed13d89bSAkshay Bhat #define RX8010_ALWDAY		0x1A
28ed13d89bSAkshay Bhat #define RX8010_TCOUNT0		0x1B
29ed13d89bSAkshay Bhat #define RX8010_TCOUNT1		0x1C
30ed13d89bSAkshay Bhat #define RX8010_EXT		0x1D
31ed13d89bSAkshay Bhat #define RX8010_FLAG		0x1E
32ed13d89bSAkshay Bhat #define RX8010_CTRL		0x1F
33ed13d89bSAkshay Bhat /* 0x20 to 0x2F are user registers */
34ed13d89bSAkshay Bhat #define RX8010_RESV30		0x30
35abf57f73SAkshay Bhat #define RX8010_RESV31		0x31
36ed13d89bSAkshay Bhat #define RX8010_IRQ		0x32
37ed13d89bSAkshay Bhat 
38ed13d89bSAkshay Bhat #define RX8010_EXT_WADA		BIT(3)
39ed13d89bSAkshay Bhat 
40ed13d89bSAkshay Bhat #define RX8010_FLAG_VLF		BIT(1)
41ed13d89bSAkshay Bhat #define RX8010_FLAG_AF		BIT(3)
42ed13d89bSAkshay Bhat #define RX8010_FLAG_TF		BIT(4)
43ed13d89bSAkshay Bhat #define RX8010_FLAG_UF		BIT(5)
44ed13d89bSAkshay Bhat 
45ed13d89bSAkshay Bhat #define RX8010_CTRL_AIE		BIT(3)
46ed13d89bSAkshay Bhat #define RX8010_CTRL_UIE		BIT(5)
47ed13d89bSAkshay Bhat #define RX8010_CTRL_STOP	BIT(6)
48ed13d89bSAkshay Bhat #define RX8010_CTRL_TEST	BIT(7)
49ed13d89bSAkshay Bhat 
50ed13d89bSAkshay Bhat #define RX8010_ALARM_AE		BIT(7)
51ed13d89bSAkshay Bhat 
52ed13d89bSAkshay Bhat static const struct i2c_device_id rx8010_id[] = {
53ed13d89bSAkshay Bhat 	{ "rx8010", 0 },
54ed13d89bSAkshay Bhat 	{ }
55ed13d89bSAkshay Bhat };
56ed13d89bSAkshay Bhat MODULE_DEVICE_TABLE(i2c, rx8010_id);
57ed13d89bSAkshay Bhat 
58782d3ebfSAlexandre Belloni static const __maybe_unused struct of_device_id rx8010_of_match[] = {
5981b779ceSJavier Martinez Canillas 	{ .compatible = "epson,rx8010" },
6081b779ceSJavier Martinez Canillas 	{ }
6181b779ceSJavier Martinez Canillas };
6281b779ceSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, rx8010_of_match);
6381b779ceSJavier Martinez Canillas 
64ed13d89bSAkshay Bhat struct rx8010_data {
659868bc1cSBartosz Golaszewski 	struct regmap *regs;
66ed13d89bSAkshay Bhat 	struct rtc_device *rtc;
67ed13d89bSAkshay Bhat 	u8 ctrlreg;
68ed13d89bSAkshay Bhat };
69ed13d89bSAkshay Bhat 
rx8010_irq_1_handler(int irq,void * dev_id)70ed13d89bSAkshay Bhat static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
71ed13d89bSAkshay Bhat {
72ed13d89bSAkshay Bhat 	struct i2c_client *client = dev_id;
73ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = i2c_get_clientdata(client);
749868bc1cSBartosz Golaszewski 	int flagreg, err;
75ed13d89bSAkshay Bhat 
762dbbedb9SAlexandre Belloni 	rtc_lock(rx8010->rtc);
77ed13d89bSAkshay Bhat 
789868bc1cSBartosz Golaszewski 	err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
799868bc1cSBartosz Golaszewski 	if (err) {
802dbbedb9SAlexandre Belloni 		rtc_unlock(rx8010->rtc);
81ed13d89bSAkshay Bhat 		return IRQ_NONE;
82ed13d89bSAkshay Bhat 	}
83ed13d89bSAkshay Bhat 
84ed13d89bSAkshay Bhat 	if (flagreg & RX8010_FLAG_VLF)
85ed13d89bSAkshay Bhat 		dev_warn(&client->dev, "Frequency stop detected\n");
86ed13d89bSAkshay Bhat 
87ed13d89bSAkshay Bhat 	if (flagreg & RX8010_FLAG_TF) {
88ed13d89bSAkshay Bhat 		flagreg &= ~RX8010_FLAG_TF;
89ed13d89bSAkshay Bhat 		rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF);
90ed13d89bSAkshay Bhat 	}
91ed13d89bSAkshay Bhat 
92ed13d89bSAkshay Bhat 	if (flagreg & RX8010_FLAG_AF) {
93ed13d89bSAkshay Bhat 		flagreg &= ~RX8010_FLAG_AF;
94ed13d89bSAkshay Bhat 		rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF);
95ed13d89bSAkshay Bhat 	}
96ed13d89bSAkshay Bhat 
97ed13d89bSAkshay Bhat 	if (flagreg & RX8010_FLAG_UF) {
98ed13d89bSAkshay Bhat 		flagreg &= ~RX8010_FLAG_UF;
99ed13d89bSAkshay Bhat 		rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);
100ed13d89bSAkshay Bhat 	}
101ed13d89bSAkshay Bhat 
1029868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg);
1032dbbedb9SAlexandre Belloni 	rtc_unlock(rx8010->rtc);
1049868bc1cSBartosz Golaszewski 	return err ? IRQ_NONE : IRQ_HANDLED;
105ed13d89bSAkshay Bhat }
106ed13d89bSAkshay Bhat 
rx8010_get_time(struct device * dev,struct rtc_time * dt)107ed13d89bSAkshay Bhat static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
108ed13d89bSAkshay Bhat {
109ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
110f702699cSBartosz Golaszewski 	u8 date[RX8010_YEAR - RX8010_SEC + 1];
11175677971SBartosz Golaszewski 	int flagreg, err;
112ed13d89bSAkshay Bhat 
1139868bc1cSBartosz Golaszewski 	err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
1149868bc1cSBartosz Golaszewski 	if (err)
1159868bc1cSBartosz Golaszewski 		return err;
116ed13d89bSAkshay Bhat 
117ed13d89bSAkshay Bhat 	if (flagreg & RX8010_FLAG_VLF) {
118ed13d89bSAkshay Bhat 		dev_warn(dev, "Frequency stop detected\n");
119ed13d89bSAkshay Bhat 		return -EINVAL;
120ed13d89bSAkshay Bhat 	}
121ed13d89bSAkshay Bhat 
1229868bc1cSBartosz Golaszewski 	err = regmap_bulk_read(rx8010->regs, RX8010_SEC, date, sizeof(date));
1239868bc1cSBartosz Golaszewski 	if (err)
1249868bc1cSBartosz Golaszewski 		return err;
125ed13d89bSAkshay Bhat 
126ed13d89bSAkshay Bhat 	dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);
127ed13d89bSAkshay Bhat 	dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);
128ed13d89bSAkshay Bhat 	dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f);
129ed13d89bSAkshay Bhat 	dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f);
130ed13d89bSAkshay Bhat 	dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1;
131ed13d89bSAkshay Bhat 	dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100;
132ed13d89bSAkshay Bhat 	dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f);
133ed13d89bSAkshay Bhat 
13422652ba7SAlexandre Belloni 	return 0;
135ed13d89bSAkshay Bhat }
136ed13d89bSAkshay Bhat 
rx8010_set_time(struct device * dev,struct rtc_time * dt)137ed13d89bSAkshay Bhat static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
138ed13d89bSAkshay Bhat {
139ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
140f702699cSBartosz Golaszewski 	u8 date[RX8010_YEAR - RX8010_SEC + 1];
1419868bc1cSBartosz Golaszewski 	int err;
142ed13d89bSAkshay Bhat 
143ed13d89bSAkshay Bhat 	/* set STOP bit before changing clock/calendar */
1449868bc1cSBartosz Golaszewski 	err = regmap_set_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
1459868bc1cSBartosz Golaszewski 	if (err)
14613952c9eSBartosz Golaszewski 		return err;
147ed13d89bSAkshay Bhat 
148ed13d89bSAkshay Bhat 	date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec);
149ed13d89bSAkshay Bhat 	date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min);
150ed13d89bSAkshay Bhat 	date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour);
151ed13d89bSAkshay Bhat 	date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday);
152ed13d89bSAkshay Bhat 	date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1);
153ed13d89bSAkshay Bhat 	date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100);
154ed13d89bSAkshay Bhat 	date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday);
155ed13d89bSAkshay Bhat 
1569868bc1cSBartosz Golaszewski 	err = regmap_bulk_write(rx8010->regs, RX8010_SEC, date, sizeof(date));
1579868bc1cSBartosz Golaszewski 	if (err)
15813952c9eSBartosz Golaszewski 		return err;
159ed13d89bSAkshay Bhat 
160ed13d89bSAkshay Bhat 	/* clear STOP bit after changing clock/calendar */
1619868bc1cSBartosz Golaszewski 	err = regmap_clear_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
1629868bc1cSBartosz Golaszewski 	if (err)
16313952c9eSBartosz Golaszewski 		return err;
164ed13d89bSAkshay Bhat 
1659868bc1cSBartosz Golaszewski 	err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_VLF);
1669868bc1cSBartosz Golaszewski 	if (err)
1679868bc1cSBartosz Golaszewski 		return err;
168ed13d89bSAkshay Bhat 
169ed13d89bSAkshay Bhat 	return 0;
170ed13d89bSAkshay Bhat }
171ed13d89bSAkshay Bhat 
rx8010_init(struct device * dev)172ba1bcafbSBartosz Golaszewski static int rx8010_init(struct device *dev)
173ed13d89bSAkshay Bhat {
1749868bc1cSBartosz Golaszewski 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
175ed13d89bSAkshay Bhat 	u8 ctrl[2];
176b3ff7fd6SBartosz Golaszewski 	int need_clear = 0, err;
177ed13d89bSAkshay Bhat 
178ed13d89bSAkshay Bhat 	/* Initialize reserved registers as specified in datasheet */
1799868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_RESV17, 0xD8);
1809868bc1cSBartosz Golaszewski 	if (err)
181ed13d89bSAkshay Bhat 		return err;
182ed13d89bSAkshay Bhat 
1839868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_RESV30, 0x00);
1849868bc1cSBartosz Golaszewski 	if (err)
185ed13d89bSAkshay Bhat 		return err;
186ed13d89bSAkshay Bhat 
1879868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_RESV31, 0x08);
1889868bc1cSBartosz Golaszewski 	if (err)
189ed13d89bSAkshay Bhat 		return err;
190ed13d89bSAkshay Bhat 
1919868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_IRQ, 0x00);
1929868bc1cSBartosz Golaszewski 	if (err)
193ed13d89bSAkshay Bhat 		return err;
194ed13d89bSAkshay Bhat 
1959868bc1cSBartosz Golaszewski 	err = regmap_bulk_read(rx8010->regs, RX8010_FLAG, ctrl, 2);
1969868bc1cSBartosz Golaszewski 	if (err)
1979868bc1cSBartosz Golaszewski 		return err;
198ed13d89bSAkshay Bhat 
199ed13d89bSAkshay Bhat 	if (ctrl[0] & RX8010_FLAG_VLF)
2009868bc1cSBartosz Golaszewski 		dev_warn(dev, "Frequency stop was detected\n");
201ed13d89bSAkshay Bhat 
202ed13d89bSAkshay Bhat 	if (ctrl[0] & RX8010_FLAG_AF) {
2039868bc1cSBartosz Golaszewski 		dev_warn(dev, "Alarm was detected\n");
204ed13d89bSAkshay Bhat 		need_clear = 1;
205ed13d89bSAkshay Bhat 	}
206ed13d89bSAkshay Bhat 
207ed13d89bSAkshay Bhat 	if (ctrl[0] & RX8010_FLAG_TF)
208ed13d89bSAkshay Bhat 		need_clear = 1;
209ed13d89bSAkshay Bhat 
210ed13d89bSAkshay Bhat 	if (ctrl[0] & RX8010_FLAG_UF)
211ed13d89bSAkshay Bhat 		need_clear = 1;
212ed13d89bSAkshay Bhat 
213ed13d89bSAkshay Bhat 	if (need_clear) {
214ed13d89bSAkshay Bhat 		ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);
2159868bc1cSBartosz Golaszewski 		err = regmap_write(rx8010->regs, RX8010_FLAG, ctrl[0]);
2169868bc1cSBartosz Golaszewski 		if (err)
217ed13d89bSAkshay Bhat 			return err;
218ed13d89bSAkshay Bhat 	}
219ed13d89bSAkshay Bhat 
220ed13d89bSAkshay Bhat 	rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST);
221ed13d89bSAkshay Bhat 
222804a6cfeSAkshay Bhat 	return 0;
223ed13d89bSAkshay Bhat }
224ed13d89bSAkshay Bhat 
rx8010_read_alarm(struct device * dev,struct rtc_wkalrm * t)225ed13d89bSAkshay Bhat static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
226ed13d89bSAkshay Bhat {
227ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
228ed13d89bSAkshay Bhat 	u8 alarmvals[3];
22975677971SBartosz Golaszewski 	int flagreg, err;
230ed13d89bSAkshay Bhat 
2319868bc1cSBartosz Golaszewski 	err = regmap_bulk_read(rx8010->regs, RX8010_ALMIN, alarmvals, 3);
2329868bc1cSBartosz Golaszewski 	if (err)
2339868bc1cSBartosz Golaszewski 		return err;
234ed13d89bSAkshay Bhat 
2359868bc1cSBartosz Golaszewski 	err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
2369868bc1cSBartosz Golaszewski 	if (err)
2379868bc1cSBartosz Golaszewski 		return err;
238ed13d89bSAkshay Bhat 
239ed13d89bSAkshay Bhat 	t->time.tm_sec = 0;
240ed13d89bSAkshay Bhat 	t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
241ed13d89bSAkshay Bhat 	t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
242ed13d89bSAkshay Bhat 
24356d86a7eSUwe Kleine-König 	if (!(alarmvals[2] & RX8010_ALARM_AE))
244ed13d89bSAkshay Bhat 		t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);
245ed13d89bSAkshay Bhat 
246ed13d89bSAkshay Bhat 	t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
247ed13d89bSAkshay Bhat 	t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;
248ed13d89bSAkshay Bhat 
249804a6cfeSAkshay Bhat 	return 0;
250ed13d89bSAkshay Bhat }
251ed13d89bSAkshay Bhat 
rx8010_set_alarm(struct device * dev,struct rtc_wkalrm * t)252ed13d89bSAkshay Bhat static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
253ed13d89bSAkshay Bhat {
254ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
255ed13d89bSAkshay Bhat 	u8 alarmvals[3];
2569868bc1cSBartosz Golaszewski 	int err;
257ed13d89bSAkshay Bhat 
258ed13d89bSAkshay Bhat 	if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {
259ed13d89bSAkshay Bhat 		rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
2609868bc1cSBartosz Golaszewski 		err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
2619868bc1cSBartosz Golaszewski 		if (err)
262ed13d89bSAkshay Bhat 			return err;
263ed13d89bSAkshay Bhat 	}
264ed13d89bSAkshay Bhat 
2659868bc1cSBartosz Golaszewski 	err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
2669868bc1cSBartosz Golaszewski 	if (err)
267ed13d89bSAkshay Bhat 		return err;
268ed13d89bSAkshay Bhat 
269ed13d89bSAkshay Bhat 	alarmvals[0] = bin2bcd(t->time.tm_min);
270ed13d89bSAkshay Bhat 	alarmvals[1] = bin2bcd(t->time.tm_hour);
271ed13d89bSAkshay Bhat 	alarmvals[2] = bin2bcd(t->time.tm_mday);
272ed13d89bSAkshay Bhat 
2739868bc1cSBartosz Golaszewski 	err = regmap_bulk_write(rx8010->regs, RX8010_ALMIN, alarmvals, 2);
2749868bc1cSBartosz Golaszewski 	if (err)
275ed13d89bSAkshay Bhat 		return err;
276ed13d89bSAkshay Bhat 
2779868bc1cSBartosz Golaszewski 	err = regmap_clear_bits(rx8010->regs, RX8010_EXT, RX8010_EXT_WADA);
2789868bc1cSBartosz Golaszewski 	if (err)
279ed13d89bSAkshay Bhat 		return err;
280ed13d89bSAkshay Bhat 
281ed13d89bSAkshay Bhat 	if (alarmvals[2] == 0)
282ed13d89bSAkshay Bhat 		alarmvals[2] |= RX8010_ALARM_AE;
283ed13d89bSAkshay Bhat 
2849868bc1cSBartosz Golaszewski 	err = regmap_write(rx8010->regs, RX8010_ALWDAY, alarmvals[2]);
2859868bc1cSBartosz Golaszewski 	if (err)
286ed13d89bSAkshay Bhat 		return err;
287ed13d89bSAkshay Bhat 
288ed13d89bSAkshay Bhat 	if (t->enabled) {
289ed13d89bSAkshay Bhat 		if (rx8010->rtc->uie_rtctimer.enabled)
290ed13d89bSAkshay Bhat 			rx8010->ctrlreg |= RX8010_CTRL_UIE;
291ed13d89bSAkshay Bhat 		if (rx8010->rtc->aie_timer.enabled)
292ed13d89bSAkshay Bhat 			rx8010->ctrlreg |=
293ed13d89bSAkshay Bhat 				(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
294ed13d89bSAkshay Bhat 
2959868bc1cSBartosz Golaszewski 		err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
2969868bc1cSBartosz Golaszewski 		if (err)
297ed13d89bSAkshay Bhat 			return err;
298ed13d89bSAkshay Bhat 	}
299ed13d89bSAkshay Bhat 
300ed13d89bSAkshay Bhat 	return 0;
301ed13d89bSAkshay Bhat }
302ed13d89bSAkshay Bhat 
rx8010_alarm_irq_enable(struct device * dev,unsigned int enabled)303ed13d89bSAkshay Bhat static int rx8010_alarm_irq_enable(struct device *dev,
304ed13d89bSAkshay Bhat 				   unsigned int enabled)
305ed13d89bSAkshay Bhat {
306ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
3079868bc1cSBartosz Golaszewski 	int err;
308ed13d89bSAkshay Bhat 	u8 ctrl;
309ed13d89bSAkshay Bhat 
310ed13d89bSAkshay Bhat 	ctrl = rx8010->ctrlreg;
311ed13d89bSAkshay Bhat 
312ed13d89bSAkshay Bhat 	if (enabled) {
313ed13d89bSAkshay Bhat 		if (rx8010->rtc->uie_rtctimer.enabled)
314ed13d89bSAkshay Bhat 			ctrl |= RX8010_CTRL_UIE;
315ed13d89bSAkshay Bhat 		if (rx8010->rtc->aie_timer.enabled)
316ed13d89bSAkshay Bhat 			ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
317ed13d89bSAkshay Bhat 	} else {
318ed13d89bSAkshay Bhat 		if (!rx8010->rtc->uie_rtctimer.enabled)
319ed13d89bSAkshay Bhat 			ctrl &= ~RX8010_CTRL_UIE;
320ed13d89bSAkshay Bhat 		if (!rx8010->rtc->aie_timer.enabled)
321ed13d89bSAkshay Bhat 			ctrl &= ~RX8010_CTRL_AIE;
322ed13d89bSAkshay Bhat 	}
323ed13d89bSAkshay Bhat 
3249868bc1cSBartosz Golaszewski 	err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
3259868bc1cSBartosz Golaszewski 	if (err)
326ed13d89bSAkshay Bhat 		return err;
327ed13d89bSAkshay Bhat 
328ed13d89bSAkshay Bhat 	if (ctrl != rx8010->ctrlreg) {
329ed13d89bSAkshay Bhat 		rx8010->ctrlreg = ctrl;
3309868bc1cSBartosz Golaszewski 		err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
3319868bc1cSBartosz Golaszewski 		if (err)
332ed13d89bSAkshay Bhat 			return err;
333ed13d89bSAkshay Bhat 	}
334ed13d89bSAkshay Bhat 
335ed13d89bSAkshay Bhat 	return 0;
336ed13d89bSAkshay Bhat }
337ed13d89bSAkshay Bhat 
rx8010_ioctl(struct device * dev,unsigned int cmd,unsigned long arg)338ed13d89bSAkshay Bhat static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
339ed13d89bSAkshay Bhat {
340ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010 = dev_get_drvdata(dev);
3419868bc1cSBartosz Golaszewski 	int tmp, flagreg, err;
342ed13d89bSAkshay Bhat 
343ed13d89bSAkshay Bhat 	switch (cmd) {
344ed13d89bSAkshay Bhat 	case RTC_VL_READ:
3459868bc1cSBartosz Golaszewski 		err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
3469868bc1cSBartosz Golaszewski 		if (err)
3479868bc1cSBartosz Golaszewski 			return err;
348ed13d89bSAkshay Bhat 
3498049c113SAlexandre Belloni 		tmp = flagreg & RX8010_FLAG_VLF ? RTC_VL_DATA_INVALID : 0;
3508049c113SAlexandre Belloni 		return put_user(tmp, (unsigned int __user *)arg);
351ed13d89bSAkshay Bhat 
352ed13d89bSAkshay Bhat 	default:
353ed13d89bSAkshay Bhat 		return -ENOIOCTLCMD;
354ed13d89bSAkshay Bhat 	}
355ed13d89bSAkshay Bhat }
356ed13d89bSAkshay Bhat 
357aaaea29cSAlexandre Belloni static const struct rtc_class_ops rx8010_rtc_ops = {
358d3b14296SBartosz Golaszewski 	.read_time = rx8010_get_time,
359d3b14296SBartosz Golaszewski 	.set_time = rx8010_set_time,
360d3b14296SBartosz Golaszewski 	.ioctl = rx8010_ioctl,
361d3b14296SBartosz Golaszewski 	.read_alarm = rx8010_read_alarm,
362d3b14296SBartosz Golaszewski 	.set_alarm = rx8010_set_alarm,
363d3b14296SBartosz Golaszewski 	.alarm_irq_enable = rx8010_alarm_irq_enable,
364d3b14296SBartosz Golaszewski };
365d3b14296SBartosz Golaszewski 
3669868bc1cSBartosz Golaszewski static const struct regmap_config rx8010_regmap_config = {
3679868bc1cSBartosz Golaszewski 	.name = "rx8010-rtc",
3689868bc1cSBartosz Golaszewski 	.reg_bits = 8,
3699868bc1cSBartosz Golaszewski 	.val_bits = 8,
3709868bc1cSBartosz Golaszewski };
3719868bc1cSBartosz Golaszewski 
rx8010_probe(struct i2c_client * client)372cee015d9SBartosz Golaszewski static int rx8010_probe(struct i2c_client *client)
373ed13d89bSAkshay Bhat {
374955a123cSBartosz Golaszewski 	struct device *dev = &client->dev;
375ed13d89bSAkshay Bhat 	struct rx8010_data *rx8010;
376ed13d89bSAkshay Bhat 	int err = 0;
377ed13d89bSAkshay Bhat 
378666f2141SBartosz Golaszewski 	rx8010 = devm_kzalloc(dev, sizeof(*rx8010), GFP_KERNEL);
379ed13d89bSAkshay Bhat 	if (!rx8010)
380ed13d89bSAkshay Bhat 		return -ENOMEM;
381ed13d89bSAkshay Bhat 
382ed13d89bSAkshay Bhat 	i2c_set_clientdata(client, rx8010);
383ed13d89bSAkshay Bhat 
3849868bc1cSBartosz Golaszewski 	rx8010->regs = devm_regmap_init_i2c(client, &rx8010_regmap_config);
3859868bc1cSBartosz Golaszewski 	if (IS_ERR(rx8010->regs))
3869868bc1cSBartosz Golaszewski 		return PTR_ERR(rx8010->regs);
3879868bc1cSBartosz Golaszewski 
388ba1bcafbSBartosz Golaszewski 	err = rx8010_init(dev);
389ed13d89bSAkshay Bhat 	if (err)
390ed13d89bSAkshay Bhat 		return err;
391ed13d89bSAkshay Bhat 
3920ce62778SBartosz Golaszewski 	rx8010->rtc = devm_rtc_allocate_device(dev);
3930ce62778SBartosz Golaszewski 	if (IS_ERR(rx8010->rtc))
3940ce62778SBartosz Golaszewski 		return PTR_ERR(rx8010->rtc);
3950ce62778SBartosz Golaszewski 
396ed13d89bSAkshay Bhat 	if (client->irq > 0) {
3974bbdced5SAlexandre Belloni 		unsigned long irqflags = IRQF_TRIGGER_LOW;
3984bbdced5SAlexandre Belloni 
3994bbdced5SAlexandre Belloni 		if (dev_fwnode(&client->dev))
4004bbdced5SAlexandre Belloni 			irqflags = 0;
4014bbdced5SAlexandre Belloni 
402955a123cSBartosz Golaszewski 		err = devm_request_threaded_irq(dev, client->irq, NULL,
403ed13d89bSAkshay Bhat 						rx8010_irq_1_handler,
4044bbdced5SAlexandre Belloni 						irqflags | IRQF_ONESHOT,
405ed13d89bSAkshay Bhat 						"rx8010", client);
406ed13d89bSAkshay Bhat 		if (err) {
407955a123cSBartosz Golaszewski 			dev_err(dev, "unable to request IRQ\n");
408d3b14296SBartosz Golaszewski 			return err;
409ed13d89bSAkshay Bhat 		}
410d3b14296SBartosz Golaszewski 	} else {
411aaaea29cSAlexandre Belloni 		clear_bit(RTC_FEATURE_ALARM, rx8010->rtc->features);
412ed13d89bSAkshay Bhat 	}
413ed13d89bSAkshay Bhat 
414aaaea29cSAlexandre Belloni 	rx8010->rtc->ops = &rx8010_rtc_ops;
415ed13d89bSAkshay Bhat 	rx8010->rtc->max_user_freq = 1;
4162fc1af30SBartosz Golaszewski 	rx8010->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
4172fc1af30SBartosz Golaszewski 	rx8010->rtc->range_max = RTC_TIMESTAMP_END_2099;
418ed13d89bSAkshay Bhat 
419fdcfd854SBartosz Golaszewski 	return devm_rtc_register_device(rx8010->rtc);
420ed13d89bSAkshay Bhat }
421ed13d89bSAkshay Bhat 
422ed13d89bSAkshay Bhat static struct i2c_driver rx8010_driver = {
423ed13d89bSAkshay Bhat 	.driver = {
424ed13d89bSAkshay Bhat 		.name = "rtc-rx8010",
42581b779ceSJavier Martinez Canillas 		.of_match_table = of_match_ptr(rx8010_of_match),
426ed13d89bSAkshay Bhat 	},
427*31b0cecbSUwe Kleine-König 	.probe		= rx8010_probe,
428ed13d89bSAkshay Bhat 	.id_table	= rx8010_id,
429ed13d89bSAkshay Bhat };
430ed13d89bSAkshay Bhat 
431ed13d89bSAkshay Bhat module_i2c_driver(rx8010_driver);
432ed13d89bSAkshay Bhat 
433ed13d89bSAkshay Bhat MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
434ed13d89bSAkshay Bhat MODULE_DESCRIPTION("Epson RX8010SJ RTC driver");
435ed13d89bSAkshay Bhat MODULE_LICENSE("GPL v2");
436