xref: /freebsd/sys/dev/iicbus/rtc/rx8803.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1d97d8385SMarcin Wojtas /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d97d8385SMarcin Wojtas  *
4d97d8385SMarcin Wojtas  * Copyright (c) 2020 Alstom Group.
5d97d8385SMarcin Wojtas  * Copyright (c) 2020 Semihalf.
6d97d8385SMarcin Wojtas  *
7d97d8385SMarcin Wojtas  * Redistribution and use in source and binary forms, with or without
8d97d8385SMarcin Wojtas  * modification, are permitted provided that the following conditions
9d97d8385SMarcin Wojtas  * are met:
10d97d8385SMarcin Wojtas  * 1. Redistributions of source code must retain the above copyright
11d97d8385SMarcin Wojtas  *    notice, this list of conditions and the following disclaimer.
12d97d8385SMarcin Wojtas  * 2. Redistributions in binary form must reproduce the above copyright
13d97d8385SMarcin Wojtas  *    notice, this list of conditions and the following disclaimer in the
14d97d8385SMarcin Wojtas  *    documentation and/or other materials provided with the distribution.
15d97d8385SMarcin Wojtas  *
16d97d8385SMarcin Wojtas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17d97d8385SMarcin Wojtas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d97d8385SMarcin Wojtas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d97d8385SMarcin Wojtas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20d97d8385SMarcin Wojtas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21d97d8385SMarcin Wojtas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22d97d8385SMarcin Wojtas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23d97d8385SMarcin Wojtas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24d97d8385SMarcin Wojtas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25d97d8385SMarcin Wojtas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26d97d8385SMarcin Wojtas  */
27d97d8385SMarcin Wojtas 
28d97d8385SMarcin Wojtas #include <sys/cdefs.h>
296f9c6226SKornel Dulęba #include "opt_platform.h"
306f9c6226SKornel Dulęba 
31d97d8385SMarcin Wojtas #include <sys/param.h>
32d97d8385SMarcin Wojtas #include <sys/systm.h>
33d97d8385SMarcin Wojtas #include <sys/bus.h>
34d97d8385SMarcin Wojtas #include <sys/clock.h>
35d97d8385SMarcin Wojtas #include <sys/kernel.h>
36d97d8385SMarcin Wojtas #include <sys/lock.h>
37d97d8385SMarcin Wojtas #include <sys/module.h>
38d97d8385SMarcin Wojtas 
39d97d8385SMarcin Wojtas #include <dev/ofw/ofw_bus.h>
40d97d8385SMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h>
41d97d8385SMarcin Wojtas 
42d97d8385SMarcin Wojtas #include <dev/iicbus/iiconf.h>
43d97d8385SMarcin Wojtas #include <dev/iicbus/iicbus.h>
44d97d8385SMarcin Wojtas 
45d97d8385SMarcin Wojtas #include "clock_if.h"
46d97d8385SMarcin Wojtas #include "iicbus_if.h"
47d97d8385SMarcin Wojtas 
48d97d8385SMarcin Wojtas #define	BIT(x)			(1 << (x))
49d97d8385SMarcin Wojtas 
50d97d8385SMarcin Wojtas #define RX8803_TIME		0x0
51d97d8385SMarcin Wojtas #define RX8803_FLAGS		0xE
52d97d8385SMarcin Wojtas #define RX8803_CTRL		0xF
53d97d8385SMarcin Wojtas 
54d97d8385SMarcin Wojtas #define RX8803_FLAGS_V1F	BIT(0)
55d97d8385SMarcin Wojtas #define RX8803_FLAGS_V2F	BIT(1)
56d97d8385SMarcin Wojtas 
57d97d8385SMarcin Wojtas #define RX8803_CTRL_DISABLE	BIT(0)
58d97d8385SMarcin Wojtas 
59d97d8385SMarcin Wojtas #define HALF_OF_SEC_NS		500000000
60d97d8385SMarcin Wojtas #define MAX_WRITE_LEN		16
61d97d8385SMarcin Wojtas 
62d97d8385SMarcin Wojtas struct rx8803_time {
63d97d8385SMarcin Wojtas 	uint8_t sec;
64d97d8385SMarcin Wojtas 	uint8_t min;
65d97d8385SMarcin Wojtas 	uint8_t hour;
66d97d8385SMarcin Wojtas 	uint8_t dow;
67d97d8385SMarcin Wojtas 	uint8_t day;
68d97d8385SMarcin Wojtas 	uint8_t mon;
69d97d8385SMarcin Wojtas 	uint8_t year;
70d97d8385SMarcin Wojtas };
71d97d8385SMarcin Wojtas 
726f9c6226SKornel Dulęba static struct ofw_compat_data compat_data[] = {
736f9c6226SKornel Dulęba 	{"epson,rx8803", 1},
746f9c6226SKornel Dulęba 	{NULL,           0},
756f9c6226SKornel Dulęba };
766f9c6226SKornel Dulęba 
77d97d8385SMarcin Wojtas static int rx8803_probe(device_t dev);
78d97d8385SMarcin Wojtas static int rx8803_attach(device_t dev);
79d97d8385SMarcin Wojtas static int rx8803_detach(device_t dev);
80d97d8385SMarcin Wojtas 
81d97d8385SMarcin Wojtas static int rx8803_gettime(device_t dev, struct timespec *ts);
82d97d8385SMarcin Wojtas static int rx8803_settime(device_t dev, struct timespec *ts);
83d97d8385SMarcin Wojtas 
84d97d8385SMarcin Wojtas static int rx8803_check_status(device_t dev);
85d97d8385SMarcin Wojtas 
86d97d8385SMarcin Wojtas static int
rx8803_check_status(device_t dev)87d97d8385SMarcin Wojtas rx8803_check_status(device_t dev)
88d97d8385SMarcin Wojtas {
89d97d8385SMarcin Wojtas 	uint8_t flags;
90d97d8385SMarcin Wojtas 	int rc;
91d97d8385SMarcin Wojtas 
92d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &flags, 1, IIC_WAIT);
93d97d8385SMarcin Wojtas 	if (rc != 0)
94d97d8385SMarcin Wojtas 		return (rc);
95d97d8385SMarcin Wojtas 
96d97d8385SMarcin Wojtas 	if (flags & RX8803_FLAGS_V2F) {
97d97d8385SMarcin Wojtas 		device_printf(dev, "Low voltage flag set, date is incorrect\n");
98d97d8385SMarcin Wojtas 		return (ENXIO);
99d97d8385SMarcin Wojtas 	}
100d97d8385SMarcin Wojtas 
101d97d8385SMarcin Wojtas 	return (0);
102d97d8385SMarcin Wojtas }
103d97d8385SMarcin Wojtas 
104d97d8385SMarcin Wojtas static int
rx8803_gettime(device_t dev,struct timespec * ts)105d97d8385SMarcin Wojtas rx8803_gettime(device_t dev, struct timespec *ts)
106d97d8385SMarcin Wojtas {
107d97d8385SMarcin Wojtas 	struct rx8803_time data;
108d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
109d97d8385SMarcin Wojtas 	int rc;
110d97d8385SMarcin Wojtas 
111d97d8385SMarcin Wojtas 	rc = rx8803_check_status(dev);
112d97d8385SMarcin Wojtas 	if (rc != 0)
113d97d8385SMarcin Wojtas 		return (rc);
114d97d8385SMarcin Wojtas 
115d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev,
116d97d8385SMarcin Wojtas 	    RX8803_TIME,
117d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
118d97d8385SMarcin Wojtas 	    IIC_WAIT);
119d97d8385SMarcin Wojtas 	if (rc != 0)
120d97d8385SMarcin Wojtas 		return (rc);
121d97d8385SMarcin Wojtas 
122d97d8385SMarcin Wojtas 	bcd.nsec = 0;
123d97d8385SMarcin Wojtas 	bcd.sec = data.sec & 0x7F;
124d97d8385SMarcin Wojtas 	bcd.min = data.min & 0x7F;
125d97d8385SMarcin Wojtas 	bcd.hour = data.hour & 0x3F;
126d97d8385SMarcin Wojtas 	bcd.dow = flsl(data.dow & 0x7F) - 1;
127d97d8385SMarcin Wojtas 	bcd.day = data.day & 0x3F;
128d97d8385SMarcin Wojtas 	bcd.mon = (data.mon & 0x1F);
129d97d8385SMarcin Wojtas 	bcd.year = data.year;
130d97d8385SMarcin Wojtas 
131d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
132d97d8385SMarcin Wojtas 
133d97d8385SMarcin Wojtas 	rc = clock_bcd_to_ts(&bcd, ts, false);
134d97d8385SMarcin Wojtas 	return (rc);
135d97d8385SMarcin Wojtas }
136d97d8385SMarcin Wojtas 
137d97d8385SMarcin Wojtas static int
rx8803_settime(device_t dev,struct timespec * ts)138d97d8385SMarcin Wojtas rx8803_settime(device_t dev, struct timespec *ts)
139d97d8385SMarcin Wojtas {
140d97d8385SMarcin Wojtas 	struct rx8803_time data;
141d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
142d97d8385SMarcin Wojtas 	uint8_t reg;
143d97d8385SMarcin Wojtas 	int rc;
144d97d8385SMarcin Wojtas 
145d97d8385SMarcin Wojtas 	ts->tv_sec -= utc_offset();
146d97d8385SMarcin Wojtas 	clock_ts_to_bcd(ts, &bcd, false);
147d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
148d97d8385SMarcin Wojtas 
149d97d8385SMarcin Wojtas 	data.sec = bcd.sec;
150d97d8385SMarcin Wojtas 	data.min = bcd.min;
151d97d8385SMarcin Wojtas 	data.hour = bcd.hour;
152d97d8385SMarcin Wojtas 	data.dow = 1 << bcd.dow;
153d97d8385SMarcin Wojtas 	data.day = bcd.day;
154d97d8385SMarcin Wojtas 	data.mon = bcd.mon;
155d97d8385SMarcin Wojtas 	data.year = bcd.year;
156d97d8385SMarcin Wojtas 
157d97d8385SMarcin Wojtas 	if (ts->tv_nsec > HALF_OF_SEC_NS)
158d97d8385SMarcin Wojtas 		data.sec++;
159d97d8385SMarcin Wojtas 
160d97d8385SMarcin Wojtas 	/* First disable clock. */
161d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
162d97d8385SMarcin Wojtas 	if (rc != 0)
163d97d8385SMarcin Wojtas 		return (rc);
164d97d8385SMarcin Wojtas 
165d97d8385SMarcin Wojtas 	reg |= RX8803_CTRL_DISABLE;
166d97d8385SMarcin Wojtas 
167d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
168d97d8385SMarcin Wojtas 	if (rc != 0)
169d97d8385SMarcin Wojtas 		return (rc);
170d97d8385SMarcin Wojtas 
171d97d8385SMarcin Wojtas 	/* Update the date. */
172d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev,
173d97d8385SMarcin Wojtas 	    RX8803_TIME,
174d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
175d97d8385SMarcin Wojtas 	    IIC_WAIT);
176d97d8385SMarcin Wojtas 	if (rc != 0)
177d97d8385SMarcin Wojtas 		return (rc);
178d97d8385SMarcin Wojtas 
179d97d8385SMarcin Wojtas 	/* Now restart it. */
180d97d8385SMarcin Wojtas 	reg &= ~RX8803_CTRL_DISABLE;
181d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
182d97d8385SMarcin Wojtas 	if (rc != 0)
183d97d8385SMarcin Wojtas 		return (rc);
184d97d8385SMarcin Wojtas 
185d97d8385SMarcin Wojtas 	/* Clear low voltage flags, as we have just updated the clock. */
186d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
187d97d8385SMarcin Wojtas 	if (rc != 0)
188d97d8385SMarcin Wojtas 		return (rc);
189d97d8385SMarcin Wojtas 
190d97d8385SMarcin Wojtas 	reg &= ~(RX8803_FLAGS_V1F | RX8803_FLAGS_V2F);
191d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
192d97d8385SMarcin Wojtas 	return (rc);
193d97d8385SMarcin Wojtas }
194d97d8385SMarcin Wojtas 
195d97d8385SMarcin Wojtas static int
rx8803_probe(device_t dev)196d97d8385SMarcin Wojtas rx8803_probe(device_t dev)
197d97d8385SMarcin Wojtas {
198d97d8385SMarcin Wojtas 
1996f9c6226SKornel Dulęba 	if (!ofw_bus_status_okay(dev))
2006f9c6226SKornel Dulęba 		return (ENXIO);
2016f9c6226SKornel Dulęba 
2026f9c6226SKornel Dulęba 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
203d97d8385SMarcin Wojtas 		return (ENXIO);
204d97d8385SMarcin Wojtas 
205d97d8385SMarcin Wojtas 	device_set_desc(dev, "Epson RX8803 Real Time Clock");
206d97d8385SMarcin Wojtas 
207d97d8385SMarcin Wojtas 	return (BUS_PROBE_GENERIC);
208d97d8385SMarcin Wojtas }
209d97d8385SMarcin Wojtas 
210d97d8385SMarcin Wojtas static int
rx8803_attach(device_t dev)211d97d8385SMarcin Wojtas rx8803_attach(device_t dev)
212d97d8385SMarcin Wojtas {
213d97d8385SMarcin Wojtas 
214d97d8385SMarcin Wojtas 	/* Set 1 sec resolution. */
215d97d8385SMarcin Wojtas 	clock_register_flags(dev, 1000000, 0);
216d97d8385SMarcin Wojtas 	clock_schedule(dev, 1);
217d97d8385SMarcin Wojtas 
218d97d8385SMarcin Wojtas 	return (0);
219d97d8385SMarcin Wojtas 
220d97d8385SMarcin Wojtas }
221d97d8385SMarcin Wojtas 
222d97d8385SMarcin Wojtas static int
rx8803_detach(device_t dev)223d97d8385SMarcin Wojtas rx8803_detach(device_t dev)
224d97d8385SMarcin Wojtas {
225d97d8385SMarcin Wojtas 
226d97d8385SMarcin Wojtas 	clock_unregister(dev);
227d97d8385SMarcin Wojtas 
228d97d8385SMarcin Wojtas 	return (0);
229d97d8385SMarcin Wojtas }
230d97d8385SMarcin Wojtas 
231d97d8385SMarcin Wojtas static device_method_t rx8803_methods[] = {
232d97d8385SMarcin Wojtas 	DEVMETHOD(device_probe, rx8803_probe),
233d97d8385SMarcin Wojtas 	DEVMETHOD(device_attach, rx8803_attach),
234d97d8385SMarcin Wojtas 	DEVMETHOD(device_detach, rx8803_detach),
235d97d8385SMarcin Wojtas 
236d97d8385SMarcin Wojtas 	DEVMETHOD(clock_gettime, rx8803_gettime),
237d97d8385SMarcin Wojtas 	DEVMETHOD(clock_settime, rx8803_settime),
238d97d8385SMarcin Wojtas 
239d97d8385SMarcin Wojtas 	DEVMETHOD_END,
240d97d8385SMarcin Wojtas };
241d97d8385SMarcin Wojtas 
242d97d8385SMarcin Wojtas static driver_t rx8803_driver = {
243d97d8385SMarcin Wojtas 	"rx8803",
244d97d8385SMarcin Wojtas 	rx8803_methods,
245d97d8385SMarcin Wojtas 	0,			/* We don't need softc for this one. */
246d97d8385SMarcin Wojtas };
247d97d8385SMarcin Wojtas 
2483a866152SJohn Baldwin DRIVER_MODULE(rx8803, iicbus, rx8803_driver, NULL, NULL);
2496f9c6226SKornel Dulęba MODULE_VERSION(rx8803, 1);
250d97d8385SMarcin Wojtas MODULE_DEPEND(rx8803, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
2516f9c6226SKornel Dulęba IICBUS_FDT_PNP_INFO(compat_data);
252