1 /* $OpenBSD: mvortc.c,v 1.1 2023/03/02 09:57:43 jmatthew Exp $ */
2 /*
3 * Copyright (c) 2022 Jonathan Matthew <jmatthew@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/fdt.h>
28
29 #include <dev/clock_subr.h>
30
31 extern todr_chip_handle_t todr_handle;
32
33 /* Registers. */
34 #define RTC_STATUS 0x0000
35 #define RTC_TIME 0x000c
36 #define RTC_CONF_TEST 0x001c
37
38 #define RTC_TIMING_CTL 0x0000
39 #define RTC_PERIOD_SHIFT 0
40 #define RTC_PERIOD_MASK (0x3ff << RTC_PERIOD_SHIFT)
41 #define RTC_READ_DELAY_SHIFT 26
42 #define RTC_READ_DELAY_MASK (0x1f << RTC_READ_DELAY_SHIFT)
43
44
45 #define HREAD4(sc, reg) \
46 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
47 #define HWRITE4(sc, reg, val) \
48 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
49
50 struct mvortc_softc {
51 struct device sc_dev;
52 bus_space_tag_t sc_iot;
53 bus_space_handle_t sc_ioh;
54 bus_space_handle_t sc_soc_ioh;
55
56 struct todr_chip_handle sc_todr;
57 };
58
59 int mvortc_match(struct device *, void *, void *);
60 void mvortc_attach(struct device *, struct device *, void *);
61
62 const struct cfattach mvortc_ca = {
63 sizeof (struct mvortc_softc), mvortc_match, mvortc_attach
64 };
65
66 struct cfdriver mvortc_cd = {
67 NULL, "mvortc", DV_DULL
68 };
69
70 int mvortc_gettime(struct todr_chip_handle *, struct timeval *);
71 int mvortc_settime(struct todr_chip_handle *, struct timeval *);
72
73 int
mvortc_match(struct device * parent,void * match,void * aux)74 mvortc_match(struct device *parent, void *match, void *aux)
75 {
76 struct fdt_attach_args *faa = aux;
77
78 return OF_is_compatible(faa->fa_node, "marvell,armada-380-rtc");
79 }
80
81 void
mvortc_attach(struct device * parent,struct device * self,void * aux)82 mvortc_attach(struct device *parent, struct device *self, void *aux)
83 {
84 struct mvortc_softc *sc = (struct mvortc_softc *)self;
85 struct fdt_attach_args *faa = aux;
86 uint32_t reg;
87
88 if (faa->fa_nreg < 2) {
89 printf(": no registers\n");
90 return;
91 }
92
93 sc->sc_iot = faa->fa_iot;
94
95 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
96 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
97 printf(": can't map registers\n");
98 return;
99 }
100
101 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
102 faa->fa_reg[1].size, 0, &sc->sc_soc_ioh)) {
103 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
104 printf(": can't map soc registers\n");
105 return;
106 }
107
108 /* Magic to make bus access actually work. */
109 reg = bus_space_read_4(sc->sc_iot, sc->sc_soc_ioh, RTC_TIMING_CTL);
110 reg &= ~RTC_PERIOD_MASK;
111 reg |= (0x3ff << RTC_PERIOD_SHIFT);
112 reg &= ~RTC_READ_DELAY_MASK;
113 reg |= (0x1f << RTC_READ_DELAY_SHIFT);
114 bus_space_write_4(sc->sc_iot, sc->sc_soc_ioh, RTC_TIMING_CTL, reg);
115 printf("\n");
116
117 sc->sc_todr.cookie = sc;
118 sc->sc_todr.todr_gettime = mvortc_gettime;
119 sc->sc_todr.todr_settime = mvortc_settime;
120 todr_handle = &sc->sc_todr;
121 }
122
123 uint32_t
mvortc_read(struct mvortc_softc * sc,int reg)124 mvortc_read(struct mvortc_softc *sc, int reg)
125 {
126 uint32_t sample, mode;
127 uint32_t samples[100];
128 int counts[100];
129 int i, j, last;
130
131 memset(samples, 0, sizeof(samples));
132 memset(counts, 0, sizeof(counts));
133 last = 0;
134 for (i = 0; i < nitems(samples); i++) {
135 sample = HREAD4(sc, reg);
136
137 for (j = 0; j < last; j++) {
138 if (samples[j] == sample)
139 break;
140 }
141
142 if (j < last) {
143 counts[j]++;
144 } else {
145 samples[last] = sample;
146 counts[last] = 1;
147 last++;
148 }
149 }
150
151 j = 0;
152 mode = 0;
153 for (i = 0; i < last; i++) {
154 if (counts[i] > mode) {
155 mode = counts[i];
156 j = i;
157 }
158 }
159
160 return samples[j];
161 }
162
163 void
mvortc_write(struct mvortc_softc * sc,int reg,uint32_t val)164 mvortc_write(struct mvortc_softc *sc, int reg, uint32_t val)
165 {
166 HWRITE4(sc, RTC_STATUS, 0);
167 HWRITE4(sc, RTC_STATUS, 0);
168 HWRITE4(sc, reg, val);
169 delay(5);
170 }
171
172 int
mvortc_gettime(struct todr_chip_handle * handle,struct timeval * tv)173 mvortc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
174 {
175 struct mvortc_softc *sc = handle->cookie;
176
177 tv->tv_sec = mvortc_read(sc, RTC_TIME);
178 tv->tv_usec = 0;
179 return 0;
180 }
181
182 int
mvortc_settime(struct todr_chip_handle * handle,struct timeval * tv)183 mvortc_settime(struct todr_chip_handle *handle, struct timeval *tv)
184 {
185 struct mvortc_softc *sc = handle->cookie;
186 uint32_t reg;
187
188 reg = mvortc_read(sc, RTC_CONF_TEST);
189 if (reg & 0xff) {
190 mvortc_write(sc, RTC_CONF_TEST, 0);
191 delay(500);
192 mvortc_write(sc, RTC_TIME, 0);
193 mvortc_write(sc, RTC_STATUS, 0x03);
194 }
195
196 mvortc_write(sc, RTC_TIME, tv->tv_sec);
197 return 0;
198 }
199