]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/s35390a.c
MFC r334002: uchcom: extend hardware support to version 0x30
[FreeBSD/FreeBSD.git] / sys / dev / iicbus / s35390a.c
1 /*-
2  * Copyright (c) 2012 Yusuke Tanaka
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 ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * 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 /*-
28  * Copyright (c) 2011 Frank Wille.
29  * All rights reserved.
30  *
31  * Written by Frank Wille for The NetBSD Project.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
43  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
44  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
46  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
47  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
48  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
49  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
50  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52  * POSSIBILITY OF SUCH DAMAGE.
53  */
54
55 #include <sys/cdefs.h>
56 __FBSDID("$FreeBSD$");
57
58 /*
59  * Driver for Seiko Instruments S-35390A Real-time Clock
60  */
61
62 #include "opt_platform.h"
63
64 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/bus.h>
67 #include <sys/clock.h>
68 #include <sys/kernel.h>
69 #include <sys/module.h>
70
71 #include <dev/iicbus/iicbus.h>
72 #include <dev/iicbus/iiconf.h>
73
74 #ifdef FDT
75 #include <dev/ofw/openfirm.h>
76 #include <dev/ofw/ofw_bus.h>
77 #include <dev/ofw/ofw_bus_subr.h>
78 #endif
79
80 #include "clock_if.h"
81 #include "iicbus_if.h"
82
83 #define S390_DEVNAME            "s35390a_rtc"
84 #define S390_DEVCODE            0x6     /* 0110 */
85 /*
86  * S-35390A uses 4-bit device code + 3-bit command in the slave address
87  * field.  The possible combination is 0x60-0x6f including the R/W bit.
88  * 0x60 means an write access to status register 1.
89  */
90 #define S390_ADDR               (S390_DEVCODE << 4)
91
92 /* Registers are encoded into the slave address */
93 #define S390_STATUS1            (0 << 1)
94 #define S390_STATUS2            (1 << 1)
95 #define S390_REALTIME1          (2 << 1)
96 #define S390_REALTIME2          (3 << 1)
97 #define S390_INT1_1             (4 << 1)
98 #define S390_INT1_2             (5 << 1)
99 #define S390_CLOCKADJ           (6 << 1)
100 #define S390_FREE               (7 << 1)
101
102 /* Status1 bits */
103 #define S390_ST1_POC            (1 << 7)
104 #define S390_ST1_BLD            (1 << 6)
105 #define S390_ST1_24H            (1 << 1)
106 #define S390_ST1_RESET          (1 << 0)
107
108 /* Status2 bits */
109 #define S390_ST2_TEST           (1 << 7)
110
111 /* Realtime1 data bytes */
112 #define S390_RT1_NBYTES         7
113 #define S390_RT1_YEAR           0
114 #define S390_RT1_MONTH          1
115 #define S390_RT1_DAY            2
116 #define S390_RT1_WDAY           3
117 #define S390_RT1_HOUR           4
118 #define S390_RT1_MINUTE         5
119 #define S390_RT1_SECOND         6
120
121 struct s390rtc_softc {
122         device_t        sc_dev;
123         uint16_t        sc_addr;
124 };
125
126 /*
127  * S-35390A interprets bits in each byte on SDA in reverse order.
128  * bitreverse() reverses the bits in uint8_t.
129  */
130 static const uint8_t nibbletab[] = {
131         /* 0x0 0000 -> 0000 */  0x0,
132         /* 0x1 0001 -> 1000 */  0x8,
133         /* 0x2 0010 -> 0100 */  0x4,
134         /* 0x3 0011 -> 1100 */  0xc,
135         /* 0x4 0100 -> 0010 */  0x2,
136         /* 0x5 0101 -> 1010 */  0xa,
137         /* 0x6 0110 -> 0110 */  0x6,
138         /* 0x7 0111 -> 1110 */  0xe,
139         /* 0x8 1000 -> 0001 */  0x1,
140         /* 0x9 1001 -> 1001 */  0x9,
141         /* 0xa 1010 -> 0101 */  0x5,
142         /* 0xb 1011 -> 1101 */  0xd,
143         /* 0xc 1100 -> 0011 */  0x3,
144         /* 0xd 1101 -> 1011 */  0xb,
145         /* 0xe 1110 -> 0111 */  0x7,
146         /* 0xf 1111 -> 1111 */  0xf, };
147
148 static uint8_t
149 bitreverse(uint8_t x)
150 {
151
152         return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
153 }
154
155 static int
156 s390rtc_read(device_t dev, uint8_t reg, uint8_t *buf, size_t len)
157 {
158         struct s390rtc_softc *sc = device_get_softc(dev);
159         struct iic_msg msg[] = {
160                 {
161                         .slave = sc->sc_addr | reg,
162                         .flags = IIC_M_RD,
163                         .len = len,
164                         .buf = buf,
165                 },
166         };
167         int i;
168         int error;
169
170         error = iicbus_transfer_excl(dev, msg, 1, IIC_WAIT);
171         if (error)
172                 return (error);
173
174         /* this chip returns each byte in reverse order */
175         for (i = 0; i < len; ++i)
176                 buf[i] = bitreverse(buf[i]);
177
178         return (0);
179 }
180
181 static int
182 s390rtc_write(device_t dev, uint8_t reg, uint8_t *buf, size_t len)
183 {
184         struct s390rtc_softc *sc = device_get_softc(dev);
185         struct iic_msg msg[] = {
186                 {
187                         .slave = sc->sc_addr | reg,
188                         .flags = IIC_M_WR,
189                         .len = len,
190                         .buf = buf,
191                 },
192         };
193         int i;
194
195         /* this chip expects each byte in reverse order */
196         for (i = 0; i < len; ++i)
197                 buf[i] = bitreverse(buf[i]);
198
199         return (iicbus_transfer_excl(dev, msg, 1, IIC_WAIT));
200 }
201
202 static int
203 s390rtc_probe(device_t dev)
204 {
205
206 #ifdef FDT
207         if (!ofw_bus_status_okay(dev))
208                 return (ENXIO);
209
210         if (!ofw_bus_is_compatible(dev, "sii,s35390a"))
211                 return (ENXIO);
212 #else
213         if (iicbus_get_addr(dev) != S390_ADDR) {
214                 if (bootverbose)
215                         device_printf(dev, "slave address mismatch. "
216                             "(%02x != %02x)\n", iicbus_get_addr(dev),
217                             S390_ADDR);
218                 return (ENXIO);
219         }
220 #endif
221         device_set_desc(dev, "Seiko Instruments S-35390A RTC");
222
223         return (BUS_PROBE_DEFAULT);
224 }
225
226 static void
227 s390rtc_start(void *arg)
228 {
229         device_t dev;
230         uint8_t reg;
231         int error;
232
233         dev = arg;
234
235         /* Reset the chip and turn on 24h mode, after power-off or battery. */
236         error = s390rtc_read(dev, S390_STATUS1, &reg, 1);
237         if (error) {
238                 device_printf(dev, "%s: cannot read status1 register\n",
239                      __func__);
240                 return;
241         }
242         if (reg & (S390_ST1_POC | S390_ST1_BLD)) {
243                 reg |= S390_ST1_24H | S390_ST1_RESET;
244                 error = s390rtc_write(dev, S390_STATUS1, &reg, 1);
245                 if (error) {
246                         device_printf(dev,
247                             "%s: cannot initialize\n", __func__);
248                         return;
249                 }
250         }
251
252         /* Disable the test mode, when enabled. */
253         error = s390rtc_read(dev, S390_STATUS2, &reg, 1);
254         if (error) {
255                 device_printf(dev, "%s: cannot read status2 register\n",
256                     __func__);
257                 return;
258         }
259         if (reg & S390_ST2_TEST) {
260                 reg &= ~S390_ST2_TEST;
261                 error = s390rtc_write(dev, S390_STATUS2, &reg, 1);
262                 if (error) {
263                         device_printf(dev,
264                             "%s: cannot disable the test mode\n", __func__);
265                         return;
266                 }
267         }
268
269         clock_register(dev, 1000000);   /* 1 second resolution */
270 }
271
272 static int
273 s390rtc_attach(device_t dev)
274 {
275         struct s390rtc_softc *sc;
276
277         sc = device_get_softc(dev);
278         sc->sc_dev = dev;
279         sc->sc_addr = iicbus_get_addr(dev);
280
281         config_intrhook_oneshot(s390rtc_start, dev);
282
283         return (0);
284 }
285
286 static int
287 s390rtc_detach(device_t dev)
288 {
289
290         clock_unregister(dev);
291         return (0);
292 }
293
294 static int
295 s390rtc_gettime(device_t dev, struct timespec *ts)
296 {
297         uint8_t bcd[S390_RT1_NBYTES];
298         struct bcd_clocktime bct;
299         int error;
300
301         error = s390rtc_read(dev, S390_REALTIME1, bcd, S390_RT1_NBYTES);
302         if (error) {
303                 device_printf(dev, "%s: cannot read realtime1 register\n",
304                     __func__);
305                 return (error);
306         }
307
308         /*
309          * Convert the register values into something useable.
310          */
311         bct.nsec = 0;
312         bct.sec  = bcd[S390_RT1_SECOND];
313         bct.min  = bcd[S390_RT1_MINUTE];
314         bct.hour = bcd[S390_RT1_HOUR] & 0x3f;
315         bct.day  = bcd[S390_RT1_DAY];
316         bct.dow  = bcd[S390_RT1_WDAY] & 0x07;
317         bct.mon  = bcd[S390_RT1_MONTH];
318         bct.year = bcd[S390_RT1_YEAR];
319
320         clock_dbgprint_bcd(dev, CLOCK_DBG_READ, &bct); 
321         return (clock_bcd_to_ts(&bct, ts, false));
322 }
323
324 static int
325 s390rtc_settime(device_t dev, struct timespec *ts)
326 {
327         uint8_t bcd[S390_RT1_NBYTES];
328         struct bcd_clocktime bct;
329
330         clock_ts_to_bcd(ts, &bct, false);
331         clock_dbgprint_bcd(dev, CLOCK_DBG_WRITE, &bct); 
332
333         /*
334          * Convert our time representation into something the S-xx390
335          * can understand.
336          */
337         bcd[S390_RT1_SECOND] = bct.sec;
338         bcd[S390_RT1_MINUTE] = bct.min;
339         bcd[S390_RT1_HOUR]   = bct.hour;
340         bcd[S390_RT1_DAY]    = bct.day;
341         bcd[S390_RT1_WDAY]   = bct.dow;
342         bcd[S390_RT1_MONTH]  = bct.mon;
343         bcd[S390_RT1_YEAR]   = bct.year & 0xff;
344
345         return (s390rtc_write(dev, S390_REALTIME1, bcd, S390_RT1_NBYTES));
346 }
347
348 static device_method_t s390rtc_methods[] = {
349         DEVMETHOD(device_probe,         s390rtc_probe),
350         DEVMETHOD(device_attach,        s390rtc_attach),
351         DEVMETHOD(device_detach,        s390rtc_detach),
352
353         DEVMETHOD(clock_gettime,        s390rtc_gettime),
354         DEVMETHOD(clock_settime,        s390rtc_settime),
355
356         DEVMETHOD_END
357 };
358
359 static driver_t s390rtc_driver = {
360         S390_DEVNAME,
361         s390rtc_methods,
362         sizeof(struct s390rtc_softc),
363 };
364 static devclass_t s390rtc_devclass;
365
366 DRIVER_MODULE(s35390a, iicbus, s390rtc_driver, s390rtc_devclass, NULL, NULL);
367 MODULE_VERSION(s35390a, 1);
368 MODULE_DEPEND(s35390a, iicbus, 1, 1, 1);