]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/ds1307.c
Update libc++ to 3.7.0 release.
[FreeBSD/FreeBSD.git] / sys / dev / iicbus / ds1307.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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);