1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Alstom Group.
5 * Copyright (c) 2021 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 PCF85063_CTRL1_REG 0x0
51 #define PCF85063_TIME_REG 0x4
52
53 #define PCF85063_CTRL1_TIME_FORMAT BIT(1)
54 #define PCF85063_CTRL1_RTC_CLK_STOP BIT(5)
55 #define PCF85063_TIME_REG_OSC_STOP BIT(7)
56
57 #define PCF85063_HALF_OF_SEC_NS 500000000
58
59 struct pcf85063_time {
60 uint8_t sec;
61 uint8_t min;
62 uint8_t hour;
63 uint8_t day;
64 uint8_t dow;
65 uint8_t mon;
66 uint8_t year;
67 };
68
69 static int pcf85063_attach(device_t dev);
70 static int pcf85063_detach(device_t dev);
71 static int pcf85063_probe(device_t dev);
72
73 static int pcf85063_get_time(device_t dev, struct timespec *ts);
74 static int pcf85063_set_time(device_t dev, struct timespec *ts);
75
76 static int pcf85063_check_status(device_t dev);
77
78 static struct ofw_compat_data pcf85063_compat_data[] = {
79 { "nxp,pcf85063", 1},
80 { NULL, 0}
81 };
82
83 static int
pcf85063_check_status(device_t dev)84 pcf85063_check_status(device_t dev)
85 {
86 uint8_t flags;
87 int error;
88
89 error = iicdev_readfrom(dev, PCF85063_TIME_REG, &flags, 1, IIC_WAIT);
90 if (error != 0)
91 return (error);
92
93 if (flags & PCF85063_TIME_REG_OSC_STOP) {
94 device_printf(dev,
95 "Low voltage flag set, date is incorrect.\n");
96 return (ENXIO);
97 }
98
99 return (0);
100 }
101
102 static int
pcf85063_attach(device_t dev)103 pcf85063_attach(device_t dev)
104 {
105
106 clock_register_flags(dev, 1000000, 0);
107 clock_schedule(dev, 1);
108
109 return (0);
110 }
111
112 static int
pcf85063_detach(device_t dev)113 pcf85063_detach(device_t dev)
114 {
115
116 clock_unregister(dev);
117
118 return (0);
119 }
120
121 static int
pcf85063_get_time(device_t dev,struct timespec * ts)122 pcf85063_get_time(device_t dev, struct timespec *ts)
123 {
124 struct pcf85063_time data;
125 struct bcd_clocktime bcd;
126 uint8_t control_reg;
127 int error;
128
129 error = pcf85063_check_status(dev);
130 if (error != 0)
131 return (error);
132
133 /* read hour format (12/24 hour mode) */
134 error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &control_reg,
135 sizeof(uint8_t), IIC_WAIT);
136 if (error != 0)
137 return (error);
138
139 /* read current date and time */
140 error = iicdev_readfrom(dev, PCF85063_TIME_REG, &data,
141 sizeof(struct pcf85063_time), IIC_WAIT);
142 if (error != 0)
143 return (error);
144
145 bcd.nsec = 0;
146 bcd.sec = data.sec & 0x7F;
147 bcd.min = data.min & 0x7F;
148
149 if (control_reg & PCF85063_CTRL1_TIME_FORMAT) {
150 /* 12 hour mode */
151 bcd.hour = data.hour & 0x1F;
152 /* Check if hour is pm */
153 bcd.ispm = data.hour & 0x20;
154 } else {
155 /* 24 hour mode */
156 bcd.hour = data.hour & 0x3F;
157 bcd.ispm = false;
158 }
159
160 bcd.dow = (data.dow & 0x7) + 1;
161 bcd.day = data.day & 0x3F;
162 bcd.mon = data.mon & 0x1F;
163 bcd.year = data.year;
164
165 clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bcd);
166 error = clock_bcd_to_ts(&bcd, ts,
167 control_reg & PCF85063_CTRL1_TIME_FORMAT);
168
169 return (error);
170 }
171
172 static int
pcf85063_set_time(device_t dev,struct timespec * ts)173 pcf85063_set_time(device_t dev, struct timespec *ts)
174 {
175 uint8_t time_reg, ctrl_reg;
176 struct pcf85063_time data;
177 struct bcd_clocktime bcd;
178 int error;
179
180 error = iicdev_readfrom(dev, PCF85063_TIME_REG, &ctrl_reg,
181 sizeof(uint8_t), IIC_WAIT);
182
183 ts->tv_sec -= utc_offset();
184 clock_ts_to_bcd(ts, &bcd, false);
185 clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bcd);
186
187 data.sec = bcd.sec;
188 data.min = bcd.min;
189 data.hour = bcd.hour;
190 data.dow = bcd.dow - 1;
191 data.day = bcd.day;
192 data.mon = bcd.mon;
193 data.year = bcd.year;
194
195 if (ts->tv_nsec > PCF85063_HALF_OF_SEC_NS)
196 data.sec++;
197
198 /* disable clock */
199 error = iicdev_readfrom(dev, PCF85063_CTRL1_REG, &ctrl_reg,
200 sizeof(uint8_t), IIC_WAIT);
201 if (error != 0)
202 return (error);
203
204 ctrl_reg |= PCF85063_CTRL1_RTC_CLK_STOP;
205 /* Explicitly set 24-hour mode. */
206 ctrl_reg &= ~PCF85063_CTRL1_TIME_FORMAT;
207
208 error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
209 sizeof(uint8_t), IIC_WAIT);
210 if (error != 0)
211 return (error);
212
213 /* clock is disabled now, write time and date */
214 error = iicdev_writeto(dev, PCF85063_TIME_REG, &data,
215 sizeof(struct pcf85063_time), IIC_WAIT);
216 if (error != 0)
217 return (error);
218
219 /* restart clock */
220 ctrl_reg &= ~PCF85063_CTRL1_RTC_CLK_STOP;
221 error = iicdev_writeto(dev, PCF85063_CTRL1_REG, &ctrl_reg,
222 sizeof(uint8_t), IIC_WAIT);
223 if (error != 0)
224 return (error);
225
226 /* reset low voltage flag */
227 error = iicdev_readfrom(dev, PCF85063_TIME_REG, &time_reg,
228 sizeof(uint8_t), IIC_WAIT);
229 if (error != 0)
230 return (error);
231
232 time_reg &= ~PCF85063_TIME_REG_OSC_STOP;
233
234 error = iicdev_writeto(dev, PCF85063_TIME_REG, &time_reg,
235 sizeof(uint8_t), IIC_WAIT);
236
237 return (error);
238 }
239
240 static int
pcf85063_probe(device_t dev)241 pcf85063_probe(device_t dev)
242 {
243
244 if (!ofw_bus_status_okay(dev))
245 return (ENXIO);
246
247 if (!ofw_bus_search_compatible(dev, pcf85063_compat_data)->ocd_data)
248 return (ENXIO);
249
250 device_set_desc(dev, "NXP pcf85063 Real Time Clock");
251
252 return (BUS_PROBE_GENERIC);
253 }
254
255 static device_method_t pcf85063_methods [] = {
256 DEVMETHOD(device_attach, pcf85063_attach),
257 DEVMETHOD(device_detach, pcf85063_detach),
258 DEVMETHOD(device_probe, pcf85063_probe),
259
260 DEVMETHOD(clock_gettime, pcf85063_get_time),
261 DEVMETHOD(clock_settime, pcf85063_set_time),
262
263 DEVMETHOD_END
264 };
265
266 static driver_t pcf85063_driver = {
267 "pcf85063",
268 pcf85063_methods,
269 0
270 };
271
272 DRIVER_MODULE(pcf85063, iicbus, pcf85063_driver, NULL, NULL);
273 IICBUS_FDT_PNP_INFO(pcf85063_compat_data);
274