]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/ds1307.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r307894, and update
[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         int             sc_mcp7941x;
64 };
65
66 static void ds1307_start(void *);
67
68 #ifdef FDT
69 static const struct ofw_compat_data ds1307_compat_data[] = {
70     {"dallas,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
71     {"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
72     {"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"},
73     { NULL, 0 }
74 };
75 #endif
76
77 static int
78 ds1307_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
79 {
80         struct iic_msg msg[2] = {
81             { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
82             { addr, IIC_M_RD, len, data },
83         };
84
85         return (iicbus_transfer(dev, msg, nitems(msg)));
86 }
87
88 static int
89 ds1307_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
90 {
91         struct iic_msg msg[1] = {
92             { addr, IIC_M_WR, len, data },
93         };
94
95         return (iicbus_transfer(dev, msg, nitems(msg)));
96 }
97
98 static int
99 ds1307_ctrl_read(struct ds1307_softc *sc)
100 {
101         int error;
102
103         sc->sc_ctrl = 0;
104         error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_CONTROL,
105             &sc->sc_ctrl, sizeof(sc->sc_ctrl));
106         if (error) {
107                 device_printf(sc->sc_dev, "cannot read from RTC.\n");
108                 return (error);
109         }
110
111         return (0);
112 }
113
114 static int
115 ds1307_ctrl_write(struct ds1307_softc *sc)
116 {
117         int error;
118         uint8_t data[2];
119
120         data[0] = DS1307_CONTROL;
121         data[1] = sc->sc_ctrl & DS1307_CTRL_MASK;
122         error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
123         if (error != 0)
124                 device_printf(sc->sc_dev, "cannot write to RTC.\n");
125
126         return (error);
127 }
128
129 static int
130 ds1307_osc_enable(struct ds1307_softc *sc)
131 {
132         int error;
133         uint8_t data[2], secs;
134
135         secs = 0;
136         error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
137             &secs, sizeof(secs));
138         if (error) {
139                 device_printf(sc->sc_dev, "cannot read from RTC.\n");
140                 return (error);
141         }
142         /* Check if the oscillator is disabled. */
143         if ((secs & DS1307_SECS_CH) == 0)
144                 return (0);
145         device_printf(sc->sc_dev, "clock was halted, check the battery.\n");
146         data[0] = DS1307_SECS;
147         data[1] = secs & DS1307_SECS_MASK;
148         error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
149         if (error != 0)
150                 device_printf(sc->sc_dev, "cannot write to RTC.\n");
151
152         return (error);
153 }
154
155 static int
156 ds1307_set_24hrs_mode(struct ds1307_softc *sc)
157 {
158         int error;
159         uint8_t data[2], hour;
160
161         hour = 0;
162         error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_HOUR,
163             &hour, sizeof(hour));
164         if (error) {
165                 device_printf(sc->sc_dev, "cannot read from RTC.\n");
166                 return (error);
167         }
168         data[0] = DS1307_HOUR;
169         data[1] = hour & DS1307_HOUR_MASK;
170         error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
171         if (error != 0)
172                 device_printf(sc->sc_dev, "cannot write to RTC.\n");
173
174         return (error);
175 }
176
177 static int
178 ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS)
179 {
180         int sqwe, error, newv, sqwe_bit;
181         struct ds1307_softc *sc;
182
183         sc = (struct ds1307_softc *)arg1;
184         error = ds1307_ctrl_read(sc);
185         if (error != 0)
186                 return (error);
187         if (sc->sc_mcp7941x)
188                 sqwe_bit = MCP7941X_CTRL_SQWE;
189         else
190                 sqwe_bit = DS1307_CTRL_SQWE;
191         sqwe = newv = (sc->sc_ctrl & sqwe_bit) ? 1 : 0;
192         error = sysctl_handle_int(oidp, &newv, 0, req);
193         if (error != 0 || req->newptr == NULL)
194                 return (error);
195         if (sqwe != newv) {
196                 sc->sc_ctrl &= ~sqwe_bit;
197                 if (newv)
198                         sc->sc_ctrl |= sqwe_bit;
199                 error = ds1307_ctrl_write(sc);
200                 if (error != 0)
201                         return (error);
202         }
203
204         return (error);
205 }
206
207 static int
208 ds1307_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS)
209 {
210         int ds1307_sqw_freq[] = { 1, 4096, 8192, 32768 };
211         int error, freq, i, newf, tmp;
212         struct ds1307_softc *sc;
213
214         sc = (struct ds1307_softc *)arg1;
215         error = ds1307_ctrl_read(sc);
216         if (error != 0)
217                 return (error);
218         tmp = (sc->sc_ctrl & DS1307_CTRL_RS_MASK);
219         if (tmp >= nitems(ds1307_sqw_freq))
220                 tmp = nitems(ds1307_sqw_freq) - 1;
221         freq = ds1307_sqw_freq[tmp];
222         error = sysctl_handle_int(oidp, &freq, 0, req);
223         if (error != 0 || req->newptr == NULL)
224                 return (error);
225         if (freq != ds1307_sqw_freq[tmp]) {
226                 newf = 0;
227                 for (i = 0; i < nitems(ds1307_sqw_freq); i++)
228                         if (freq >= ds1307_sqw_freq[i])
229                                 newf = i;
230                 sc->sc_ctrl &= ~DS1307_CTRL_RS_MASK;
231                 sc->sc_ctrl |= newf;
232                 error = ds1307_ctrl_write(sc);
233                 if (error != 0)
234                         return (error);
235         }
236
237         return (error);
238 }
239
240 static int
241 ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS)
242 {
243         int sqwe, error, newv;
244         struct ds1307_softc *sc;
245
246         sc = (struct ds1307_softc *)arg1;
247         error = ds1307_ctrl_read(sc);
248         if (error != 0)
249                 return (error);
250         sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_OUT) ? 1 : 0;
251         error = sysctl_handle_int(oidp, &newv, 0, req);
252         if (error != 0 || req->newptr == NULL)
253                 return (error);
254         if (sqwe != newv) {
255                 sc->sc_ctrl &= ~DS1307_CTRL_OUT;
256                 if (newv)
257                         sc->sc_ctrl |= DS1307_CTRL_OUT;
258                 error = ds1307_ctrl_write(sc);
259                 if (error != 0)
260                         return (error);
261         }
262
263         return (error);
264 }
265
266 static int
267 ds1307_probe(device_t dev)
268 {
269 #ifdef FDT
270         const struct ofw_compat_data *compat;
271
272         if (!ofw_bus_status_okay(dev))
273                 return (ENXIO);
274
275         compat = ofw_bus_search_compatible(dev, ds1307_compat_data);
276
277         if (compat->ocd_str == NULL)
278                 return (ENXIO);
279
280         device_set_desc(dev, (const char *)compat->ocd_data);
281
282         return (BUS_PROBE_DEFAULT);
283 #else
284         device_set_desc(dev, "Maxim DS1307 RTC");
285
286         return (BUS_PROBE_DEFAULT);
287 #endif
288 }
289
290 static int
291 ds1307_attach(device_t dev)
292 {
293         struct ds1307_softc *sc;
294
295         sc = device_get_softc(dev);
296         sc->sc_dev = dev;
297         sc->sc_addr = iicbus_get_addr(dev);
298         sc->sc_year0 = 1900;
299         sc->enum_hook.ich_func = ds1307_start;
300         sc->enum_hook.ich_arg = dev;
301
302         if (ofw_bus_is_compatible(dev, "microchip,mcp7941x"))
303                 sc->sc_mcp7941x = 1;
304
305         /*
306          * We have to wait until interrupts are enabled.  Usually I2C read
307          * and write only works when the interrupts are available.
308          */
309         if (config_intrhook_establish(&sc->enum_hook) != 0)
310                 return (ENOMEM);
311
312         return (0);
313 }
314
315 static void
316 ds1307_start(void *xdev)
317 {
318         device_t dev;
319         struct ds1307_softc *sc;
320         struct sysctl_ctx_list *ctx;
321         struct sysctl_oid *tree_node;
322         struct sysctl_oid_list *tree;
323
324         dev = (device_t)xdev;
325         sc = device_get_softc(dev);
326         ctx = device_get_sysctl_ctx(dev);
327         tree_node = device_get_sysctl_tree(dev);
328         tree = SYSCTL_CHILDREN(tree_node);
329
330         config_intrhook_disestablish(&sc->enum_hook);
331         /* Set the 24 hours mode. */
332         if (ds1307_set_24hrs_mode(sc) != 0)
333                 return;
334         /* Enable the oscillator if halted. */
335         if (ds1307_osc_enable(sc) != 0)
336                 return;
337
338         /* Configuration parameters. */
339         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe",
340             CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
341             ds1307_sqwe_sysctl, "IU", "DS1307 square-wave enable");
342         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq",
343             CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
344             ds1307_sqw_freq_sysctl, "IU",
345             "DS1307 square-wave output frequency");
346         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_out",
347             CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
348             ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state");
349
350         /* 1 second resolution. */
351         clock_register(dev, 1000000);
352 }
353
354 static int
355 ds1307_gettime(device_t dev, struct timespec *ts)
356 {
357         int error;
358         struct clocktime ct;
359         struct ds1307_softc *sc;
360         uint8_t data[7];
361
362         sc = device_get_softc(dev);
363         memset(data, 0, sizeof(data));
364         error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
365             data, sizeof(data)); 
366         if (error != 0) {
367                 device_printf(dev, "cannot read from RTC.\n");
368                 return (error);
369         }
370         ct.nsec = 0;
371         ct.sec = FROMBCD(data[DS1307_SECS] & DS1307_SECS_MASK);
372         ct.min = FROMBCD(data[DS1307_MINS] & DS1307_MINS_MASK);
373         ct.hour = FROMBCD(data[DS1307_HOUR] & DS1307_HOUR_MASK);
374         ct.day = FROMBCD(data[DS1307_DATE] & DS1307_DATE_MASK);
375         ct.dow = data[DS1307_WEEKDAY] & DS1307_WEEKDAY_MASK;
376         ct.mon = FROMBCD(data[DS1307_MONTH] & DS1307_MONTH_MASK);
377         ct.year = FROMBCD(data[DS1307_YEAR] & DS1307_YEAR_MASK);
378         ct.year += sc->sc_year0;
379         if (ct.year < POSIX_BASE_YEAR)
380                 ct.year += 100; /* assume [1970, 2069] */
381
382         return (clock_ct_to_ts(&ct, ts));
383 }
384
385 static int
386 ds1307_settime(device_t dev, struct timespec *ts)
387 {
388         int error;
389         struct clocktime ct;
390         struct ds1307_softc *sc;
391         uint8_t data[8];
392
393         sc = device_get_softc(dev);
394         /* Accuracy is only one second. */
395         if (ts->tv_nsec >= 500000000)
396                 ts->tv_sec++;
397         ts->tv_nsec = 0;
398         clock_ts_to_ct(ts, &ct);
399         memset(data, 0, sizeof(data));
400         data[0] = DS1307_SECS;
401         data[DS1307_SECS + 1] = TOBCD(ct.sec);
402         data[DS1307_MINS + 1] = TOBCD(ct.min);
403         data[DS1307_HOUR + 1] = TOBCD(ct.hour);
404         data[DS1307_DATE + 1] = TOBCD(ct.day);
405         data[DS1307_WEEKDAY + 1] = ct.dow;
406         data[DS1307_MONTH + 1] = TOBCD(ct.mon);
407         data[DS1307_YEAR + 1] = TOBCD(ct.year % 100);
408         /* Write the time back to RTC. */
409         error = ds1307_write(dev, sc->sc_addr, data, sizeof(data));
410         if (error != 0)
411                 device_printf(dev, "cannot write to RTC.\n");
412
413         return (error);
414 }
415
416 static device_method_t ds1307_methods[] = {
417         DEVMETHOD(device_probe,         ds1307_probe),
418         DEVMETHOD(device_attach,        ds1307_attach),
419
420         DEVMETHOD(clock_gettime,        ds1307_gettime),
421         DEVMETHOD(clock_settime,        ds1307_settime),
422
423         DEVMETHOD_END
424 };
425
426 static driver_t ds1307_driver = {
427         "ds1307",
428         ds1307_methods,
429         sizeof(struct ds1307_softc),
430 };
431
432 static devclass_t ds1307_devclass;
433
434 DRIVER_MODULE(ds1307, iicbus, ds1307_driver, ds1307_devclass, NULL, NULL);
435 MODULE_VERSION(ds1307, 1);
436 MODULE_DEPEND(ds1307, iicbus, 1, 1, 1);