]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/pcf8563.c
Update ELF Tool Chain to upstream rev 3400
[FreeBSD/FreeBSD.git] / sys / dev / iicbus / pcf8563.c
1 /*-
2  * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org>
3  * Copyright (c) 2015 Juraj Lutter
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * Driver for NXP PCF8563 real-time clock/calendar
33  */
34
35 #include "opt_platform.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/bus.h>
40 #include <sys/clock.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43
44 #include <dev/iicbus/iicbus.h>
45 #include <dev/iicbus/iiconf.h>
46 #include <dev/iicbus/pcf8563reg.h>
47 #ifdef FDT
48 #include <dev/ofw/openfirm.h>
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 #endif
52
53 #include "clock_if.h"
54 #include "iicbus_if.h"
55
56 #define PCF8563_NCLOCKREGS      (PCF8563_R_YEAR - PCF8563_R_CS1 + 1)
57
58 struct pcf8563_softc {
59         struct intr_config_hook enum_hook;
60         uint32_t        sc_flags;
61 #define PCF8563_CPOL    (1 << 0)        /* PCF8563_R_MONTH_C means 19xx */
62         uint16_t        sc_addr;        /* PCF8563 slave address */
63         uint16_t        sc_year0;       /* TOD clock year 0 */
64 };
65
66 static device_attach_t pcf8563_attach;
67 static device_probe_t pcf8563_probe;
68 static clock_gettime_t pcf8563_gettime;
69 static clock_settime_t pcf8563_settime;
70 static void pcf8563_start(void *);
71
72 static int
73 pcf8563_probe(device_t dev)
74 {
75
76 #ifdef FDT
77         if (!ofw_bus_status_okay(dev))
78                 return (ENXIO);
79         if (!ofw_bus_is_compatible(dev, "nxp,pcf8563") &&
80             !ofw_bus_is_compatible(dev, "philips,pcf8563") &&
81             !ofw_bus_is_compatible(dev, "pcf8563"))
82                 return (ENXIO);
83 #endif
84         device_set_desc(dev, "NXP PCF8563 RTC");
85
86         return (BUS_PROBE_DEFAULT);
87 }
88
89 static int
90 pcf8563_attach(device_t dev)
91 {
92         struct pcf8563_softc *sc;
93
94         sc = device_get_softc(dev);
95         sc->sc_addr = iicbus_get_addr(dev);
96         if (sc->sc_addr == 0)
97                 sc->sc_addr = PCF8563_ADDR;
98         sc->sc_year0 = 1900;
99         sc->enum_hook.ich_func = pcf8563_start;
100         sc->enum_hook.ich_arg = dev;
101
102         /*
103          * We have to wait until interrupts are enabled.  Sometimes I2C read
104          * and write only works when the interrupts are available.
105          */
106         if (config_intrhook_establish(&sc->enum_hook) != 0)
107                 return (ENOMEM);
108
109         return (0);
110 }
111
112 static void
113 pcf8563_start(void *xdev)
114 {
115         device_t dev;
116         uint8_t reg = PCF8563_R_SECOND, val;
117         struct iic_msg msgs[] = {
118                 { 0, IIC_M_WR, sizeof(reg), &reg },
119                 { 0, IIC_M_RD, sizeof(val), &val }
120         };
121         struct pcf8563_softc *sc;
122
123         dev = (device_t)xdev;
124         sc = device_get_softc(dev);
125         config_intrhook_disestablish(&sc->enum_hook);
126
127         /*
128          * NB: PCF8563_R_SECOND_VL doesn't automatically clear when VDD
129          * rises above Vlow again and needs to be cleared manually.
130          * However, apparently this needs all of the time registers to be
131          * set, i.e. pcf8563_settime(), and not just PCF8563_R_SECOND in
132          * order for PCF8563_R_SECOND_VL to stick.  Thus, we just issue a
133          * warning here rather than failing with ENXIO in case it is set.
134          * Note that pcf8563_settime() will also clear PCF8563_R_SECOND_VL
135          * as a side-effect.
136          */
137         msgs[0].slave = msgs[1].slave = sc->sc_addr;
138         if (iicbus_transfer(dev, msgs, nitems(msgs)) != 0) {
139                 device_printf(dev, "%s: cannot read RTC\n", __func__);
140                 return;
141         }
142         if ((val & PCF8563_R_SECOND_VL) != 0)
143                 device_printf(dev, "%s: battery low\n", __func__);
144
145         clock_register(dev, 1000000);   /* 1 second resolution */
146 }
147
148 static int
149 pcf8563_gettime(device_t dev, struct timespec *ts)
150 {
151         struct clocktime ct;
152         uint8_t reg = PCF8563_R_SECOND, val[PCF8563_NCLOCKREGS];
153         struct iic_msg msgs[] = {
154                 { 0, IIC_M_WR, sizeof(reg), &reg },
155                 { 0, IIC_M_RD, PCF8563_NCLOCKREGS, &val[PCF8563_R_SECOND] }
156         };
157         struct pcf8563_softc *sc;
158         int error;
159
160         sc = device_get_softc(dev);
161         msgs[0].slave = msgs[1].slave = sc->sc_addr;
162         error = iicbus_transfer(dev, msgs, nitems(msgs));
163         if (error != 0) {
164                 device_printf(dev, "%s: cannot read RTC\n", __func__);
165                 return (error);
166         }
167
168         ct.nsec = 0;
169         ct.sec = FROMBCD(val[PCF8563_R_SECOND] & PCF8563_M_SECOND);
170         ct.min = FROMBCD(val[PCF8563_R_MINUTE] & PCF8563_M_MINUTE);
171         ct.hour = FROMBCD(val[PCF8563_R_HOUR] & PCF8563_M_HOUR);
172         ct.day = FROMBCD(val[PCF8563_R_DAY] & PCF8563_M_DAY);
173         ct.dow = val[PCF8563_R_WEEKDAY] & PCF8563_M_WEEKDAY;
174         ct.mon = FROMBCD(val[PCF8563_R_MONTH] & PCF8563_M_MONTH);
175         ct.year = FROMBCD(val[PCF8563_R_YEAR] & PCF8563_M_YEAR);
176         ct.year += sc->sc_year0;
177         if (ct.year < POSIX_BASE_YEAR)
178                 ct.year += 100; /* assume [1970, 2069] */
179         if ((val[PCF8563_R_MONTH] & PCF8563_R_MONTH_C) != 0) {
180                 if (ct.year >= 100 + sc->sc_year0)
181                         sc->sc_flags |= PCF8563_CPOL;
182         } else if (ct.year < 100 + sc->sc_year0)
183                         sc->sc_flags |= PCF8563_CPOL;
184
185         return (clock_ct_to_ts(&ct, ts));
186 }
187
188 static int
189 pcf8563_settime(device_t dev, struct timespec *ts)
190 {
191         struct clocktime ct;
192         uint8_t val[PCF8563_NCLOCKREGS];
193         struct iic_msg msgs[] = {
194                 { 0, IIC_M_WR, PCF8563_NCLOCKREGS - 1, &val[PCF8563_R_CS2] }
195         };
196         struct pcf8563_softc *sc;
197         int error;
198
199         sc = device_get_softc(dev);
200         val[PCF8563_R_CS2] = PCF8563_R_SECOND;  /* abuse */
201         /* Accuracy is only one second. */
202         if (ts->tv_nsec >= 500000000)
203                 ts->tv_sec++;
204         ts->tv_nsec = 0;
205         clock_ts_to_ct(ts, &ct);
206         val[PCF8563_R_SECOND] = TOBCD(ct.sec);
207         val[PCF8563_R_MINUTE] = TOBCD(ct.min);
208         val[PCF8563_R_HOUR] = TOBCD(ct.hour);
209         val[PCF8563_R_DAY] = TOBCD(ct.day);
210         val[PCF8563_R_WEEKDAY] = ct.dow;
211         val[PCF8563_R_MONTH] = TOBCD(ct.mon);
212         val[PCF8563_R_YEAR] = TOBCD(ct.year % 100);
213         if ((sc->sc_flags & PCF8563_CPOL) != 0) {
214                 if (ct.year >= 100 + sc->sc_year0)
215                         val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C;
216         } else if (ct.year < 100 + sc->sc_year0)
217                         val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C;
218
219         msgs[0].slave = sc->sc_addr;
220         error = iicbus_transfer(dev, msgs, nitems(msgs));
221         if (error != 0)
222                 device_printf(dev, "%s: cannot write RTC\n", __func__);
223
224         return (error);
225 }
226
227 static device_method_t pcf8563_methods[] = {
228         DEVMETHOD(device_probe,         pcf8563_probe),
229         DEVMETHOD(device_attach,        pcf8563_attach),
230
231         DEVMETHOD(clock_gettime,        pcf8563_gettime),
232         DEVMETHOD(clock_settime,        pcf8563_settime),
233
234         DEVMETHOD_END
235 };
236
237 static driver_t pcf8563_driver = {
238         "pcf8563_rtc",
239         pcf8563_methods,
240         sizeof(struct pcf8563_softc),
241 };
242
243 static devclass_t pcf8563_devclass;
244
245 DRIVER_MODULE(pcf8563, iicbus, pcf8563_driver, pcf8563_devclass, NULL, NULL);
246 MODULE_VERSION(pcf8563, 1);
247 MODULE_DEPEND(pcf8563, iicbus, 1, 1, 1);