xref: /freebsd/sys/dev/iicbus/rtc/rx8803.c (revision 3a86615217bfa70b2bbaeb85e36026da33a11059)
1d97d8385SMarcin Wojtas /*-
2d97d8385SMarcin Wojtas  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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>
29d97d8385SMarcin Wojtas __FBSDID("$FreeBSD$");
30d97d8385SMarcin Wojtas 
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 
72d97d8385SMarcin Wojtas static int rx8803_probe(device_t dev);
73d97d8385SMarcin Wojtas static int rx8803_attach(device_t dev);
74d97d8385SMarcin Wojtas static int rx8803_detach(device_t dev);
75d97d8385SMarcin Wojtas 
76d97d8385SMarcin Wojtas static int rx8803_gettime(device_t dev, struct timespec *ts);
77d97d8385SMarcin Wojtas static int rx8803_settime(device_t dev, struct timespec *ts);
78d97d8385SMarcin Wojtas 
79d97d8385SMarcin Wojtas static int rx8803_check_status(device_t dev);
80d97d8385SMarcin Wojtas 
81d97d8385SMarcin Wojtas static int
82d97d8385SMarcin Wojtas rx8803_check_status(device_t dev)
83d97d8385SMarcin Wojtas {
84d97d8385SMarcin Wojtas 	uint8_t flags;
85d97d8385SMarcin Wojtas 	int rc;
86d97d8385SMarcin Wojtas 
87d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &flags, 1, IIC_WAIT);
88d97d8385SMarcin Wojtas 	if (rc != 0)
89d97d8385SMarcin Wojtas 		return (rc);
90d97d8385SMarcin Wojtas 
91d97d8385SMarcin Wojtas 	if (flags & RX8803_FLAGS_V2F) {
92d97d8385SMarcin Wojtas 		device_printf(dev, "Low voltage flag set, date is incorrect\n");
93d97d8385SMarcin Wojtas 		return (ENXIO);
94d97d8385SMarcin Wojtas 	}
95d97d8385SMarcin Wojtas 
96d97d8385SMarcin Wojtas 	return (0);
97d97d8385SMarcin Wojtas }
98d97d8385SMarcin Wojtas 
99d97d8385SMarcin Wojtas static int
100d97d8385SMarcin Wojtas rx8803_gettime(device_t dev, struct timespec *ts)
101d97d8385SMarcin Wojtas {
102d97d8385SMarcin Wojtas 	struct rx8803_time data;
103d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
104d97d8385SMarcin Wojtas 	int rc;
105d97d8385SMarcin Wojtas 
106d97d8385SMarcin Wojtas 	rc = rx8803_check_status(dev);
107d97d8385SMarcin Wojtas 	if (rc != 0)
108d97d8385SMarcin Wojtas 		return (rc);
109d97d8385SMarcin Wojtas 
110d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev,
111d97d8385SMarcin Wojtas 	    RX8803_TIME,
112d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
113d97d8385SMarcin Wojtas 	    IIC_WAIT);
114d97d8385SMarcin Wojtas 	if (rc != 0)
115d97d8385SMarcin Wojtas 		return (rc);
116d97d8385SMarcin Wojtas 
117d97d8385SMarcin Wojtas 	bcd.nsec = 0;
118d97d8385SMarcin Wojtas 	bcd.sec = data.sec & 0x7F;
119d97d8385SMarcin Wojtas 	bcd.min = data.min & 0x7F;
120d97d8385SMarcin Wojtas 	bcd.hour = data.hour & 0x3F;
121d97d8385SMarcin Wojtas 	bcd.dow = flsl(data.dow & 0x7F) - 1;
122d97d8385SMarcin Wojtas 	bcd.day = data.day & 0x3F;
123d97d8385SMarcin Wojtas 	bcd.mon = (data.mon & 0x1F);
124d97d8385SMarcin Wojtas 	bcd.year = data.year;
125d97d8385SMarcin Wojtas 
126d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
127d97d8385SMarcin Wojtas 
128d97d8385SMarcin Wojtas 	rc = clock_bcd_to_ts(&bcd, ts, false);
129d97d8385SMarcin Wojtas 	return (rc);
130d97d8385SMarcin Wojtas }
131d97d8385SMarcin Wojtas 
132d97d8385SMarcin Wojtas static int
133d97d8385SMarcin Wojtas rx8803_settime(device_t dev, struct timespec *ts)
134d97d8385SMarcin Wojtas {
135d97d8385SMarcin Wojtas 	struct rx8803_time data;
136d97d8385SMarcin Wojtas 	struct bcd_clocktime bcd;
137d97d8385SMarcin Wojtas 	uint8_t reg;
138d97d8385SMarcin Wojtas 	int rc;
139d97d8385SMarcin Wojtas 
140d97d8385SMarcin Wojtas 	ts->tv_sec -= utc_offset();
141d97d8385SMarcin Wojtas 	clock_ts_to_bcd(ts, &bcd, false);
142d97d8385SMarcin Wojtas 	clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
143d97d8385SMarcin Wojtas 
144d97d8385SMarcin Wojtas 	data.sec = bcd.sec;
145d97d8385SMarcin Wojtas 	data.min = bcd.min;
146d97d8385SMarcin Wojtas 	data.hour = bcd.hour;
147d97d8385SMarcin Wojtas 	data.dow = 1 << bcd.dow;
148d97d8385SMarcin Wojtas 	data.day = bcd.day;
149d97d8385SMarcin Wojtas 	data.mon = bcd.mon;
150d97d8385SMarcin Wojtas 	data.year = bcd.year;
151d97d8385SMarcin Wojtas 
152d97d8385SMarcin Wojtas 	if (ts->tv_nsec > HALF_OF_SEC_NS)
153d97d8385SMarcin Wojtas 		data.sec++;
154d97d8385SMarcin Wojtas 
155d97d8385SMarcin Wojtas 	/* First disable clock. */
156d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
157d97d8385SMarcin Wojtas 	if (rc != 0)
158d97d8385SMarcin Wojtas 		return (rc);
159d97d8385SMarcin Wojtas 
160d97d8385SMarcin Wojtas 	reg |= RX8803_CTRL_DISABLE;
161d97d8385SMarcin Wojtas 
162d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
163d97d8385SMarcin Wojtas 	if (rc != 0)
164d97d8385SMarcin Wojtas 		return (rc);
165d97d8385SMarcin Wojtas 
166d97d8385SMarcin Wojtas 	/* Update the date. */
167d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev,
168d97d8385SMarcin Wojtas 	    RX8803_TIME,
169d97d8385SMarcin Wojtas 	    &data, sizeof(struct rx8803_time),
170d97d8385SMarcin Wojtas 	    IIC_WAIT);
171d97d8385SMarcin Wojtas 	if (rc != 0)
172d97d8385SMarcin Wojtas 		return (rc);
173d97d8385SMarcin Wojtas 
174d97d8385SMarcin Wojtas 	/* Now restart it. */
175d97d8385SMarcin Wojtas 	reg &= ~RX8803_CTRL_DISABLE;
176d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_CTRL, &reg, sizeof(uint8_t), IIC_WAIT);
177d97d8385SMarcin Wojtas 	if (rc != 0)
178d97d8385SMarcin Wojtas 		return (rc);
179d97d8385SMarcin Wojtas 
180d97d8385SMarcin Wojtas 	/* Clear low voltage flags, as we have just updated the clock. */
181d97d8385SMarcin Wojtas 	rc = iicdev_readfrom(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
182d97d8385SMarcin Wojtas 	if (rc != 0)
183d97d8385SMarcin Wojtas 		return (rc);
184d97d8385SMarcin Wojtas 
185d97d8385SMarcin Wojtas 	reg &= ~(RX8803_FLAGS_V1F | RX8803_FLAGS_V2F);
186d97d8385SMarcin Wojtas 	rc = iicdev_writeto(dev, RX8803_FLAGS, &reg, sizeof(uint8_t), IIC_WAIT);
187d97d8385SMarcin Wojtas 	return (rc);
188d97d8385SMarcin Wojtas }
189d97d8385SMarcin Wojtas 
190d97d8385SMarcin Wojtas static int
191d97d8385SMarcin Wojtas rx8803_probe(device_t dev)
192d97d8385SMarcin Wojtas {
193d97d8385SMarcin Wojtas 
194d97d8385SMarcin Wojtas 	if (!ofw_bus_is_compatible(dev, "epson,rx8803"))
195d97d8385SMarcin Wojtas 		return (ENXIO);
196d97d8385SMarcin Wojtas 
197d97d8385SMarcin Wojtas 	device_set_desc(dev, "Epson RX8803 Real Time Clock");
198d97d8385SMarcin Wojtas 
199d97d8385SMarcin Wojtas 	return (BUS_PROBE_GENERIC);
200d97d8385SMarcin Wojtas }
201d97d8385SMarcin Wojtas 
202d97d8385SMarcin Wojtas static int
203d97d8385SMarcin Wojtas rx8803_attach(device_t dev)
204d97d8385SMarcin Wojtas {
205d97d8385SMarcin Wojtas 
206d97d8385SMarcin Wojtas 	/* Set 1 sec resolution. */
207d97d8385SMarcin Wojtas 	clock_register_flags(dev, 1000000, 0);
208d97d8385SMarcin Wojtas 	clock_schedule(dev, 1);
209d97d8385SMarcin Wojtas 
210d97d8385SMarcin Wojtas 	return (0);
211d97d8385SMarcin Wojtas 
212d97d8385SMarcin Wojtas }
213d97d8385SMarcin Wojtas 
214d97d8385SMarcin Wojtas static int
215d97d8385SMarcin Wojtas rx8803_detach(device_t dev)
216d97d8385SMarcin Wojtas {
217d97d8385SMarcin Wojtas 
218d97d8385SMarcin Wojtas 	clock_unregister(dev);
219d97d8385SMarcin Wojtas 
220d97d8385SMarcin Wojtas 	return (0);
221d97d8385SMarcin Wojtas }
222d97d8385SMarcin Wojtas 
223d97d8385SMarcin Wojtas static device_method_t rx8803_methods[] = {
224d97d8385SMarcin Wojtas 	DEVMETHOD(device_probe, rx8803_probe),
225d97d8385SMarcin Wojtas 	DEVMETHOD(device_attach, rx8803_attach),
226d97d8385SMarcin Wojtas 	DEVMETHOD(device_detach, rx8803_detach),
227d97d8385SMarcin Wojtas 
228d97d8385SMarcin Wojtas 	DEVMETHOD(clock_gettime, rx8803_gettime),
229d97d8385SMarcin Wojtas 	DEVMETHOD(clock_settime, rx8803_settime),
230d97d8385SMarcin Wojtas 
231d97d8385SMarcin Wojtas 	DEVMETHOD_END,
232d97d8385SMarcin Wojtas };
233d97d8385SMarcin Wojtas 
234d97d8385SMarcin Wojtas static driver_t rx8803_driver = {
235d97d8385SMarcin Wojtas 	"rx8803",
236d97d8385SMarcin Wojtas 	rx8803_methods,
237d97d8385SMarcin Wojtas 	0,			/* We don't need softc for this one. */
238d97d8385SMarcin Wojtas };
239d97d8385SMarcin Wojtas 
240*3a866152SJohn Baldwin DRIVER_MODULE(rx8803, iicbus, rx8803_driver, NULL, NULL);
241d97d8385SMarcin Wojtas MODULE_DEPEND(rx8803, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
242