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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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);
Cache object: dfa58f5348305ea5fa3a0ebbb75d84b5
|