1/*	$NetBSD$	*/
2
3/*-
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Adam Glass, Gordon W. Ross, and Matthew Fredette.
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 * Setup the system to run on the current machine.
34 *
35 * Configure() is called at boot time.  Available devices are
36 * determined (from possibilities mentioned in ioconf.c), and
37 * the drivers are initialized.
38 */
39
40#include <sys/cdefs.h>
41__KERNEL_RCSID(0, "$NetBSD$");
42
43#include "opt_kgdb.h"
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/conf.h>
48#include <sys/device.h>
49#include <sys/reboot.h>
50
51#include "locators.h"
52
53#include "scsibus.h"
54
55#if NSCSIBUS > 0
56#include <dev/scsipi/scsi_all.h>
57#include <dev/scsipi/scsipi_all.h>
58#include <dev/scsipi/scsiconf.h>
59#endif /* NSCSIBUS > 0 */
60
61#include <machine/autoconf.h>
62#include <machine/intr.h>
63#include <machine/promlib.h>
64
65#ifdef	KGDB
66#include <sys/kgdb.h>
67#endif
68
69/*
70 * Do general device autoconfiguration,
71 * then choose root device (etc.)
72 * Called by sys/kern/subr_autoconf.c: configure()
73 */
74void
75cpu_configure(void)
76{
77
78	/*
79	 * Consider stopping for a debugger before
80	 * autoconfiguration.
81	 */
82	if (boothowto & RB_KDB) {
83#ifdef KGDB
84		/* XXX - Ask on console for kgdb_dev? */
85		/* Note: this will just return if kgdb_dev==NODEV */
86		kgdb_connect(1);
87#else	/* KGDB */
88		/* Either DDB or no debugger (just PROM). */
89		Debugger();
90#endif	/* KGDB */
91	}
92
93	/* General device autoconfiguration. */
94	if (config_rootfound("mainbus", NULL) == NULL)
95		panic("%s: mainbus not found", __func__);
96
97	/*
98	 * Now that device autoconfiguration is finished,
99	 * we can safely enable interrupts.
100	 */
101	printf("enabling interrupts\n");
102	(void)spl0();
103}
104
105static int 	mainbus_match(device_t, cfdata_t, void *);
106static void	mainbus_attach(device_t, device_t, void *);
107
108CFATTACH_DECL_NEW(mainbus, 0,
109    mainbus_match, mainbus_attach, NULL, NULL);
110
111/*
112 * Probe for the mainbus; always succeeds.
113 */
114static int
115mainbus_match(device_t parent, cfdata_t cf, void *aux)
116{
117
118	return 1;
119}
120
121/*
122 * Do "direct" configuration for the bus types on mainbus.
123 * This controls the order of autoconfig for important things
124 * used early.  For example, idprom is used by Ether drivers.
125 */
126static void
127mainbus_attach(device_t parent, device_t self, void *args)
128{
129	struct mainbus_attach_args ma;
130	const char *const *cpp;
131	static const char *const special[] = {
132		/* find these first */
133		"obio",
134		"obmem",
135		NULL
136	};
137
138	aprint_normal("\n");
139
140	ma.ma_bustag = &mainbus_space_tag;
141	ma.ma_dmatag = &mainbus_dma_tag;
142	ma.ma_paddr = LOCATOR_FORBIDDEN;
143	ma.ma_pri = LOCATOR_FORBIDDEN;
144
145	/* Find all `early' mainbus buses */
146	for (cpp = special; *cpp != NULL; cpp++) {
147		ma.ma_name = *cpp;
148		(void)config_found(self, &ma, NULL);
149	}
150
151	/* Find the remaining buses */
152	ma.ma_name = NULL;
153	(void)config_found(self, &ma, NULL);
154
155	/* Lastly, find the PROM console */
156	ma.ma_name = "pcons";
157	(void)config_found(self, &ma, NULL);
158}
159
160/*
161 * sun68k_bus_search:
162 * This function is passed to config_search_ia() by the attach function
163 * for each of the "bus" drivers (obio, obmem, mbmem, vme, ...).
164 * The purpose of this function is to copy the "locators" into our
165 * _attach_args structure, so child drivers may use the _attach_args both
166 * as match parameters and as temporary storage for the defaulted
167 * locator values determined in the child_match and preserved for
168 * the child_attach function.  If the bus attach functions just
169 * used config_found, then we would not have an opportunity to
170 * setup the _attach_args for each child match and attach call.
171 */
172int
173sun68k_bus_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
174{
175	struct mainbus_attach_args *map = aux;
176	struct mainbus_attach_args ma;
177
178	/* Check whether we're looking for a specifically named device */
179	if (map->ma_name != NULL && strcmp(map->ma_name, cf->cf_name) != 0)
180		return 0;
181
182#ifdef	DIAGNOSTIC
183	if (cf->cf_fstate == FSTATE_STAR)
184		panic("%s: FSTATE_STAR", __func__);
185#endif
186
187	/*
188	 * Prepare to copy the locators into our _attach_args.
189	 */
190	ma = *map;
191	ma.ma_name = NULL;
192
193	/*
194	 * Avoid entries which are missing attach information that
195	 * they need, or that have attach information that they
196	 * cannot have.  The individual bus attach functions tell
197	 * us this by initializing the locator fields in the attach
198	 * args they provide us.
199	 *
200	 * At the same time we copy these values into the _attach_args
201	 * will pass to the device's match and attach functions.
202	 */
203#ifdef	DIAGNOSTIC
204#define BAD_LOCATOR(ma_loc, what) \
205	panic("%s: %s %s for: %s%d", __func__, \
206	    map->ma_loc == LOCATOR_REQUIRED ? "missing" : "unexpected", \
207	    what, cf->cf_name, cf->cf_unit)
208#else
209#define BAD_LOCATOR(ma_loc, what) return 0
210#endif
211
212#define CHECK_LOCATOR(ma_loc, cf_loc, what) \
213	if ((map->ma_loc == LOCATOR_FORBIDDEN && cf->cf_loc != -1) || \
214	    (map->ma_loc == LOCATOR_REQUIRED && cf->cf_loc == -1)) \
215		BAD_LOCATOR(ma_loc, what); \
216	else \
217		ma.ma_loc = cf->cf_loc
218
219	CHECK_LOCATOR(ma_paddr, cf_loc[MBIOCF_ADDR], "address");
220	CHECK_LOCATOR(ma_pri, cf_loc[MBIOCF_IPL], "ipl");
221
222	/*
223	 * Note that this allows the match function to save
224	 * defaulted locators in the _attach_args that will be
225	 * preserved for the related attach call.
226	 * XXX - This is a hack...
227	 */
228	if (config_match(parent, cf, &ma) > 0) {
229		config_attach(parent, cf, &ma, sun68k_bus_print);
230	}
231	return 0;
232}
233
234/*
235 * sun68k_bus_print:
236 * Just print out the final (non-default) locators.
237 * The parent name is non-NULL when there was no match
238 * found by config_found().
239 */
240int
241sun68k_bus_print(void *args, const char *name)
242{
243	struct mainbus_attach_args *ma = args;
244
245	if (name)
246		aprint_normal("%s:", name);
247
248	if (ma->ma_paddr != -1)
249		aprint_normal(" addr 0x%x", (unsigned int) ma->ma_paddr);
250	if (ma->ma_pri != -1)
251		aprint_normal(" ipl %d", ma->ma_pri);
252
253	return UNCONF;
254}
255
256/****************************************************************/
257
258/* This takes the args: name, ctlr, unit */
259typedef device_t (*findfunc_t)(char *, int, int);
260
261static device_t net_find(char *, int, int);
262#if NSCSIBUS > 0
263static device_t scsi_find(char *, int, int);
264#endif /* NSCSIBUS > 0 */
265static device_t xx_find(char *, int, int);
266
267struct prom_n2f {
268	const char name[4];
269	findfunc_t func;
270};
271static struct prom_n2f prom_dev_table[] = {
272	{ "ie",		net_find },
273	{ "ec",		net_find },
274	{ "le",		net_find },
275#if NSCSIBUS > 0
276	{ "sd",		scsi_find },
277#endif /* NSCSIBUS > 0 */
278	{ "xy",		xx_find },
279	{ "xd",		xx_find },
280	{ "",		0 },
281};
282
283/*
284 * This converts one hex number to an integer, and returns
285 * an updated string pointer.
286 */
287static const char *str2hex(const char *, int *);
288static const char *
289str2hex(const char *p, int *_val)
290{
291	int val;
292	int c;
293
294	for (val = 0;; val = (val << 4) + c, p++) {
295		c = *((const unsigned char *)p);
296		if (c >= 'a')
297			c-= ('a' + 10);
298		else if (c >= 'A')
299			c -= ('A' + 10);
300		else if (c >= '0')
301			c -= '0';
302		if (c < 0 || c > 15)
303			break;
304	}
305	*_val = val;
306	return p;
307}
308
309/*
310 * Choose root and swap devices.
311 */
312void
313cpu_rootconf(void)
314{
315	struct prom_n2f *nf;
316	const char *devname;
317	findfunc_t find;
318	char promname[4];
319	char partname[4];
320	const char *prompath;
321	int prom_ctlr, prom_unit, prom_part;
322
323	/* Get the PROM boot path and take it apart. */
324	prompath = prom_getbootpath();
325	if (prompath == NULL)
326		prompath = "zz(0,0,0)";
327	promname[0] = *(prompath++);
328	promname[1] = *(prompath++);
329	promname[2] = '\0';
330	prom_ctlr = prom_unit = prom_part = 0;
331	if (*prompath == '(' &&
332	    *(prompath = str2hex(++prompath, &prom_ctlr)) == ',' &&
333	    *(prompath = str2hex(++prompath, &prom_unit)) == ',')
334		(void)str2hex(++prompath, &prom_part);
335
336	/* Default to "unknown" */
337	booted_device = NULL;
338	booted_partition = 0;
339	devname = "<unknown>";
340	partname[0] = '\0';
341	find = NULL;
342
343	/* Do we know anything about the PROM boot device? */
344	for (nf = prom_dev_table; nf->func; nf++)
345		if (!strcmp(nf->name, promname)) {
346			find = nf->func;
347			break;
348		}
349	if (find)
350		booted_device = (*find)(promname, prom_ctlr, prom_unit);
351	if (booted_device) {
352		devname = booted_device->dv_xname;
353		if (device_class(booted_device) == DV_DISK) {
354			booted_partition = prom_part & 7;
355			partname[0] = 'a' + booted_partition;
356			partname[1] = '\0';
357		}
358	}
359
360	printf("boot device: %s%s\n", devname, partname);
361	rootconf();
362}
363
364/*
365 * Functions to find devices using PROM boot parameters.
366 */
367
368/*
369 * Network device:  Just use controller number.
370 */
371static device_t
372net_find(char *name, int ctlr, int unit)
373{
374
375	return device_find_by_driver_unit(name, ctlr);
376}
377
378#if NSCSIBUS > 0
379/*
380 * SCSI device:  The controller number corresponds to the
381 * scsibus number, and the unit number is (targ*8 + LUN).
382 */
383static device_t
384scsi_find(char *name, int ctlr, int unit)
385{
386	device_t scsibus;
387	struct scsibus_softc *sbsc;
388	struct scsipi_periph *periph;
389	int target, lun;
390
391	if ((scsibus = device_find_by_driver_unit("scsibus", ctlr)) == NULL)
392		return NULL;
393
394	/* Compute SCSI target/LUN from PROM unit. */
395	target = prom_sd_target((unit >> 3) & 7);
396	lun = unit & 7;
397
398	/* Find the device at this target/LUN */
399	sbsc = device_private(scsibus);
400	periph = scsipi_lookup_periph(sbsc->sc_channel, target, lun);
401	if (periph == NULL)
402		return NULL;
403
404	return periph->periph_dev;
405}
406#endif /* NSCSIBUS > 0 */
407
408/*
409 * Xylogics SMD disk: (xy, xd)
410 * Assume wired-in unit numbers for now...
411 */
412static device_t
413xx_find(char *name, int ctlr, int unit)
414{
415
416	return device_find_by_driver_unit(name, ctlr * 2 + unit);
417}
418