xref: /NextBSD/sys/dev/iicbus/ds1307.c (revision 287e3b14e9552995def1802ec9c5034f4adf28ec)
1 /*-
2  * Copyright (c) 2015 Luiz Otavio O Souza <loos@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 /*
31  * Driver for Maxim DS1307 I2C real-time clock/calendar.
32  */
33 
34 #include "opt_platform.h"
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/clock.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/sysctl.h>
43 
44 #include <dev/iicbus/iicbus.h>
45 #include <dev/iicbus/iiconf.h>
46 #ifdef FDT
47 #include <dev/ofw/openfirm.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50 #endif
51 
52 #include <dev/iicbus/ds1307reg.h>
53 
54 #include "clock_if.h"
55 #include "iicbus_if.h"
56 
57 struct ds1307_softc {
58 	device_t	sc_dev;
59 	int		sc_year0;
60 	struct intr_config_hook	enum_hook;
61 	uint16_t	sc_addr;	/* DS1307 slave address. */
62 	uint8_t		sc_ctrl;
63 };
64 
65 static void ds1307_start(void *);
66 
67 static int
ds1307_read(device_t dev,uint16_t addr,uint8_t reg,uint8_t * data,size_t len)68 ds1307_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
69 {
70 	struct iic_msg msg[2] = {
71 	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
72 	    { addr, IIC_M_RD, len, data },
73 	};
74 
75 	return (iicbus_transfer(dev, msg, nitems(msg)));
76 }
77 
78 static int
ds1307_write(device_t dev,uint16_t addr,uint8_t * data,size_t len)79 ds1307_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
80 {
81 	struct iic_msg msg[1] = {
82 	    { addr, IIC_M_WR, len, data },
83 	};
84 
85 	return (iicbus_transfer(dev, msg, nitems(msg)));
86 }
87 
88 static int
ds1307_ctrl_read(struct ds1307_softc * sc)89 ds1307_ctrl_read(struct ds1307_softc *sc)
90 {
91 	int error;
92 
93 	sc->sc_ctrl = 0;
94 	error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_CONTROL,
95 	    &sc->sc_ctrl, sizeof(sc->sc_ctrl));
96 	if (error) {
97 		device_printf(sc->sc_dev, "cannot read from RTC.\n");
98 		return (error);
99 	}
100 
101 	return (0);
102 }
103 
104 static int
ds1307_ctrl_write(struct ds1307_softc * sc)105 ds1307_ctrl_write(struct ds1307_softc *sc)
106 {
107 	int error;
108 	uint8_t data[2];
109 
110 	data[0] = DS1307_CONTROL;
111 	data[1] = sc->sc_ctrl & DS1307_CTRL_MASK;
112 	error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
113 	if (error != 0)
114 		device_printf(sc->sc_dev, "cannot write to RTC.\n");
115 
116 	return (error);
117 }
118 
119 static int
ds1307_osc_enable(struct ds1307_softc * sc)120 ds1307_osc_enable(struct ds1307_softc *sc)
121 {
122 	int error;
123 	uint8_t data[2], secs;
124 
125 	secs = 0;
126 	error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
127 	    &secs, sizeof(secs));
128 	if (error) {
129 		device_printf(sc->sc_dev, "cannot read from RTC.\n");
130 		return (error);
131 	}
132 	/* Check if the oscillator is disabled. */
133 	if ((secs & DS1307_SECS_CH) == 0)
134 		return (0);
135 	device_printf(sc->sc_dev, "clock was halted, check the battery.\n");
136 	data[0] = DS1307_SECS;
137 	data[1] = secs & DS1307_SECS_MASK;
138 	error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
139 	if (error != 0)
140 		device_printf(sc->sc_dev, "cannot write to RTC.\n");
141 
142 	return (error);
143 }
144 
145 static int
ds1307_set_24hrs_mode(struct ds1307_softc * sc)146 ds1307_set_24hrs_mode(struct ds1307_softc *sc)
147 {
148 	int error;
149 	uint8_t data[2], hour;
150 
151 	hour = 0;
152 	error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_HOUR,
153 	    &hour, sizeof(hour));
154 	if (error) {
155 		device_printf(sc->sc_dev, "cannot read from RTC.\n");
156 		return (error);
157 	}
158 	data[0] = DS1307_HOUR;
159 	data[1] = hour & DS1307_HOUR_MASK;
160 	error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
161 	if (error != 0)
162 		device_printf(sc->sc_dev, "cannot write to RTC.\n");
163 
164 	return (error);
165 }
166 
167 static int
ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS)168 ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS)
169 {
170 	int sqwe, error, newv;
171 	struct ds1307_softc *sc;
172 
173 	sc = (struct ds1307_softc *)arg1;
174 	error = ds1307_ctrl_read(sc);
175 	if (error != 0)
176 		return (error);
177 	sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_SQWE) ? 1 : 0;
178 	error = sysctl_handle_int(oidp, &newv, 0, req);
179 	if (error != 0 || req->newptr == NULL)
180 		return (error);
181 	if (sqwe != newv) {
182 		sc->sc_ctrl &= ~DS1307_CTRL_SQWE;
183 		if (newv)
184 			sc->sc_ctrl |= DS1307_CTRL_SQWE;
185 		error = ds1307_ctrl_write(sc);
186 		if (error != 0)
187 			return (error);
188 	}
189 
190 	return (error);
191 }
192 
193 static int
ds1307_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS)194 ds1307_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS)
195 {
196 	int ds1307_sqw_freq[] = { 1, 4096, 8192, 32768 };
197 	int error, freq, i, newf, tmp;
198 	struct ds1307_softc *sc;
199 
200 	sc = (struct ds1307_softc *)arg1;
201 	error = ds1307_ctrl_read(sc);
202 	if (error != 0)
203 		return (error);
204 	tmp = (sc->sc_ctrl & DS1307_CTRL_RS_MASK);
205 	if (tmp >= nitems(ds1307_sqw_freq))
206 		tmp = nitems(ds1307_sqw_freq) - 1;
207 	freq = ds1307_sqw_freq[tmp];
208 	error = sysctl_handle_int(oidp, &freq, 0, req);
209 	if (error != 0 || req->newptr == NULL)
210 		return (error);
211 	if (freq != ds1307_sqw_freq[tmp]) {
212 		newf = 0;
213 		for (i = 0; i < nitems(ds1307_sqw_freq); i++)
214 			if (freq >= ds1307_sqw_freq[i])
215 				newf = i;
216 		sc->sc_ctrl &= ~DS1307_CTRL_RS_MASK;
217 		sc->sc_ctrl |= newf;
218 		error = ds1307_ctrl_write(sc);
219 		if (error != 0)
220 			return (error);
221 	}
222 
223 	return (error);
224 }
225 
226 static int
ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS)227 ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS)
228 {
229 	int sqwe, error, newv;
230 	struct ds1307_softc *sc;
231 
232 	sc = (struct ds1307_softc *)arg1;
233 	error = ds1307_ctrl_read(sc);
234 	if (error != 0)
235 		return (error);
236 	sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_OUT) ? 1 : 0;
237 	error = sysctl_handle_int(oidp, &newv, 0, req);
238 	if (error != 0 || req->newptr == NULL)
239 		return (error);
240 	if (sqwe != newv) {
241 		sc->sc_ctrl &= ~DS1307_CTRL_OUT;
242 		if (newv)
243 			sc->sc_ctrl |= DS1307_CTRL_OUT;
244 		error = ds1307_ctrl_write(sc);
245 		if (error != 0)
246 			return (error);
247 	}
248 
249 	return (error);
250 }
251 
252 static int
ds1307_probe(device_t dev)253 ds1307_probe(device_t dev)
254 {
255 
256 #ifdef FDT
257 	if (!ofw_bus_status_okay(dev))
258 		return (ENXIO);
259 	if (!ofw_bus_is_compatible(dev, "dallas,ds1307") &&
260 	    !ofw_bus_is_compatible(dev, "maxim,ds1307"))
261 		return (ENXIO);
262 #endif
263 	device_set_desc(dev, "Maxim DS1307 RTC");
264 
265 	return (BUS_PROBE_DEFAULT);
266 }
267 
268 static int
ds1307_attach(device_t dev)269 ds1307_attach(device_t dev)
270 {
271 	struct ds1307_softc *sc;
272 
273 	sc = device_get_softc(dev);
274 	sc->sc_dev = dev;
275 	sc->sc_addr = iicbus_get_addr(dev);
276 	sc->sc_year0 = 1900;
277 	sc->enum_hook.ich_func = ds1307_start;
278 	sc->enum_hook.ich_arg = dev;
279 
280 	/*
281 	 * We have to wait until interrupts are enabled.  Usually I2C read
282 	 * and write only works when the interrupts are available.
283 	 */
284 	if (config_intrhook_establish(&sc->enum_hook) != 0)
285 		return (ENOMEM);
286 
287 	return (0);
288 }
289 
290 static void
ds1307_start(void * xdev)291 ds1307_start(void *xdev)
292 {
293 	device_t dev;
294 	struct ds1307_softc *sc;
295 	struct sysctl_ctx_list *ctx;
296 	struct sysctl_oid *tree_node;
297 	struct sysctl_oid_list *tree;
298 
299 	dev = (device_t)xdev;
300 	sc = device_get_softc(dev);
301 	ctx = device_get_sysctl_ctx(dev);
302 	tree_node = device_get_sysctl_tree(dev);
303 	tree = SYSCTL_CHILDREN(tree_node);
304 
305 	config_intrhook_disestablish(&sc->enum_hook);
306 	/* Set the 24 hours mode. */
307 	if (ds1307_set_24hrs_mode(sc) != 0)
308 		return;
309 	/* Enable the oscillator if halted. */
310 	if (ds1307_osc_enable(sc) != 0)
311 		return;
312 
313 	/* Configuration parameters. */
314 	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe",
315 	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
316 	    ds1307_sqwe_sysctl, "IU", "DS1307 square-wave enable");
317 	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq",
318 	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
319 	    ds1307_sqw_freq_sysctl, "IU",
320 	    "DS1307 square-wave output frequency");
321 	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_out",
322 	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
323 	    ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state");
324 
325 	/* 1 second resolution. */
326 	clock_register(dev, 1000000);
327 }
328 
329 static int
ds1307_gettime(device_t dev,struct timespec * ts)330 ds1307_gettime(device_t dev, struct timespec *ts)
331 {
332 	int error;
333 	struct clocktime ct;
334 	struct ds1307_softc *sc;
335 	uint8_t data[7];
336 
337 	sc = device_get_softc(dev);
338 	memset(data, 0, sizeof(data));
339 	error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
340 	    data, sizeof(data));
341 	if (error != 0) {
342 		device_printf(dev, "cannot read from RTC.\n");
343 		return (error);
344 	}
345 	ct.nsec = 0;
346 	ct.sec = FROMBCD(data[DS1307_SECS] & DS1307_SECS_MASK);
347 	ct.min = FROMBCD(data[DS1307_MINS] & DS1307_MINS_MASK);
348 	ct.hour = FROMBCD(data[DS1307_HOUR] & DS1307_HOUR_MASK);
349 	ct.day = FROMBCD(data[DS1307_DATE] & DS1307_DATE_MASK);
350 	ct.dow = data[DS1307_WEEKDAY] & DS1307_WEEKDAY_MASK;
351 	ct.mon = FROMBCD(data[DS1307_MONTH] & DS1307_MONTH_MASK);
352 	ct.year = FROMBCD(data[DS1307_YEAR] & DS1307_YEAR_MASK);
353 	ct.year += sc->sc_year0;
354 	if (ct.year < POSIX_BASE_YEAR)
355 		ct.year += 100;	/* assume [1970, 2069] */
356 
357 	return (clock_ct_to_ts(&ct, ts));
358 }
359 
360 static int
ds1307_settime(device_t dev,struct timespec * ts)361 ds1307_settime(device_t dev, struct timespec *ts)
362 {
363 	int error;
364 	struct clocktime ct;
365 	struct ds1307_softc *sc;
366 	uint8_t data[8];
367 
368 	sc = device_get_softc(dev);
369 	/* Accuracy is only one second. */
370 	if (ts->tv_nsec >= 500000000)
371 		ts->tv_sec++;
372 	ts->tv_nsec = 0;
373 	clock_ts_to_ct(ts, &ct);
374 	memset(data, 0, sizeof(data));
375 	data[0] = DS1307_SECS;
376 	data[DS1307_SECS + 1] = TOBCD(ct.sec);
377 	data[DS1307_MINS + 1] = TOBCD(ct.min);
378 	data[DS1307_HOUR + 1] = TOBCD(ct.hour);
379 	data[DS1307_DATE + 1] = TOBCD(ct.day);
380 	data[DS1307_WEEKDAY + 1] = ct.dow;
381 	data[DS1307_MONTH + 1] = TOBCD(ct.mon);
382 	data[DS1307_YEAR + 1] = TOBCD(ct.year % 100);
383 	/* Write the time back to RTC. */
384 	error = ds1307_write(dev, sc->sc_addr, data, sizeof(data));
385 	if (error != 0)
386 		device_printf(dev, "cannot write to RTC.\n");
387 
388 	return (error);
389 }
390 
391 static device_method_t ds1307_methods[] = {
392 	DEVMETHOD(device_probe,		ds1307_probe),
393 	DEVMETHOD(device_attach,	ds1307_attach),
394 
395 	DEVMETHOD(clock_gettime,	ds1307_gettime),
396 	DEVMETHOD(clock_settime,	ds1307_settime),
397 
398 	DEVMETHOD_END
399 };
400 
401 static driver_t ds1307_driver = {
402 	"ds1307",
403 	ds1307_methods,
404 	sizeof(struct ds1307_softc),
405 };
406 
407 static devclass_t ds1307_devclass;
408 
409 DRIVER_MODULE(ds1307, iicbus, ds1307_driver, ds1307_devclass, NULL, NULL);
410 MODULE_VERSION(ds1307, 1);
411 MODULE_DEPEND(ds1307, iicbus, 1, 1, 1);
412