xref: /openbsd/sys/arch/hppa/hppa/autoconf.c (revision 3ad05d8a)
1 /*	$OpenBSD: autoconf.c,v 1.63 2022/09/02 20:06:55 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 1998-2003 Michael Shalayeff
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * All advertising materials mentioning features or use of this software
13  * must display the following acknowledgement:
14  *	This product includes software developed by the University of
15  *	California, Lawrence Berkeley Laboratory.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *	@(#)autoconf.c	8.4 (Berkeley) 10/1/93
42  */
43 
44 #include "pci.h"
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/buf.h>
49 #include <sys/disklabel.h>
50 #include <sys/conf.h>
51 #include <sys/reboot.h>
52 #include <sys/device.h>
53 #include <sys/timeout.h>
54 
55 #include <uvm/uvm_extern.h>
56 
57 #include <machine/iomod.h>
58 #include <machine/autoconf.h>
59 
60 #include <dev/cons.h>
61 
62 #include <hppa/dev/cpudevs.h>
63 
64 #if NPCI > 0
65 #include <dev/pci/pcivar.h>
66 #include <dev/pci/pcireg.h>
67 #endif
68 
69 /* device we booted from */
70 struct device *bootdv;
71 void	dumpconf(void);
72 
73 void (*cold_hook)(int); /* see below */
74 
75 /*
76  * LED blinking thing
77  */
78 #ifdef USELEDS
79 #include <sys/kernel.h>
80 
81 struct timeout heartbeat_tmo;
82 void heartbeat(void *);
83 #endif
84 
85 #include "cd.h"
86 #include "sd.h"
87 #include "st.h"
88 #include "mpath.h"
89 
90 #include <scsi/scsi_all.h>
91 #include <scsi/scsiconf.h>
92 #if NMPATH > 0
93 #include <scsi/mpathvar.h>
94 #endif
95 
96 #ifdef USELEDS
97 /*
98  * turn the heartbeat alive.
99  * right thing would be to pass counter to each subsequent timeout
100  * as an argument to heartbeat() incrementing every turn,
101  * i.e. avoiding the static hbcnt, but doing timeout_set() on each
102  * timeout_add() sounds ugly, guts of struct timeout looks ugly
103  * to ponder in even more.
104  */
105 void
heartbeat(v)106 heartbeat(v)
107 	void *v;
108 {
109 	static u_int hbcnt = 0, ocp_total, ocp_idle;
110 	int toggle, cp_mask, cp_total, cp_idle;
111 	struct schedstate_percpu *spc = &(curcpu()->ci_schedstate);
112 
113 	timeout_add(&heartbeat_tmo, hz / 16);
114 
115 	cp_idle = spc->spc_cp_time[CP_IDLE];
116 	cp_total = spc->spc_cp_time[CP_USER] + spc->spc_cp_time[CP_NICE] +
117 	    spc->spc_cp_time[CP_SYS] + spc->spc_cp_time[CP_INTR] +
118 	    spc->spc_cp_time[CP_IDLE];
119 	if (cp_total == ocp_total)
120 		cp_total = ocp_total + 1;
121 	if (cp_idle == ocp_idle)
122 		cp_idle = ocp_idle + 1;
123 	cp_mask = 0xf0 >> (cp_idle - ocp_idle) * 4 / (cp_total - ocp_total);
124 	cp_mask &= 0xf0;
125 	ocp_total = cp_total;
126 	ocp_idle = cp_idle;
127 	/*
128 	 * do this:
129 	 *
130 	 *   |~| |~|
131 	 *  _| |_| |_,_,_,_
132 	 *   0 1 2 3 4 6 7
133 	 */
134 	toggle = 0;
135 	if (hbcnt++ < 8 && hbcnt & 1)
136 		toggle = PALED_HEARTBEAT;
137 	hbcnt &= 15;
138 	ledctl(cp_mask,
139 	    (~cp_mask & 0xf0) | PALED_NETRCV | PALED_NETSND | PALED_DISK,
140 	    toggle);
141 }
142 #endif
143 
144 /*
145  * This is called by configure to set dumplo and dumpsize.
146  * Dumps always skip the first CLBYTES of disk space
147  * in case there might be a disk label stored there.
148  * If there is extra space, put dump at the end to
149  * reduce the chance that swapping trashes it.
150  */
151 void
dumpconf(void)152 dumpconf(void)
153 {
154 	extern int dumpsize;
155 	int nblks, dumpblks;	/* size of dump area */
156 
157 	if (dumpdev == NODEV ||
158 	    (nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
159 		return;
160 	if (nblks <= ctod(1))
161 		return;
162 
163 	dumpblks = cpu_dumpsize();
164 	if (dumpblks < 0)
165 		return;
166 	dumpblks += ctod(physmem);
167 
168 	/* If dump won't fit (incl. room for possible label), punt. */
169 	if (dumpblks > (nblks - ctod(1)))
170 		return;
171 
172 	/* Put dump at end of partition */
173 	dumplo = nblks - dumpblks;
174 
175 	/* dumpsize is in page units, and doesn't include headers. */
176 	dumpsize = physmem;
177 }
178 
179 void	print_devpath(const char *label, struct pz_device *pz);
180 
181 void
print_devpath(const char * label,struct pz_device * pz)182 print_devpath(const char *label, struct pz_device *pz)
183 {
184 	int i;
185 
186 	printf("%s: ", label);
187 
188 	for (i = 0; i < 6; i++)
189 		if (pz->pz_bc[i] >= 0)
190 			printf("%d/", pz->pz_bc[i]);
191 
192 	printf("%d.%x", pz->pz_mod, pz->pz_layers[0]);
193 	for (i = 1; i < 6 && pz->pz_layers[i]; i++)
194 		printf(".%x", pz->pz_layers[i]);
195 
196 	printf(" class=%d flags=%b hpa=0x%x spa=0x%x io=0x%x\n", pz->pz_class,
197 	    pz->pz_flags, PZF_BITS, pz->pz_hpa, pz->pz_spa, pz->pz_iodc_io);
198 }
199 
200 struct pdc_memmap pdc_memmap PDC_ALIGNMENT;
201 struct pdc_sysmap_find pdc_find PDC_ALIGNMENT;
202 struct pdc_sysmap_addrs pdc_addr PDC_ALIGNMENT;
203 struct pdc_iodc_read pdc_iodc_read PDC_ALIGNMENT;
204 
205 void
pdc_scanbus(struct device * self,struct confargs * ca,int maxmod,hppa_hpa_t hpa,int cpu_scan)206 pdc_scanbus(struct device *self, struct confargs *ca, int maxmod,
207     hppa_hpa_t hpa, int cpu_scan)
208 {
209 	int start, end, incr, i;
210 
211 	/* Scan forwards for CPUs, backwards for everything else. */
212 	if (cpu_scan) {
213 		start = 0;
214 		incr = 1;
215 		end = maxmod;
216 	} else {
217 		start = maxmod - 1;
218 		incr = -1;
219 		end = -1;
220 	}
221 
222 	for (i = start; i != end; i += incr) {
223 		struct confargs nca;
224 		int error;
225 
226 		bzero(&nca, sizeof(nca));
227 		nca.ca_iot = ca->ca_iot;
228 		nca.ca_dmatag = ca->ca_dmatag;
229 		nca.ca_dp.dp_bc[0] = ca->ca_dp.dp_bc[1];
230 		nca.ca_dp.dp_bc[1] = ca->ca_dp.dp_bc[2];
231 		nca.ca_dp.dp_bc[2] = ca->ca_dp.dp_bc[3];
232 		nca.ca_dp.dp_bc[3] = ca->ca_dp.dp_bc[4];
233 		nca.ca_dp.dp_bc[4] = ca->ca_dp.dp_bc[5];
234 		nca.ca_dp.dp_bc[5] = ca->ca_dp.dp_mod;
235 		nca.ca_dp.dp_mod = i;
236 		nca.ca_hpamask = ca->ca_hpamask;
237 		nca.ca_naddrs = 0;
238 		nca.ca_hpa = 0;
239 
240 		if (hpa) {
241 			nca.ca_hpa = hpa + IOMOD_HPASIZE * i;
242 			nca.ca_dp.dp_mod = i;
243 		} else if ((error = pdc_call((iodcio_t)pdc, 0, PDC_MEMMAP,
244 		    PDC_MEMMAP_HPA, &pdc_memmap, &nca.ca_dp)) == 0)
245 			nca.ca_hpa = pdc_memmap.hpa;
246 		else if ((error = pdc_call((iodcio_t)pdc, 0, PDC_SYSMAP,
247 		    PDC_SYSMAP_HPA, &pdc_memmap, &nca.ca_dp)) == 0) {
248 			struct device_path path;
249 			int im, ia;
250 
251 			nca.ca_hpa = pdc_memmap.hpa;
252 
253 			for (im = 0; !(error = pdc_call((iodcio_t)pdc, 0,
254 			    PDC_SYSMAP, PDC_SYSMAP_FIND,
255 			    &pdc_find, &path, im)) &&
256 			    pdc_find.hpa != nca.ca_hpa; im++)
257 				;
258 
259 			if (!error)
260 				nca.ca_hpasz = pdc_find.size << PGSHIFT;
261 
262 			if (!error && pdc_find.naddrs) {
263 				nca.ca_naddrs = pdc_find.naddrs;
264 				if (nca.ca_naddrs > 16) {
265 					nca.ca_naddrs = 16;
266 					printf("WARNING: too many (%d) addrs\n",
267 					    pdc_find.naddrs);
268 				}
269 
270 				if (autoconf_verbose)
271 					printf(">> ADDRS:");
272 
273 				for (ia = 0; !(error = pdc_call((iodcio_t)pdc,
274 				    0, PDC_SYSMAP, PDC_SYSMAP_ADDR, &pdc_addr,
275 				    im, ia + 1)) && ia < nca.ca_naddrs; ia++) {
276 					nca.ca_addrs[ia].addr = pdc_addr.hpa;
277 					nca.ca_addrs[ia].size =
278 					    pdc_addr.size << PGSHIFT;
279 
280 					if (autoconf_verbose)
281 						printf(" 0x%lx[0x%x]",
282 						    nca.ca_addrs[ia].addr,
283 						    nca.ca_addrs[ia].size);
284 				}
285 				if (autoconf_verbose)
286 					printf("\n");
287 			}
288 		}
289 
290 		if (!nca.ca_hpa)
291 			continue;
292 
293 		if (autoconf_verbose)
294 			printf(">> HPA 0x%lx[0x%x]\n",
295 			    nca.ca_hpa, nca.ca_hpasz);
296 
297 		if ((error = pdc_call((iodcio_t)pdc, 0, PDC_IODC,
298 		    PDC_IODC_READ, &pdc_iodc_read, nca.ca_hpa, IODC_DATA,
299 		    &nca.ca_type, sizeof(nca.ca_type))) < 0) {
300 			if (autoconf_verbose)
301 				printf(">> iodc_data error %d\n", error);
302 			continue;
303 		}
304 
305 		nca.ca_pdc_iodc_read = &pdc_iodc_read;
306 		nca.ca_name = hppa_mod_info(nca.ca_type.iodc_type,
307 					    nca.ca_type.iodc_sv_model);
308 
309 		if (autoconf_verbose) {
310 			printf(">> probing: flags %b bc %d/%d/%d/%d/%d/%d ",
311 			    nca.ca_dp.dp_flags, PZF_BITS,
312 			    nca.ca_dp.dp_bc[0], nca.ca_dp.dp_bc[1],
313 			    nca.ca_dp.dp_bc[2], nca.ca_dp.dp_bc[3],
314 			    nca.ca_dp.dp_bc[4], nca.ca_dp.dp_bc[5]);
315 			printf("mod %x hpa %lx type %x sv %x\n",
316 			    nca.ca_dp.dp_mod, nca.ca_hpa,
317 			    nca.ca_type.iodc_type, nca.ca_type.iodc_sv_model);
318 		}
319 
320 		if (cpu_scan && nca.ca_type.iodc_type == HPPA_TYPE_NPROC &&
321 		    nca.ca_type.iodc_sv_model == HPPA_NPROC_HPPA)
322 			ncpusfound++;
323 
324 		if (cpu_scan &&
325 		    ((nca.ca_type.iodc_type != HPPA_TYPE_NPROC ||
326 	            nca.ca_type.iodc_sv_model != HPPA_NPROC_HPPA) &&
327 		    (nca.ca_type.iodc_type != HPPA_TYPE_MEMORY ||
328 		    nca.ca_type.iodc_sv_model != HPPA_MEMORY_PDEP)))
329 			continue;
330 
331 		if (!cpu_scan &&
332 		    ((nca.ca_type.iodc_type == HPPA_TYPE_NPROC &&
333 		    nca.ca_type.iodc_sv_model == HPPA_NPROC_HPPA) ||
334 		    (nca.ca_type.iodc_type == HPPA_TYPE_MEMORY &&
335 		    nca.ca_type.iodc_sv_model == HPPA_MEMORY_PDEP)))
336 			continue;
337 
338 		config_found_sm(self, &nca, mbprint, mbsubmatch);
339 	}
340 }
341 
342 const struct hppa_mod_info hppa_knownmods[] = {
343 #include <hppa/dev/cpudevs_data.h>
344 };
345 
346 const char *
hppa_mod_info(type,sv)347 hppa_mod_info(type, sv)
348 	int type, sv;
349 {
350 	const struct hppa_mod_info *mi;
351 	static char fakeid[32];
352 
353 	for (mi = hppa_knownmods; mi->mi_type >= 0 &&
354 	    (mi->mi_type != type || mi->mi_sv != sv); mi++);
355 
356 	if (mi->mi_type < 0) {
357 		snprintf(fakeid, sizeof fakeid, "type %x, sv %x", type, sv);
358 		return fakeid;
359 	} else
360 		return mi->mi_name;
361 }
362 
363 void
device_register(struct device * dev,void * aux)364 device_register(struct device *dev, void *aux)
365 {
366 #if NPCI > 0
367 	extern struct cfdriver pci_cd;
368 #endif
369 #if NCD > 0 || NSD > 0 || NST > 0
370 	extern struct cfdriver scsibus_cd;
371 #endif
372 	struct confargs *ca = aux;
373 	static struct device *elder = NULL;
374 
375 	if (bootdv != NULL)
376 		return;	/* We already have a winner */
377 
378 #if NPCI > 0
379 	if (dev->dv_parent &&
380 	    dev->dv_parent->dv_cfdata->cf_driver == &pci_cd) {
381 		struct pci_attach_args *pa = aux;
382 		pcireg_t addr;
383 		int reg;
384 
385 		for (reg = PCI_MAPREG_START; reg < PCI_MAPREG_END; reg += 4) {
386 			addr = pci_conf_read(pa->pa_pc, pa->pa_tag, reg);
387 			if (PCI_MAPREG_TYPE(addr) == PCI_MAPREG_TYPE_IO)
388 				addr = PCI_MAPREG_IO_ADDR(addr);
389 			else
390 				addr = PCI_MAPREG_MEM_ADDR(addr);
391 
392 			if (addr == (pcireg_t)PAGE0->mem_boot.pz_hpa) {
393 				elder = dev;
394 				break;
395 			}
396 		}
397 	} else
398 #endif
399 	if (ca->ca_hpa == (hppa_hpa_t)PAGE0->mem_boot.pz_hpa) {
400 		/*
401 		 * If hpa matches, the only thing we know is that the
402 		 * booted device is either this one or one of its children.
403 		 * And the children will not necessarily have the correct
404 		 * hpa value.
405 		 * Save this elder for now.
406 		 */
407 		elder = dev;
408 	} else if (elder == NULL) {
409 		return;	/* not the device we booted from */
410 	}
411 
412 	/*
413 	 * Unfortunately, we can not match on pz_class vs dv_class on
414 	 * older snakes netbooting using the rbootd protocol.
415 	 * In this case, we'll end up with pz_class == PCL_RANDOM...
416 	 * Instead, trust the device class from what the kernel attached
417 	 * now...
418 	 */
419 	switch (dev->dv_class) {
420 	case DV_IFNET:
421 		/*
422 		 * Netboot is the top elder
423 		 */
424 		if (elder == dev) {
425 			bootdv = dev;
426 		}
427 		return;
428 	case DV_DISK:
429 	case DV_DULL:
430 		if ((PAGE0->mem_boot.pz_class & PCL_CLASS_MASK) != PCL_RANDOM)
431 			return;
432 		break;
433 	case DV_TAPE:
434 		if ((PAGE0->mem_boot.pz_class & PCL_CLASS_MASK) != PCL_SEQU)
435 			return;
436 		break;
437 	default:
438 		/* No idea what we were booted from, but better ask the user */
439 		return;
440 	}
441 
442 	/*
443 	 * If control goes here, we are booted from a block device and we
444 	 * matched a block device.
445 	 *
446 	 * We only grok SCSI boot currently.  Match on proper device
447 	 * hierarchy and unit/lun values.
448 	 */
449 
450 #if NCD > 0 || NSD > 0 || NST > 0
451 	if (dev->dv_parent &&
452 	    dev->dv_parent->dv_cfdata->cf_driver == &scsibus_cd) {
453 		struct scsi_attach_args *sa = aux;
454 		struct scsi_link *sl = sa->sa_sc_link;
455 
456 		/*
457 		 * sd/st/cd is attached to scsibus which is attached to
458 		 * the controller. Hence the grandparent here should be
459 		 * the elder.
460 		 */
461 		if (dev->dv_parent->dv_parent != elder) {
462 			return;
463 		}
464 
465 		/*
466 		 * And now check for proper target and lun values
467 		 */
468 		if (sl->target == PAGE0->mem_boot.pz_layers[0] &&
469 		    sl->lun == PAGE0->mem_boot.pz_layers[1]) {
470 			bootdv = dev;
471 		}
472 	}
473 #endif
474 }
475 
476 /*
477  * cpu_configure:
478  * called at boot time, configure all devices on system
479  */
480 void
cpu_configure(void)481 cpu_configure(void)
482 {
483 	splhigh();
484 	if (config_rootfound("mainbus", "mainbus") == NULL)
485 		panic("no mainbus found");
486 
487 	cpu_intr_init();
488 	spl0();
489 
490 	if (cold_hook)
491 		(*cold_hook)(HPPA_COLD_HOT);
492 
493 #ifdef USELEDS
494 	timeout_set(&heartbeat_tmo, heartbeat, NULL);
495 	heartbeat(NULL);
496 #endif
497 	cold = 0;
498 }
499 
500 void
diskconf(void)501 diskconf(void)
502 {
503 	print_devpath("bootpath", &PAGE0->mem_boot);
504 
505 #if NMPATH > 0
506 	if (bootdv != NULL)
507 		bootdv = mpath_bootdv(bootdv);
508 #endif
509 
510 	setroot(bootdv, 0, RB_USERREQ);
511 	dumpconf();
512 }
513 
514 const struct nam2blk nam2blk[] = {
515 	{ "vnd",	2 },
516 	{ "rd",		3 },
517 	{ "sd",		4 },
518 	{ "cd",		6 },
519 	{ "fd",		7 },
520 	{ "wd",		8 },
521 	{ NULL,		-1 }
522 };
523