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