]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/freescale/imx/imx6_snvs.c
MFHead @348740
[FreeBSD/FreeBSD.git] / sys / arm / freescale / imx / imx6_snvs.c
1 /*-
2  * Copyright (c) 2017 Ian Lepore <ian@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 imx6 Secure Non-Volatile Storage system, which really means "all
32  * the stuff that's powered by a battery when main power is off".  This includes
33  * realtime clock, tamper monitor, and power-management functions.  Currently
34  * this driver provides only realtime clock support.
35  */
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 #include <machine/bus.h>
44
45 #include <dev/ofw/ofw_bus_subr.h>
46
47 #include "clock_if.h"
48
49 #define SNVS_LPCR               0x38            /* Control register */
50 #define   LPCR_LPCALB_VAL_SHIFT   10            /* Calibration shift */
51 #define   LPCR_LPCALB_VAL_MASK    0x1f          /* Calibration mask */
52 #define   LPCR_LPCALB_EN          (1u << 8)     /* Calibration enable */
53 #define   LPCR_SRTC_ENV           (1u << 0)     /* RTC enabled/valid */
54
55 #define SNVS_LPSRTCMR           0x50            /* Counter MSB */
56 #define SNVS_LPSRTCLR           0x54            /* Counter LSB */
57
58 #define RTC_RESOLUTION_US       (1000000 / 32768) /* 32khz clock */
59
60 /*
61  * The RTC is a 47-bit counter clocked at 32KHz and organized as a 32.15
62  * fixed-point binary value.  Shifting by SBT_LSB bits translates between
63  * counter and sbintime values.
64  */
65 #define RTC_BITS        47
66 #define SBT_BITS        64
67 #define SBT_LSB         (SBT_BITS - RTC_BITS)
68
69 struct snvs_softc {
70         device_t                dev;
71         struct resource *       memres;
72         uint32_t                lpcr;
73 };
74
75 static struct ofw_compat_data compat_data[] = {
76         {"fsl,sec-v4.0-mon-rtc-lp", true},
77         {"fsl,sec-v4.0-mon", true},
78         {NULL,               false}
79 };
80
81 static inline uint32_t
82 RD4(struct snvs_softc *sc, bus_size_t offset)
83 {
84
85         return (bus_read_4(sc->memres, offset));
86 }
87
88 static inline void
89 WR4(struct snvs_softc *sc, bus_size_t offset, uint32_t value)
90 {
91
92         bus_write_4(sc->memres, offset, value);
93 }
94
95 static void
96 snvs_rtc_enable(struct snvs_softc *sc, bool enable)
97 {
98         uint32_t enbit;
99
100         if (enable)
101                 sc->lpcr |= LPCR_SRTC_ENV;
102         else
103                 sc->lpcr &= ~LPCR_SRTC_ENV;
104         WR4(sc, SNVS_LPCR, sc->lpcr);
105
106         /* Wait for the hardware to achieve the requested state. */
107         enbit = sc->lpcr & LPCR_SRTC_ENV;
108         while ((RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV) != enbit)
109                 continue;
110 }
111
112 static int
113 snvs_gettime(device_t dev, struct timespec *ts)
114 {
115         struct snvs_softc *sc;
116         sbintime_t counter1, counter2;
117
118         sc = device_get_softc(dev);
119
120         /* If the clock is not enabled and valid, we can't help. */
121         if (!(RD4(sc, SNVS_LPCR) & LPCR_SRTC_ENV)) {
122                 return (EINVAL);
123         }
124
125         /*
126          * The counter is clocked asynchronously to cpu accesses; read and
127          * assemble the pieces of the counter until we get the same value twice.
128          * The counter is 47 bits, organized as a 32.15 binary fixed-point
129          * value. If we shift it up to the high order part of a 64-bit word it
130          * turns into an sbintime.
131          */
132         do {
133                 counter1  = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);
134                 counter1 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);
135                 counter2  = (uint64_t)RD4(sc, SNVS_LPSRTCMR) << (SBT_LSB + 32);
136                 counter2 |= (uint64_t)RD4(sc, SNVS_LPSRTCLR) << (SBT_LSB);
137         } while (counter1 != counter2);
138
139         *ts = sbttots(counter1);
140
141         clock_dbgprint_ts(sc->dev, CLOCK_DBG_READ, ts); 
142
143         return (0);
144 }
145
146 static int
147 snvs_settime(device_t dev, struct timespec *ts)
148 {
149         struct snvs_softc *sc;
150         sbintime_t sbt;
151
152         sc = device_get_softc(dev);
153
154         /*
155          * The hardware format is the same as sbt (with fewer fractional bits),
156          * so first convert the time to sbt.  It takes two clock cycles for the
157          * counter to start after setting the enable bit, so add two SBT_LSBs to
158          * what we're about to set.
159          */
160         sbt = tstosbt(*ts);
161         sbt += 2 << SBT_LSB;
162         snvs_rtc_enable(sc, false);
163         WR4(sc, SNVS_LPSRTCMR, (uint32_t)(sbt >> (SBT_LSB + 32)));
164         WR4(sc, SNVS_LPSRTCLR, (uint32_t)(sbt >> (SBT_LSB)));
165         snvs_rtc_enable(sc, true);
166
167         clock_dbgprint_ts(sc->dev, CLOCK_DBG_WRITE, ts); 
168
169         return (0);
170 }
171
172 static int
173 snvs_probe(device_t dev)
174 {
175
176         if (!ofw_bus_status_okay(dev))
177                 return (ENXIO);
178
179         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
180                 return (ENXIO);
181
182         device_set_desc(dev, "i.MX6 SNVS RTC");
183         return (BUS_PROBE_DEFAULT);
184 }
185
186 static int
187 snvs_attach(device_t dev)
188 {
189         struct snvs_softc *sc;
190         int rid;
191
192         sc = device_get_softc(dev);
193         sc->dev = dev;
194
195         rid = 0;
196         sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid,
197             RF_ACTIVE);
198         if (sc->memres == NULL) {
199                 device_printf(sc->dev, "could not allocate registers\n");
200                 return (ENXIO);
201         }
202
203         clock_register(sc->dev, RTC_RESOLUTION_US);
204
205         return (0);
206 }
207
208 static int
209 snvs_detach(device_t dev)
210 {
211         struct snvs_softc *sc;
212
213         sc = device_get_softc(dev);
214         clock_unregister(sc->dev);
215         bus_release_resource(sc->dev, SYS_RES_MEMORY, 0, sc->memres);
216         return (0);
217 }
218
219 static device_method_t snvs_methods[] = {
220         DEVMETHOD(device_probe,         snvs_probe),
221         DEVMETHOD(device_attach,        snvs_attach),
222         DEVMETHOD(device_detach,        snvs_detach),
223
224         /* clock_if methods */
225         DEVMETHOD(clock_gettime,        snvs_gettime),
226         DEVMETHOD(clock_settime,        snvs_settime),
227
228         DEVMETHOD_END
229 };
230
231 static driver_t snvs_driver = {
232         "snvs",
233         snvs_methods,
234         sizeof(struct snvs_softc),
235 };
236
237 static devclass_t snvs_devclass;
238
239 DRIVER_MODULE(snvs, simplebus, snvs_driver, snvs_devclass, 0, 0);
240 SIMPLEBUS_PNP_INFO(compat_data);