1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_platform.h"
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/clock.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/module.h>
38
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41
42 #include <dev/iicbus/iiconf.h>
43 #include <dev/iicbus/iicbus.h>
44
45 #include "clock_if.h"
46 #include "iicbus_if.h"
47
48 #define BIT(x) (1 << (x))
49
50 #define RX8803_TIME 0x0
51 #define RX8803_FLAGS 0xE
52 #define RX8803_CTRL 0xF
53
54 #define RX8803_FLAGS_V1F BIT(0)
55 #define RX8803_FLAGS_V2F BIT(1)
56
57 #define RX8803_CTRL_DISABLE BIT(0)
58
59 #define HALF_OF_SEC_NS 500000000
60 #define MAX_WRITE_LEN 16
61
62 struct rx8803_time {
63 uint8_t sec;
64 uint8_t min;
65 uint8_t hour;
66 uint8_t dow;
67 uint8_t day;
68 uint8_t mon;
69 uint8_t year;
70 };
71
72 static struct ofw_compat_data compat_data[] = {
73 {"epson,rx8803", 1},
74 {NULL, 0},
75 };
76
77 static int rx8803_probe(device_t dev);
78 static int rx8803_attach(device_t dev);
79 static int rx8803_detach(device_t dev);
80
81 static int rx8803_gettime(device_t dev, struct timespec *ts);
82 static int rx8803_settime(device_t dev, struct timespec *ts);
83
84 static int rx8803_check_status(device_t dev);
85
86 static int
rx8803_check_status(device_t dev)87 rx8803_check_status(device_t dev)
88 {
89 uint8_t flags;
90 int rc;
91
92 rc = iicdev_readfrom(dev, RX8803_FLAGS, &flags, 1, IIC_WAIT);
93 if (rc != 0)
94 return (rc);
95
96 if (flags & RX8803_FLAGS_V2F) {
97 device_printf(dev, "Low voltage flag set, date is incorrect\n");
98 return (ENXIO);
99 }
100
101 return (0);
102 }
103
104 static int
rx8803_gettime(device_t dev,struct timespec * ts)105 rx8803_gettime(device_t dev, struct timespec *ts)
106 {
107 struct rx8803_time data;
108 struct bcd_clocktime bcd;
109 int rc;
110
111 rc = rx8803_check_status(dev);
112 if (rc != 0)
113 return (rc);
114
115 rc = iicdev_readfrom(dev,
116 RX8803_TIME,
117 &data, sizeof(struct rx8803_time),
118 IIC_WAIT);
119 if (rc != 0)
120 return (rc);
121
122 bcd.nsec = 0;
123 bcd.sec = data.sec & 0x7F;
124 bcd.min = data.min & 0x7F;
125 bcd.hour = data.hour & 0x3F;
126 bcd.dow = flsl(data.dow & 0x7F) - 1;
127 bcd.day = data.day & 0x3F;
128 bcd.mon = (data.mon & 0x1F);
129 bcd.year = data.year;
130
131 clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
132
133 rc = clock_bcd_to_ts(&bcd, ts, false);
134 return (rc);
135 }
136
137 static int
rx8803_settime(device_t dev,struct timespec * ts)138 rx8803_settime(device_t dev, struct timespec *ts)
139 {
140 struct rx8803_time data;
141 struct bcd_clocktime bcd;
142 uint8_t reg;
143 int rc;
144
145 ts->tv_sec -= utc_offset();
146 clock_ts_to_bcd(ts, &bcd, false);
147 clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
148
149 data.sec = bcd.sec;
150 data.min = bcd.min;
151 data.hour = bcd.hour;
152 data.dow = 1 << bcd.dow;
153 data.day = bcd.day;
154 data.mon = bcd.mon;
155 data.year = bcd.year;
156
157 if (ts->tv_nsec > HALF_OF_SEC_NS)
158 data.sec++;
159
160 /* First disable clock. */
161 rc = iicdev_readfrom(dev, RX8803_CTRL, ®, sizeof(uint8_t), IIC_WAIT);
162 if (rc != 0)
163 return (rc);
164
165 reg |= RX8803_CTRL_DISABLE;
166
167 rc = iicdev_writeto(dev, RX8803_CTRL, ®, sizeof(uint8_t), IIC_WAIT);
168 if (rc != 0)
169 return (rc);
170
171 /* Update the date. */
172 rc = iicdev_writeto(dev,
173 RX8803_TIME,
174 &data, sizeof(struct rx8803_time),
175 IIC_WAIT);
176 if (rc != 0)
177 return (rc);
178
179 /* Now restart it. */
180 reg &= ~RX8803_CTRL_DISABLE;
181 rc = iicdev_writeto(dev, RX8803_CTRL, ®, sizeof(uint8_t), IIC_WAIT);
182 if (rc != 0)
183 return (rc);
184
185 /* Clear low voltage flags, as we have just updated the clock. */
186 rc = iicdev_readfrom(dev, RX8803_FLAGS, ®, sizeof(uint8_t), IIC_WAIT);
187 if (rc != 0)
188 return (rc);
189
190 reg &= ~(RX8803_FLAGS_V1F | RX8803_FLAGS_V2F);
191 rc = iicdev_writeto(dev, RX8803_FLAGS, ®, sizeof(uint8_t), IIC_WAIT);
192 return (rc);
193 }
194
195 static int
rx8803_probe(device_t dev)196 rx8803_probe(device_t dev)
197 {
198
199 if (!ofw_bus_status_okay(dev))
200 return (ENXIO);
201
202 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
203 return (ENXIO);
204
205 device_set_desc(dev, "Epson RX8803 Real Time Clock");
206
207 return (BUS_PROBE_GENERIC);
208 }
209
210 static int
rx8803_attach(device_t dev)211 rx8803_attach(device_t dev)
212 {
213
214 /* Set 1 sec resolution. */
215 clock_register_flags(dev, 1000000, 0);
216 clock_schedule(dev, 1);
217
218 return (0);
219
220 }
221
222 static int
rx8803_detach(device_t dev)223 rx8803_detach(device_t dev)
224 {
225
226 clock_unregister(dev);
227
228 return (0);
229 }
230
231 static device_method_t rx8803_methods[] = {
232 DEVMETHOD(device_probe, rx8803_probe),
233 DEVMETHOD(device_attach, rx8803_attach),
234 DEVMETHOD(device_detach, rx8803_detach),
235
236 DEVMETHOD(clock_gettime, rx8803_gettime),
237 DEVMETHOD(clock_settime, rx8803_settime),
238
239 DEVMETHOD_END,
240 };
241
242 static driver_t rx8803_driver = {
243 "rx8803",
244 rx8803_methods,
245 0, /* We don't need softc for this one. */
246 };
247
248 DRIVER_MODULE(rx8803, iicbus, rx8803_driver, NULL, NULL);
249 MODULE_VERSION(rx8803, 1);
250 MODULE_DEPEND(rx8803, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
251 IICBUS_FDT_PNP_INFO(compat_data);
252