1 /* $NetBSD: mkclock.c,v 1.20 2021/01/24 07:36:54 mrg Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * time-of-day clock driver for sparc machines with the MOSTEK MK48Txx.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: mkclock.c,v 1.20 2021/01/24 07:36:54 mrg Exp $");
38
39 #include "opt_sparc_arch.h"
40
41 #include <sys/param.h>
42 #include <sys/kernel.h>
43 #include <sys/device.h>
44 #include <sys/systm.h>
45
46 #include <uvm/uvm_extern.h>
47
48 #include <sys/bus.h>
49 #include <machine/autoconf.h>
50 #include <machine/eeprom.h>
51 #include <machine/promlib.h>
52 #include <machine/cpu.h>
53
54 #include <sparc/dev/bootbusvar.h>
55 #include <sparc/sparc/timervar.h>
56
57 #include <dev/clock_subr.h>
58 #include <dev/ic/mk48txxreg.h>
59 #include <dev/ic/mk48txxvar.h>
60
61 /* Location and size of the MK48xx TOD clock, if present */
62 static bus_space_handle_t mk_nvram_base;
63 static bus_size_t mk_nvram_size;
64
65 static int mk_clk_wenable(todr_chip_handle_t, int);
66 static int mk_nvram_wenable(int);
67
68 static int clockmatch_mainbus(device_t, cfdata_t, void *);
69 static int clockmatch_obio(device_t, cfdata_t, void *);
70 static int clockmatch_bootbus(device_t, cfdata_t, void *);
71 static void clockattach_mainbus(device_t, device_t, void *);
72 static void clockattach_obio(device_t, device_t, void *);
73 static void clockattach_bootbus(device_t, device_t, void *);
74
75 static void clockattach(struct mk48txx_softc *, int);
76
77 CFATTACH_DECL_NEW(clock_mainbus, sizeof(struct mk48txx_softc),
78 clockmatch_mainbus, clockattach_mainbus, NULL, NULL);
79
80 CFATTACH_DECL_NEW(clock_obio, sizeof(struct mk48txx_softc),
81 clockmatch_obio, clockattach_obio, NULL, NULL);
82
83 CFATTACH_DECL_NEW(clock_bootbus, sizeof(struct mk48txx_softc),
84 clockmatch_bootbus, clockattach_bootbus, NULL, NULL);
85
86
87 /*
88 * The OPENPROM calls the clock the "eeprom", so we have to have our
89 * own special match function to call it the "clock".
90 */
91 static int
clockmatch_mainbus(device_t parent,cfdata_t cf,void * aux)92 clockmatch_mainbus(device_t parent, cfdata_t cf, void *aux)
93 {
94 struct mainbus_attach_args *ma = aux;
95
96 return (strcmp("eeprom", ma->ma_name) == 0);
97 }
98
99 static int
clockmatch_obio(device_t parent,cfdata_t cf,void * aux)100 clockmatch_obio(device_t parent, cfdata_t cf, void *aux)
101 {
102 union obio_attach_args *uoba = aux;
103 struct obio4_attach_args *oba;
104
105 if (uoba->uoba_isobio4 == 0)
106 return (strcmp("eeprom", uoba->uoba_sbus.sa_name) == 0);
107
108 if (!CPU_ISSUN4) {
109 printf("clockmatch_obio: attach args mixed up\n");
110 return (0);
111 }
112
113 /* Only these sun4s have "clock" (others have "oclock") */
114 if (cpuinfo.cpu_type != CPUTYP_4_300 &&
115 cpuinfo.cpu_type != CPUTYP_4_400)
116 return (0);
117
118 /* Make sure there is something there */
119 oba = &uoba->uoba_oba4;
120 return (bus_space_probe(oba->oba_bustag, oba->oba_paddr,
121 1, /* probe size */
122 0, /* offset */
123 0, /* flags */
124 NULL, NULL));
125 }
126
127 static int
clockmatch_bootbus(device_t parent,cfdata_t cf,void * aux)128 clockmatch_bootbus(device_t parent, cfdata_t cf, void *aux)
129 {
130 struct bootbus_attach_args *baa = aux;
131
132 return (strcmp("eeprom", baa->ba_name) == 0);
133 }
134
135 /* ARGSUSED */
136 static void
clockattach_mainbus(device_t parent,device_t self,void * aux)137 clockattach_mainbus(device_t parent, device_t self, void *aux)
138 {
139 struct mk48txx_softc *sc = device_private(self);
140 struct mainbus_attach_args *ma = aux;
141
142 sc->sc_dev = self;
143 sc->sc_bst = ma->ma_bustag;
144
145 /*
146 * We ignore any existing virtual address as we need to map
147 * this read-only and make it read-write only temporarily,
148 * whenever we read or write the clock chip. The clock also
149 * contains the ID ``PROM'', and I have already had the pleasure
150 * of reloading the CPU type, Ethernet address, etc, by hand from
151 * the console FORTH interpreter. I intend not to enjoy it again.
152 */
153 if (bus_space_map(sc->sc_bst,
154 ma->ma_paddr,
155 ma->ma_size,
156 BUS_SPACE_MAP_LINEAR,
157 &sc->sc_bsh) != 0) {
158 aprint_error(": can't map register\n");
159 return;
160 }
161
162 clockattach(sc, ma->ma_node);
163 }
164
165 static void
clockattach_obio(device_t parent,device_t self,void * aux)166 clockattach_obio(device_t parent, device_t self, void *aux)
167 {
168 struct mk48txx_softc *sc = device_private(self);
169 union obio_attach_args *uoba = aux;
170 int node;
171
172 if (uoba->uoba_isobio4 == 0) {
173 /* sun4m clock at obio */
174 struct sbus_attach_args *sa = &uoba->uoba_sbus;
175
176 node = sa->sa_node;
177 sc->sc_bst = sa->sa_bustag;
178 if (sbus_bus_map(sc->sc_bst,
179 sa->sa_slot, sa->sa_offset, sa->sa_size,
180 BUS_SPACE_MAP_LINEAR, &sc->sc_bsh) != 0) {
181 aprint_error(": can't map register\n");
182 return;
183 }
184 } else {
185 /* sun4 clock at obio */
186 struct obio4_attach_args *oba = &uoba->uoba_oba4;
187
188 /*
189 * Sun4's only have mk48t02 clocks, so we hard-code
190 * the device address space length to 2048.
191 */
192 node = 0;
193 sc->sc_bst = oba->oba_bustag;
194 if (bus_space_map(sc->sc_bst,
195 oba->oba_paddr,
196 2048, /* size */
197 BUS_SPACE_MAP_LINEAR, /* flags */
198 &sc->sc_bsh) != 0) {
199 aprint_error(": can't map register\n");
200 return;
201 }
202 }
203
204 clockattach(sc, node);
205 }
206
207 static void
clockattach_bootbus(device_t parent,device_t self,void * aux)208 clockattach_bootbus(device_t parent, device_t self, void *aux)
209 {
210 struct mk48txx_softc *sc = device_private(self);
211 struct bootbus_attach_args *baa = aux;
212 sc->sc_bst = baa->ba_bustag;
213
214 if (bus_space_map(sc->sc_bst,
215 BUS_ADDR(baa->ba_reg[0].oa_space,
216 baa->ba_reg[0].oa_base),
217 baa->ba_reg[0].oa_size,
218 BUS_SPACE_MAP_LINEAR,
219 &sc->sc_bsh) != 0) {
220 aprint_error(": can't map register\n");
221 return;
222 }
223
224 clockattach(sc, baa->ba_node);
225 }
226
227 static void
clockattach(struct mk48txx_softc * sc,int node)228 clockattach(struct mk48txx_softc *sc, int node)
229 {
230
231 if (CPU_ISSUN4)
232 sc->sc_model = "mk48t02"; /* Hard-coded sun4 clock */
233 else if (node != 0)
234 sc->sc_model = prom_getpropstring(node, "model");
235 else
236 panic("clockattach: node == 0");
237
238 /* Our TOD clock year 0 represents 1968 */
239 sc->sc_year0 = 1968;
240 mk48txx_attach(sc);
241
242 aprint_normal("\n");
243
244 /*
245 * Store NVRAM base address and size in globals for use
246 * by mk_nvram_wenable().
247 */
248 mk_nvram_base = sc->sc_bsh;
249 if (mk48txx_get_nvram_size(&sc->sc_handle, &mk_nvram_size) != 0)
250 panic("Cannot get nvram size on %s", sc->sc_model);
251
252 /* Establish clock write-enable method */
253 sc->sc_handle.todr_setwen = mk_clk_wenable;
254
255 #if defined(SUN4)
256 if (CPU_ISSUN4) {
257 if (cpuinfo.cpu_type == CPUTYP_4_300 ||
258 cpuinfo.cpu_type == CPUTYP_4_400) {
259 eeprom_va = bus_space_vaddr(sc->sc_bst, sc->sc_bsh);
260 eeprom_nvram_wenable = mk_nvram_wenable;
261 }
262 }
263 #endif
264 }
265
266 /*
267 * Write en/dis-able TOD clock registers. This is a safety net to
268 * save idprom (part of mk48txx TOD clock) from being accidentally
269 * stomped on by a buggy code. We coordinate so that several writers
270 * can run simultaneously.
271 */
272 int
mk_clk_wenable(todr_chip_handle_t handle,int onoff)273 mk_clk_wenable(todr_chip_handle_t handle, int onoff)
274 {
275
276 /* XXX - we ignore `handle' here... */
277 return (mk_nvram_wenable(onoff));
278 }
279
280 int
mk_nvram_wenable(int onoff)281 mk_nvram_wenable(int onoff)
282 {
283 int s;
284 vm_prot_t prot;/* nonzero => change prot */
285 vaddr_t base;
286 vsize_t size;
287 static int writers;
288
289 s = splhigh();
290 if (onoff)
291 prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0;
292 else
293 prot = --writers == 0 ? VM_PROT_READ : 0;
294 splx(s);
295
296 size = round_page((vsize_t)mk_nvram_size);
297 base = trunc_page((vaddr_t)mk_nvram_base);
298 if (prot)
299 pmap_kprotect(base, size, prot);
300
301 return (0);
302 }
303