xref: /illumos-gate/usr/src/uts/sun4u/io/opl_cfg.c (revision d5ebc493)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  * Copyright 2023 Oxide Computer Company
26  */
27 
28 #include <sys/conf.h>
29 #include <sys/kmem.h>
30 #include <sys/debug.h>
31 #include <sys/modctl.h>
32 #include <sys/autoconf.h>
33 #include <sys/hwconf.h>
34 #include <sys/ddi_impldefs.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/sunndi.h>
38 #include <sys/ndi_impldefs.h>
39 #include <sys/machsystm.h>
40 #include <sys/fcode.h>
41 #include <sys/promif.h>
42 #include <sys/promimpl.h>
43 #include <sys/opl_cfg.h>
44 #include <sys/scfd/scfostoescf.h>
45 
46 static unsigned int		opl_cfg_inited;
47 static opl_board_cfg_t		opl_boards[HWD_SBS_PER_DOMAIN];
48 
49 /*
50  * Module control operations
51  */
52 
53 extern struct mod_ops mod_miscops;
54 
55 static struct modlmisc modlmisc = {
56 	&mod_miscops,				/* Type of module */
57 	"OPL opl_cfg"
58 };
59 
60 static struct modlinkage modlinkage = {
61 	MODREV_1, (void *)&modlmisc, NULL
62 };
63 
64 static int	opl_map_in(dev_info_t *, fco_handle_t, fc_ci_t *);
65 static int	opl_map_out(dev_info_t *, fco_handle_t, fc_ci_t *);
66 static int	opl_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *);
67 static int	opl_register_store(dev_info_t *, fco_handle_t, fc_ci_t *);
68 
69 static int	opl_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
70 static int	opl_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
71 static int	opl_vtop(dev_info_t *, fco_handle_t, fc_ci_t *);
72 
73 static int	opl_config_child(dev_info_t *, fco_handle_t, fc_ci_t *);
74 
75 static int	opl_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *);
76 static int	opl_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *);
77 
78 static int	opl_map_phys(dev_info_t *, struct regspec *,  caddr_t *,
79 				ddi_device_acc_attr_t *, ddi_acc_handle_t *);
80 static void	opl_unmap_phys(ddi_acc_handle_t *);
81 static int	opl_get_hwd_va(dev_info_t *, fco_handle_t, fc_ci_t *);
82 static int	opl_master_interrupt(dev_info_t *, fco_handle_t, fc_ci_t *);
83 
84 extern int	prom_get_fcode_size(char *);
85 extern int	prom_get_fcode(char *, char *);
86 
87 static int	master_interrupt_init(uint32_t, uint32_t);
88 
89 #define	PROBE_STR_SIZE	64
90 #define	UNIT_ADDR_SIZE	64
91 
92 opl_fc_ops_t	opl_fc_ops[] = {
93 
94 	{	FC_MAP_IN,		opl_map_in},
95 	{	FC_MAP_OUT,		opl_map_out},
96 	{	"rx@",			opl_register_fetch},
97 	{	FC_RL_FETCH,		opl_register_fetch},
98 	{	FC_RW_FETCH,		opl_register_fetch},
99 	{	FC_RB_FETCH,		opl_register_fetch},
100 	{	"rx!",			opl_register_store},
101 	{	FC_RL_STORE,		opl_register_store},
102 	{	FC_RW_STORE,		opl_register_store},
103 	{	FC_RB_STORE,		opl_register_store},
104 	{	"claim-memory",		opl_claim_memory},
105 	{	"release-memory",	opl_release_memory},
106 	{	"vtop",			opl_vtop},
107 	{	FC_CONFIG_CHILD,	opl_config_child},
108 	{	FC_GET_FCODE_SIZE,	opl_get_fcode_size},
109 	{	FC_GET_FCODE,		opl_get_fcode},
110 	{	"get-hwd-va",		opl_get_hwd_va},
111 	{	"master-interrupt",	opl_master_interrupt},
112 	{	NULL,			NULL}
113 
114 };
115 
116 extern caddr_t	efcode_vaddr;
117 extern int	efcode_size;
118 
119 #ifdef DEBUG
120 #define	HWDDUMP_OFFSETS		1
121 #define	HWDDUMP_ALL_STATUS	2
122 #define	HWDDUMP_CHUNKS		3
123 #define	HWDDUMP_SBP		4
124 
125 int		hwddump_flags = HWDDUMP_SBP | HWDDUMP_CHUNKS;
126 #endif
127 
128 static int	master_interrupt_inited = 0;
129 
130 int
_init()131 _init()
132 {
133 	int	err = 0;
134 
135 	/*
136 	 * Create a resource map for the contiguous memory allocated
137 	 * at start-of-day in startup.c
138 	 */
139 	err = ndi_ra_map_setup(ddi_root_node(), "opl-fcodemem");
140 	if (err == NDI_FAILURE) {
141 		cmn_err(CE_WARN, "Cannot setup resource map opl-fcodemem\n");
142 		return (1);
143 	}
144 
145 	/*
146 	 * Put the allocated memory into the pool.
147 	 */
148 	(void) ndi_ra_free(ddi_root_node(), (uint64_t)efcode_vaddr,
149 	    (uint64_t)efcode_size, "opl-fcodemem", 0);
150 
151 	if ((err = mod_install(&modlinkage)) != 0) {
152 		cmn_err(CE_WARN, "opl_cfg failed to load, error=%d", err);
153 		(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
154 	}
155 
156 	return (err);
157 }
158 
159 int
_fini(void)160 _fini(void)
161 {
162 	int ret;
163 
164 	ret = (mod_remove(&modlinkage));
165 	if (ret != 0)
166 		return (ret);
167 
168 	(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
169 
170 	return (ret);
171 }
172 
173 int
_info(struct modinfo * modinfop)174 _info(struct modinfo *modinfop)
175 {
176 	return (mod_info(&modlinkage, modinfop));
177 }
178 
179 #ifdef DEBUG
180 static void
opl_dump_hwd(opl_probe_t * probe)181 opl_dump_hwd(opl_probe_t *probe)
182 {
183 	hwd_header_t		*hdrp;
184 	hwd_sb_status_t		*statp;
185 	hwd_domain_info_t	*dinfop;
186 	hwd_sb_t		*sbp;
187 	hwd_cpu_chip_t		*chips;
188 	hwd_pci_ch_t		*channels;
189 	int			board, i, status;
190 
191 	board = probe->pr_board;
192 
193 	hdrp = probe->pr_hdr;
194 	statp = probe->pr_sb_status;
195 	dinfop = probe->pr_dinfo;
196 	sbp = probe->pr_sb;
197 
198 	printf("HWD: board %d\n", board);
199 	printf("HWD:magic = 0x%x\n", hdrp->hdr_magic);
200 	printf("HWD:version = 0x%x.%x\n", hdrp->hdr_version.major,
201 	    hdrp->hdr_version.minor);
202 
203 	if (hwddump_flags & HWDDUMP_OFFSETS) {
204 		printf("HWD:status offset = 0x%x\n",
205 		    hdrp->hdr_sb_status_offset);
206 		printf("HWD:domain offset = 0x%x\n",
207 		    hdrp->hdr_domain_info_offset);
208 		printf("HWD:board offset = 0x%x\n", hdrp->hdr_sb_info_offset);
209 	}
210 
211 	if (hwddump_flags & HWDDUMP_SBP)
212 		printf("HWD:sb_t ptr = 0x%p\n", (void *)probe->pr_sb);
213 
214 	if (hwddump_flags & HWDDUMP_ALL_STATUS) {
215 		int bd;
216 		printf("HWD:board status =");
217 		for (bd = 0; bd < HWD_SBS_PER_DOMAIN; bd++)
218 			printf("%x ", statp->sb_status[bd]);
219 		printf("\n");
220 	} else {
221 		printf("HWD:board status = %d\n", statp->sb_status[board]);
222 	}
223 
224 	printf("HWD:banner name = %s\n", dinfop->dinf_banner_name);
225 	printf("HWD:platform = %s\n", dinfop->dinf_platform_token);
226 
227 	printf("HWD:chip status:\n");
228 	chips = &sbp->sb_cmu.cmu_cpu_chips[0];
229 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
230 
231 		status = chips[i].chip_status;
232 		printf("chip[%d] = ", i);
233 		if (HWD_STATUS_NONE(status))
234 			printf("none");
235 		else if (HWD_STATUS_FAILED(status))
236 			printf("fail");
237 		else if (HWD_STATUS_OK(status))
238 			printf("ok");
239 		printf("\n");
240 	}
241 
242 	if (hwddump_flags & HWDDUMP_CHUNKS) {
243 		int chunk;
244 		hwd_memory_t *mem = &sbp->sb_cmu.cmu_memory;
245 		printf("HWD:chunks:\n");
246 		for (chunk = 0; chunk < HWD_MAX_MEM_CHUNKS; chunk++)
247 			printf("\t%d 0x%lx 0x%lx\n", chunk,
248 			    mem->mem_chunks[chunk].chnk_start_address,
249 			    mem->mem_chunks[chunk].chnk_size);
250 	}
251 
252 	printf("HWD:channel status:\n");
253 	channels = &sbp->sb_pci_ch[0];
254 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
255 
256 		status = channels[i].pci_status;
257 		printf("channels[%d] = ", i);
258 		if (HWD_STATUS_NONE(status))
259 			printf("none");
260 		else if (HWD_STATUS_FAILED(status))
261 			printf("fail");
262 		else if (HWD_STATUS_OK(status))
263 			printf("ok");
264 		printf("\n");
265 	}
266 	printf("channels[%d] = ", i);
267 	status = sbp->sb_cmu.cmu_ch.chan_status;
268 	if (HWD_STATUS_NONE(status))
269 		printf("none");
270 	else if (HWD_STATUS_FAILED(status))
271 		printf("fail");
272 	else if (HWD_STATUS_OK(status))
273 		printf("ok");
274 	printf("\n");
275 }
276 #endif /* DEBUG */
277 
278 #ifdef UCTEST
279 	/*
280 	 * For SesamI debugging, just map the SRAM directly to a kernel
281 	 * VA and read it out from there
282 	 */
283 
284 #include <sys/vmem.h>
285 #include <vm/seg_kmem.h>
286 
287 /*
288  * 0x4081F1323000LL is the HWD base address for LSB 0. But we need to map
289  * at page boundaries. So, we use a base address of 0x4081F1322000LL.
290  * Note that this has to match the HWD base pa set in .sesami-common-defs.
291  *
292  * The size specified for the HWD in the SCF spec is 36K. But since
293  * we adjusted the base address by 4K, we need to use 40K for the
294  * mapping size to cover the HWD. And 40K is also a multiple of the
295  * base page size.
296  */
297 #define	OPL_HWD_BASE(lsb)       \
298 (0x4081F1322000LL | (((uint64_t)(lsb)) << 40))
299 
300 	void    *opl_hwd_vaddr;
301 #endif /* UCTEST */
302 
303 /*
304  * Get the hardware descriptor from SCF.
305  */
306 
307 /*ARGSUSED*/
308 int
opl_read_hwd(int board,hwd_header_t ** hdrp,hwd_sb_status_t ** statp,hwd_domain_info_t ** dinfop,hwd_sb_t ** sbp)309 opl_read_hwd(int board, hwd_header_t **hdrp, hwd_sb_status_t **statp,
310     hwd_domain_info_t **dinfop, hwd_sb_t **sbp)
311 {
312 	static int (*getinfop)(uint32_t, uint8_t, uint32_t, uint32_t *,
313 	    void *) = NULL;
314 	void *hwdp;
315 
316 	uint32_t key = KEY_ESCF;	/* required value */
317 	uint8_t  type = 0x40;		/* SUB_OS_RECEIVE_HWD */
318 	uint32_t transid = board;
319 	uint32_t datasize = HWD_DATA_SIZE;
320 
321 	hwd_header_t		*hd;
322 	hwd_sb_status_t		*st;
323 	hwd_domain_info_t	*di;
324 	hwd_sb_t		*sb;
325 
326 	int	ret;
327 
328 	if (opl_boards[board].cfg_hwd == NULL) {
329 #ifdef UCTEST
330 		/*
331 		 * Just map the HWD in SRAM to a kernel VA
332 		 */
333 
334 		size_t			size;
335 		pfn_t			pfn;
336 
337 		size = 0xA000;
338 
339 		opl_hwd_vaddr = vmem_alloc(heap_arena, size, VM_SLEEP);
340 		if (opl_hwd_vaddr == NULL) {
341 			cmn_err(CE_NOTE, "No space for HWD");
342 			return (-1);
343 		}
344 
345 		pfn = btop(OPL_HWD_BASE(board));
346 		hat_devload(kas.a_hat, opl_hwd_vaddr, size, pfn, PROT_READ,
347 		    HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK);
348 
349 		hwdp = (void *)((char *)opl_hwd_vaddr + 0x1000);
350 		opl_boards[board].cfg_hwd = hwdp;
351 		ret = 0;
352 #else
353 
354 		/* find the scf_service_getinfo() function */
355 		if (getinfop == NULL)
356 			getinfop = (int (*)(uint32_t, uint8_t, uint32_t,
357 			    uint32_t *,
358 			    void *))modgetsymvalue("scf_service_getinfo", 0);
359 
360 		if (getinfop == NULL)
361 			return (-1);
362 
363 		/* allocate memory to receive the data */
364 		hwdp = kmem_alloc(HWD_DATA_SIZE, KM_SLEEP);
365 
366 		/* get the HWD */
367 		ret = (*getinfop)(key, type, transid, &datasize, hwdp);
368 		if (ret == 0)
369 			opl_boards[board].cfg_hwd = hwdp;
370 		else
371 			kmem_free(hwdp, HWD_DATA_SIZE);
372 #endif
373 	} else {
374 		hwdp = opl_boards[board].cfg_hwd;
375 		ret = 0;
376 	}
377 
378 	/* copy the data to the destination */
379 	if (ret == 0) {
380 		hd = (hwd_header_t *)hwdp;
381 		st = (hwd_sb_status_t *)
382 		    ((char *)hwdp + hd->hdr_sb_status_offset);
383 		di = (hwd_domain_info_t *)
384 		    ((char *)hwdp + hd->hdr_domain_info_offset);
385 		sb = (hwd_sb_t *)
386 		    ((char *)hwdp + hd->hdr_sb_info_offset);
387 		if (hdrp != NULL)
388 			*hdrp = hd;
389 		if (statp != NULL)
390 			*statp = st;
391 		if (dinfop != NULL)
392 			*dinfop = di;
393 		if (sbp != NULL)
394 			*sbp = sb;
395 	}
396 
397 	return (ret);
398 }
399 
400 /*
401  * The opl_probe_t probe structure is used to pass all sorts of parameters
402  * to callback functions during probing. It also contains a snapshot of
403  * the hardware descriptor that is taken at the beginning of a probe.
404  */
405 static int
opl_probe_init(opl_probe_t * probe)406 opl_probe_init(opl_probe_t *probe)
407 {
408 	hwd_header_t		**hdrp;
409 	hwd_sb_status_t		**statp;
410 	hwd_domain_info_t	**dinfop;
411 	hwd_sb_t		**sbp;
412 	int			board, ret;
413 
414 	board = probe->pr_board;
415 
416 	hdrp = &probe->pr_hdr;
417 	statp = &probe->pr_sb_status;
418 	dinfop = &probe->pr_dinfo;
419 	sbp = &probe->pr_sb;
420 
421 	/*
422 	 * Read the hardware descriptor.
423 	 */
424 	ret = opl_read_hwd(board, hdrp, statp, dinfop, sbp);
425 	if (ret != 0) {
426 
427 		cmn_err(CE_WARN, "IKP: failed to read HWD header");
428 		return (-1);
429 	}
430 
431 #ifdef DEBUG
432 	opl_dump_hwd(probe);
433 #endif
434 	return (0);
435 }
436 
437 /*
438  * This function is used to obtain pointers to relevant device nodes
439  * which are created by Solaris at boot time.
440  *
441  * This function walks the child nodes of a given node, extracts
442  * the "name" property, if it exists, and passes the node to a
443  * callback init function. The callback determines if this node is
444  * interesting or not. If it is, then a pointer to the node is
445  * stored away by the callback for use during unprobe.
446  *
447  * The DDI get property function allocates storage for the name
448  * property. That needs to be freed within this function.
449  */
450 static int
opl_init_nodes(dev_info_t * parent,opl_init_func_t init)451 opl_init_nodes(dev_info_t *parent, opl_init_func_t init)
452 {
453 	dev_info_t	*node;
454 	char		*name;
455 	int		ret;
456 	int		len;
457 
458 	ASSERT(parent != NULL);
459 
460 	/*
461 	 * Hold parent node busy to walk its child list
462 	 */
463 	ndi_devi_enter(parent);
464 	node = ddi_get_child(parent);
465 
466 	while (node != NULL) {
467 
468 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
469 		if (ret != DDI_PROP_SUCCESS) {
470 			/*
471 			 * The property does not exist for this node.
472 			 */
473 			node = ddi_get_next_sibling(node);
474 			continue;
475 		}
476 
477 		ret = init(node, name, len);
478 		kmem_free(name, len);
479 		if (ret != 0) {
480 
481 			ndi_devi_exit(parent);
482 			return (-1);
483 		}
484 
485 		node = ddi_get_next_sibling(node);
486 	}
487 
488 	ndi_devi_exit(parent);
489 
490 	return (0);
491 }
492 
493 /*
494  * This init function finds all the interesting nodes under the
495  * root node and stores pointers to them. The following nodes
496  * are considered interesting by this implementation:
497  *
498  *	"cmp"
499  *		These are nodes that represent processor chips.
500  *
501  *	"pci"
502  *		These are nodes that represent PCI leaves.
503  *
504  *	"pseudo-mc"
505  *		These are nodes that contain memory information.
506  */
507 static int
opl_init_root_nodes(dev_info_t * node,char * name,int len)508 opl_init_root_nodes(dev_info_t *node, char *name, int len)
509 {
510 	int		portid, board, chip, channel, leaf;
511 	int		ret;
512 
513 	if (strncmp(name, OPL_CPU_CHIP_NODE, len) == 0) {
514 
515 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
516 		if (ret != DDI_PROP_SUCCESS)
517 			return (-1);
518 
519 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
520 		if (ret != DDI_PROP_SUCCESS)
521 			return (-1);
522 
523 		chip = OPL_CPU_CHIP(portid);
524 		opl_boards[board].cfg_cpu_chips[chip] = node;
525 
526 	} else if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
527 
528 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
529 		if (ret != DDI_PROP_SUCCESS)
530 			return (-1);
531 
532 		board = OPL_IO_PORTID_TO_LSB(portid);
533 		channel = OPL_PORTID_TO_CHANNEL(portid);
534 
535 		if (channel == OPL_CMU_CHANNEL) {
536 
537 			opl_boards[board].cfg_cmuch_leaf = node;
538 
539 		} else {
540 
541 			leaf = OPL_PORTID_TO_LEAF(portid);
542 			opl_boards[board].cfg_pcich_leaf[channel][leaf] = node;
543 		}
544 	} else if (strncmp(name, OPL_PSEUDO_MC_NODE, len) == 0) {
545 
546 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
547 		if (ret != DDI_PROP_SUCCESS)
548 			return (-1);
549 
550 		ASSERT((board >= 0) && (board < HWD_SBS_PER_DOMAIN));
551 
552 		opl_boards[board].cfg_pseudo_mc = node;
553 	}
554 
555 	return (0);
556 }
557 
558 /*
559  * This function initializes the OPL IKP feature. Currently, all it does
560  * is find the interesting nodes that Solaris has created at boot time
561  * for boards present at boot time and store pointers to them. This
562  * is useful if those boards are unprobed by DR.
563  */
564 int
opl_init_cfg()565 opl_init_cfg()
566 {
567 	dev_info_t	*root;
568 
569 	if (opl_cfg_inited == 0) {
570 
571 		root = ddi_root_node();
572 		if ((opl_init_nodes(root, opl_init_root_nodes) != 0)) {
573 			cmn_err(CE_WARN, "IKP: init failed");
574 			return (1);
575 		}
576 
577 		opl_cfg_inited = 1;
578 	}
579 
580 	return (0);
581 }
582 
583 /*
584  * When DR is initialized, we walk the device tree and acquire a hold on
585  * all the nodes that are interesting to IKP. This is so that the corresponding
586  * branches cannot be deleted.
587  *
588  * The following function informs the walk about which nodes are interesting
589  * so that it can hold the corresponding branches.
590  */
591 static int
opl_hold_node(char * name)592 opl_hold_node(char *name)
593 {
594 	/*
595 	 * We only need to hold/release the following nodes which
596 	 * represent separate branches that must be managed.
597 	 */
598 	return ((strcmp(name, OPL_CPU_CHIP_NODE) == 0) ||
599 	    (strcmp(name, OPL_PSEUDO_MC_NODE) == 0) ||
600 	    (strcmp(name, OPL_PCI_LEAF_NODE) == 0));
601 }
602 
603 static int
opl_hold_rele_devtree(dev_info_t * rdip,void * arg)604 opl_hold_rele_devtree(dev_info_t *rdip, void *arg)
605 {
606 
607 	int	*holdp = (int *)arg;
608 	char	*name = ddi_node_name(rdip);
609 
610 	/*
611 	 * We only need to hold/release the following nodes which
612 	 * represent separate branches that must be managed.
613 	 */
614 	if (opl_hold_node(name) == 0) {
615 		/* Not of interest to us */
616 		return (DDI_WALK_PRUNECHILD);
617 	}
618 	if (*holdp) {
619 		ASSERT(!e_ddi_branch_held(rdip));
620 		e_ddi_branch_hold(rdip);
621 	} else {
622 		ASSERT(e_ddi_branch_held(rdip));
623 		e_ddi_branch_rele(rdip);
624 	}
625 
626 	return (DDI_WALK_PRUNECHILD);
627 }
628 
629 void
opl_hold_devtree()630 opl_hold_devtree()
631 {
632 	dev_info_t *dip;
633 	int hold = 1;
634 
635 	dip = ddi_root_node();
636 	ndi_devi_enter(dip);
637 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
638 	ndi_devi_exit(dip);
639 }
640 
641 void
opl_release_devtree()642 opl_release_devtree()
643 {
644 	dev_info_t *dip;
645 	int hold = 0;
646 
647 	dip = ddi_root_node();
648 	ndi_devi_enter(dip);
649 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
650 	ndi_devi_exit(dip);
651 }
652 
653 /*
654  * This is a helper function that allows opl_create_node() to return a
655  * pointer to a newly created node to its caller.
656  */
657 /*ARGSUSED*/
658 static void
opl_set_node(dev_info_t * node,void * arg,uint_t flags)659 opl_set_node(dev_info_t *node, void *arg, uint_t flags)
660 {
661 	opl_probe_t	*probe;
662 
663 	probe = arg;
664 	probe->pr_node = node;
665 }
666 
667 /*
668  * Function to create a node in the device tree under a specified parent.
669  *
670  * e_ddi_branch_create() allows the creation of a whole branch with a
671  * single call of the function. However, we only use it to create one node
672  * at a time in the case of non-I/O device nodes. In other words, we
673  * create branches by repeatedly using this function. This makes the
674  * code more readable.
675  *
676  * The branch descriptor passed to e_ddi_branch_create() takes two
677  * callbacks. The create() callback is used to set the properties of a
678  * newly created node. The other callback is used to return a pointer
679  * to the newly created node. The create() callback is passed by the
680  * caller of this function based on the kind of node it wishes to
681  * create.
682  *
683  * e_ddi_branch_create() returns with the newly created node held. We
684  * only need to hold the top nodes of the branches we create. We release
685  * the hold for the others. E.g., the "cmp" node needs to be held. Since
686  * we hold the "cmp" node, there is no need to hold the "core" and "cpu"
687  * nodes below it.
688  */
689 static dev_info_t *
opl_create_node(opl_probe_t * probe)690 opl_create_node(opl_probe_t *probe)
691 {
692 	devi_branch_t	branch;
693 
694 	probe->pr_node = NULL;
695 
696 	branch.arg = probe;
697 	branch.type = DEVI_BRANCH_SID;
698 	branch.create.sid_branch_create = probe->pr_create;
699 	branch.devi_branch_callback = opl_set_node;
700 
701 	if (e_ddi_branch_create(probe->pr_parent, &branch, NULL, 0) != 0)
702 		return (NULL);
703 
704 	ASSERT(probe->pr_node != NULL);
705 
706 	if (probe->pr_hold == 0)
707 		e_ddi_branch_rele(probe->pr_node);
708 
709 	return (probe->pr_node);
710 }
711 
712 /*
713  * Function to tear down a whole branch rooted at the specified node.
714  *
715  * Although we create each node of a branch individually, we destroy
716  * a whole branch in one call. This is more efficient.
717  */
718 static int
opl_destroy_node(dev_info_t * node)719 opl_destroy_node(dev_info_t *node)
720 {
721 	if (e_ddi_branch_destroy(node, NULL, 0) != 0) {
722 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
723 		(void) ddi_pathname(node, path);
724 		cmn_err(CE_WARN, "OPL node removal failed: %s (%p)", path,
725 		    (void *)node);
726 		kmem_free(path, MAXPATHLEN);
727 		return (-1);
728 	}
729 
730 	return (0);
731 }
732 
733 /*
734  * Set the properties for a "cpu" node.
735  */
736 /*ARGSUSED*/
737 static int
opl_create_cpu(dev_info_t * node,void * arg,uint_t flags)738 opl_create_cpu(dev_info_t *node, void *arg, uint_t flags)
739 {
740 	opl_probe_t	*probe;
741 	hwd_cpu_chip_t	*chip;
742 	hwd_core_t	*core;
743 	hwd_cpu_t	*cpu;
744 	int		ret;
745 
746 	probe = arg;
747 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
748 	core = &chip->chip_cores[probe->pr_core];
749 	cpu = &core->core_cpus[probe->pr_cpu];
750 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_NODE);
751 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CPU_NODE);
752 
753 	OPL_UPDATE_PROP(int, node, "cpuid", cpu->cpu_cpuid);
754 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_cpu);
755 
756 	OPL_UPDATE_PROP(string, node, "status", "okay");
757 
758 	return (DDI_WALK_TERMINATE);
759 }
760 
761 /*
762  * Create "cpu" nodes as child nodes of a given "core" node.
763  */
764 static int
opl_probe_cpus(opl_probe_t * probe)765 opl_probe_cpus(opl_probe_t *probe)
766 {
767 	int		i;
768 	hwd_cpu_chip_t	*chip;
769 	hwd_core_t	*core;
770 	hwd_cpu_t	*cpus;
771 
772 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
773 	core = &chip->chip_cores[probe->pr_core];
774 	cpus = &core->core_cpus[0];
775 
776 	for (i = 0; i < HWD_CPUS_PER_CORE; i++) {
777 
778 		/*
779 		 * Olympus-C has 2 cpus per core.
780 		 * Jupiter has 4 cpus per core.
781 		 * For the Olympus-C based platform, we expect the cpu_status
782 		 * of the non-existent cpus to be set to missing.
783 		 */
784 		if (!HWD_STATUS_OK(cpus[i].cpu_status))
785 			continue;
786 
787 		probe->pr_create = opl_create_cpu;
788 		probe->pr_cpu = i;
789 		if (opl_create_node(probe) == NULL) {
790 
791 			cmn_err(CE_WARN, "IKP: create cpu (%d-%d-%d-%d) failed",
792 			    probe->pr_board, probe->pr_cpu_chip, probe->pr_core,
793 			    probe->pr_cpu);
794 			return (-1);
795 		}
796 	}
797 
798 	return (0);
799 }
800 
801 /*
802  * Set the properties for a "core" node.
803  */
804 /*ARGSUSED*/
805 static int
opl_create_core(dev_info_t * node,void * arg,uint_t flags)806 opl_create_core(dev_info_t *node, void *arg, uint_t flags)
807 {
808 	opl_probe_t	*probe;
809 	hwd_cpu_chip_t	*chip;
810 	hwd_core_t	*core;
811 	int		sharing[2];
812 	int		ret;
813 
814 	probe = arg;
815 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
816 	core = &chip->chip_cores[probe->pr_core];
817 
818 	OPL_UPDATE_PROP(string, node, "name", OPL_CORE_NODE);
819 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CORE_NODE);
820 	OPL_UPDATE_PROP(string, node, "compatible", chip->chip_compatible);
821 
822 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_core);
823 	OPL_UPDATE_PROP(int, node, "manufacturer#", core->core_manufacturer);
824 	OPL_UPDATE_PROP(int, node, "implementation#",
825 	    core->core_implementation);
826 	OPL_UPDATE_PROP(int, node, "mask#", core->core_mask);
827 
828 	OPL_UPDATE_PROP(int, node, "sparc-version", 9);
829 	OPL_UPDATE_PROP(int, node, "clock-frequency", core->core_frequency);
830 
831 	OPL_UPDATE_PROP(int, node, "l1-icache-size", core->core_l1_icache_size);
832 	OPL_UPDATE_PROP(int, node, "l1-icache-line-size",
833 	    core->core_l1_icache_line_size);
834 	OPL_UPDATE_PROP(int, node, "l1-icache-associativity",
835 	    core->core_l1_icache_associativity);
836 	OPL_UPDATE_PROP(int, node, "#itlb-entries",
837 	    core->core_num_itlb_entries);
838 
839 	OPL_UPDATE_PROP(int, node, "l1-dcache-size", core->core_l1_dcache_size);
840 	OPL_UPDATE_PROP(int, node, "l1-dcache-line-size",
841 	    core->core_l1_dcache_line_size);
842 	OPL_UPDATE_PROP(int, node, "l1-dcache-associativity",
843 	    core->core_l1_dcache_associativity);
844 	OPL_UPDATE_PROP(int, node, "#dtlb-entries",
845 	    core->core_num_dtlb_entries);
846 
847 	OPL_UPDATE_PROP(int, node, "l2-cache-size", core->core_l2_cache_size);
848 	OPL_UPDATE_PROP(int, node, "l2-cache-line-size",
849 	    core->core_l2_cache_line_size);
850 	OPL_UPDATE_PROP(int, node, "l2-cache-associativity",
851 	    core->core_l2_cache_associativity);
852 	sharing[0] = 0;
853 	sharing[1] = core->core_l2_cache_sharing;
854 	OPL_UPDATE_PROP_ARRAY(int, node, "l2-cache-sharing", sharing, 2);
855 
856 	OPL_UPDATE_PROP(string, node, "status", "okay");
857 
858 	return (DDI_WALK_TERMINATE);
859 }
860 
861 /*
862  * Create "core" nodes as child nodes of a given "cmp" node.
863  *
864  * Create the branch below each "core" node".
865  */
866 static int
opl_probe_cores(opl_probe_t * probe)867 opl_probe_cores(opl_probe_t *probe)
868 {
869 	int		i;
870 	hwd_cpu_chip_t	*chip;
871 	hwd_core_t	*cores;
872 	dev_info_t	*parent, *node;
873 
874 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
875 	cores = &chip->chip_cores[0];
876 	parent = probe->pr_parent;
877 
878 	for (i = 0; i < HWD_CORES_PER_CPU_CHIP; i++) {
879 
880 		if (!HWD_STATUS_OK(cores[i].core_status))
881 			continue;
882 
883 		probe->pr_parent = parent;
884 		probe->pr_create = opl_create_core;
885 		probe->pr_core = i;
886 		node = opl_create_node(probe);
887 		if (node == NULL) {
888 
889 			cmn_err(CE_WARN, "IKP: create core (%d-%d-%d) failed",
890 			    probe->pr_board, probe->pr_cpu_chip,
891 			    probe->pr_core);
892 			return (-1);
893 		}
894 
895 		/*
896 		 * Create "cpu" nodes below "core".
897 		 */
898 		probe->pr_parent = node;
899 		if (opl_probe_cpus(probe) != 0)
900 			return (-1);
901 		probe->pr_cpu_impl |= (1 << cores[i].core_implementation);
902 	}
903 
904 	return (0);
905 }
906 
907 /*
908  * Set the properties for a "cmp" node.
909  */
910 /*ARGSUSED*/
911 static int
opl_create_cpu_chip(dev_info_t * node,void * arg,uint_t flags)912 opl_create_cpu_chip(dev_info_t *node, void *arg, uint_t flags)
913 {
914 	opl_probe_t	*probe;
915 	hwd_cpu_chip_t	*chip;
916 	opl_range_t	range;
917 	uint64_t	dummy_addr;
918 	int		ret;
919 
920 	probe = arg;
921 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
922 
923 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_CHIP_NODE);
924 
925 	OPL_UPDATE_PROP(int, node, "portid", chip->chip_portid);
926 	OPL_UPDATE_PROP(int, node, "board#", probe->pr_board);
927 
928 	dummy_addr = OPL_PROC_AS(probe->pr_board, probe->pr_cpu_chip);
929 	range.rg_addr_hi = OPL_HI(dummy_addr);
930 	range.rg_addr_lo = OPL_LO(dummy_addr);
931 	range.rg_size_hi = 0;
932 	range.rg_size_lo = 0;
933 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
934 
935 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
936 	OPL_UPDATE_PROP(int, node, "#size-cells", 0);
937 
938 	OPL_UPDATE_PROP(string, node, "status", "okay");
939 
940 	return (DDI_WALK_TERMINATE);
941 }
942 
943 /*
944  * Create "cmp" nodes as child nodes of the root node.
945  *
946  * Create the branch below each "cmp" node.
947  */
948 static int
opl_probe_cpu_chips(opl_probe_t * probe)949 opl_probe_cpu_chips(opl_probe_t *probe)
950 {
951 	int		i;
952 	dev_info_t	**cfg_cpu_chips;
953 	hwd_cpu_chip_t	*chips;
954 	dev_info_t	*node;
955 
956 	cfg_cpu_chips = opl_boards[probe->pr_board].cfg_cpu_chips;
957 	chips = &probe->pr_sb->sb_cmu.cmu_cpu_chips[0];
958 
959 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
960 
961 		ASSERT(cfg_cpu_chips[i] == NULL);
962 
963 		if (!HWD_STATUS_OK(chips[i].chip_status))
964 			continue;
965 
966 		probe->pr_parent = ddi_root_node();
967 		probe->pr_create = opl_create_cpu_chip;
968 		probe->pr_cpu_chip = i;
969 		probe->pr_hold = 1;
970 		node = opl_create_node(probe);
971 		if (node == NULL) {
972 
973 			cmn_err(CE_WARN, "IKP: create chip (%d-%d) failed",
974 			    probe->pr_board, probe->pr_cpu_chip);
975 			return (-1);
976 		}
977 
978 		cfg_cpu_chips[i] = node;
979 
980 		/*
981 		 * Create "core" nodes below "cmp".
982 		 * We hold the "cmp" node. So, there is no need to hold
983 		 * the "core" and "cpu" nodes below it.
984 		 */
985 		probe->pr_parent = node;
986 		probe->pr_hold = 0;
987 		if (opl_probe_cores(probe) != 0)
988 			return (-1);
989 	}
990 
991 	return (0);
992 }
993 
994 /*
995  * Set the properties for a "pseudo-mc" node.
996  */
997 /*ARGSUSED*/
998 static int
opl_create_pseudo_mc(dev_info_t * node,void * arg,uint_t flags)999 opl_create_pseudo_mc(dev_info_t *node, void *arg, uint_t flags)
1000 {
1001 	opl_probe_t	*probe;
1002 	int		board, portid;
1003 	hwd_bank_t	*bank;
1004 	hwd_memory_t	*mem;
1005 	opl_range_t	range;
1006 	opl_mc_addr_t	mc[HWD_BANKS_PER_CMU];
1007 	int		status[2][7];
1008 	int		i, j;
1009 	int		ret;
1010 
1011 	probe = arg;
1012 	board = probe->pr_board;
1013 
1014 	OPL_UPDATE_PROP(string, node, "name", OPL_PSEUDO_MC_NODE);
1015 	OPL_UPDATE_PROP(string, node, "device_type", "memory-controller");
1016 	OPL_UPDATE_PROP(string, node, "compatible", "FJSV,oplmc");
1017 
1018 	portid = OPL_LSB_TO_PSEUDOMC_PORTID(board);
1019 	OPL_UPDATE_PROP(int, node, "portid", portid);
1020 
1021 	range.rg_addr_hi = OPL_HI(OPL_MC_AS(board));
1022 	range.rg_addr_lo = 0x200;
1023 	range.rg_size_hi = 0;
1024 	range.rg_size_lo = 0;
1025 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
1026 
1027 	OPL_UPDATE_PROP(int, node, "board#", board);
1028 	OPL_UPDATE_PROP(int, node, "physical-board#",
1029 	    probe->pr_sb->sb_psb_number);
1030 
1031 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
1032 	OPL_UPDATE_PROP(int, node, "#size-cells", 2);
1033 
1034 	mem = &probe->pr_sb->sb_cmu.cmu_memory;
1035 
1036 	range.rg_addr_hi = OPL_HI(mem->mem_start_address);
1037 	range.rg_addr_lo = OPL_LO(mem->mem_start_address);
1038 	range.rg_size_hi = OPL_HI(mem->mem_size);
1039 	range.rg_size_lo = OPL_LO(mem->mem_size);
1040 	OPL_UPDATE_PROP_ARRAY(int, node, "sb-mem-ranges", (int *)&range, 4);
1041 
1042 	bank = probe->pr_sb->sb_cmu.cmu_memory.mem_banks;
1043 	for (i = 0, j = 0; i < HWD_BANKS_PER_CMU; i++) {
1044 
1045 		if (!HWD_STATUS_OK(bank[i].bank_status))
1046 			continue;
1047 
1048 		mc[j].mc_bank = i;
1049 		mc[j].mc_hi = OPL_HI(bank[i].bank_register_address);
1050 		mc[j].mc_lo = OPL_LO(bank[i].bank_register_address);
1051 		j++;
1052 	}
1053 
1054 	if (j > 0) {
1055 		OPL_UPDATE_PROP_ARRAY(int, node, "mc-addr", (int *)mc, j*3);
1056 	} else {
1057 		/*
1058 		 * If there is no memory, we need the mc-addr property, but
1059 		 * it is length 0.  The only way to do this using ndi seems
1060 		 * to be by creating a boolean property.
1061 		 */
1062 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node, "mc-addr");
1063 		OPL_UPDATE_PROP_ERR(ret, "mc-addr");
1064 	}
1065 
1066 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs0-mc-pa-trans-table",
1067 	    mem->mem_cs[0].cs_pa_mac_table, 64);
1068 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs1-mc-pa-trans-table",
1069 	    mem->mem_cs[1].cs_pa_mac_table, 64);
1070 
1071 #define	CS_PER_MEM 2
1072 
1073 	for (i = 0, j = 0; i < CS_PER_MEM; i++) {
1074 		if (HWD_STATUS_OK(mem->mem_cs[i].cs_status) ||
1075 		    HWD_STATUS_FAILED(mem->mem_cs[i].cs_status)) {
1076 			status[j][0] = i;
1077 			if (HWD_STATUS_OK(mem->mem_cs[i].cs_status))
1078 				status[j][1] = 0;
1079 			else
1080 				status[j][1] = 1;
1081 			status[j][2] =
1082 			    OPL_HI(mem->mem_cs[i].cs_available_capacity);
1083 			status[j][3] =
1084 			    OPL_LO(mem->mem_cs[i].cs_available_capacity);
1085 			status[j][4] = OPL_HI(mem->mem_cs[i].cs_dimm_capacity);
1086 			status[j][5] = OPL_LO(mem->mem_cs[i].cs_dimm_capacity);
1087 			status[j][6] = mem->mem_cs[i].cs_number_of_dimms;
1088 			j++;
1089 		}
1090 	}
1091 
1092 	if (j > 0) {
1093 		OPL_UPDATE_PROP_ARRAY(int, node, "cs-status", (int *)status,
1094 		    j*7);
1095 	} else {
1096 		/*
1097 		 * If there is no memory, we need the cs-status property, but
1098 		 * it is length 0.  The only way to do this using ndi seems
1099 		 * to be by creating a boolean property.
1100 		 */
1101 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node,
1102 		    "cs-status");
1103 		OPL_UPDATE_PROP_ERR(ret, "cs-status");
1104 	}
1105 
1106 	return (DDI_WALK_TERMINATE);
1107 }
1108 
1109 /*
1110  * Create "pseudo-mc" nodes
1111  */
1112 static int
opl_probe_memory(opl_probe_t * probe)1113 opl_probe_memory(opl_probe_t *probe)
1114 {
1115 	int		board;
1116 	opl_board_cfg_t	*board_cfg;
1117 	dev_info_t	*node;
1118 
1119 	board = probe->pr_board;
1120 	board_cfg = &opl_boards[board];
1121 
1122 	ASSERT(board_cfg->cfg_pseudo_mc == NULL);
1123 
1124 	probe->pr_parent = ddi_root_node();
1125 	probe->pr_create = opl_create_pseudo_mc;
1126 	probe->pr_hold = 1;
1127 	node = opl_create_node(probe);
1128 	if (node == NULL) {
1129 
1130 		cmn_err(CE_WARN, "IKP: create pseudo-mc (%d) failed", board);
1131 		return (-1);
1132 	}
1133 
1134 	board_cfg->cfg_pseudo_mc = node;
1135 
1136 	return (0);
1137 }
1138 
1139 /*
1140  * Allocate the fcode ops handle.
1141  */
1142 /*ARGSUSED*/
1143 static
1144 fco_handle_t
opl_fc_ops_alloc_handle(dev_info_t * parent,dev_info_t * child,void * fcode,size_t fcode_size,char * unit_address,char * my_args)1145 opl_fc_ops_alloc_handle(dev_info_t *parent, dev_info_t *child,
1146     void *fcode, size_t fcode_size, char *unit_address,
1147     char *my_args)
1148 {
1149 	fco_handle_t	rp;
1150 	phandle_t	h;
1151 	char		*buf;
1152 
1153 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
1154 	rp->next_handle = fc_ops_alloc_handle(parent, child, fcode, fcode_size,
1155 	    unit_address, NULL);
1156 	rp->ap = parent;
1157 	rp->child = child;
1158 	rp->fcode = fcode;
1159 	rp->fcode_size = fcode_size;
1160 	rp->my_args = my_args;
1161 
1162 	if (unit_address) {
1163 		buf = kmem_zalloc(UNIT_ADDR_SIZE, KM_SLEEP);
1164 		(void) strcpy(buf, unit_address);
1165 		rp->unit_address = buf;
1166 	}
1167 
1168 	/*
1169 	 * Add the child's nodeid to our table...
1170 	 */
1171 	h = ddi_get_nodeid(rp->child);
1172 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h);
1173 
1174 	return (rp);
1175 }
1176 
1177 
1178 static void
opl_fc_ops_free_handle(fco_handle_t rp)1179 opl_fc_ops_free_handle(fco_handle_t rp)
1180 {
1181 	struct fc_resource	*resp, *nresp;
1182 
1183 	ASSERT(rp);
1184 
1185 	if (rp->next_handle)
1186 		fc_ops_free_handle(rp->next_handle);
1187 	if (rp->unit_address)
1188 		kmem_free(rp->unit_address, UNIT_ADDR_SIZE);
1189 
1190 	/*
1191 	 * Release all the resources from the resource list
1192 	 */
1193 	for (resp = rp->head; resp != NULL; resp = nresp) {
1194 		nresp = resp->next;
1195 		switch (resp->type) {
1196 
1197 		case RT_MAP:
1198 			/*
1199 			 * If this is still mapped, we'd better unmap it now,
1200 			 * or all our structures that are tracking it will
1201 			 * be leaked.
1202 			 */
1203 			if (resp->fc_map_handle != NULL)
1204 				opl_unmap_phys(&resp->fc_map_handle);
1205 			break;
1206 
1207 		case RT_DMA:
1208 			/*
1209 			 * DMA has to be freed up at exit time.
1210 			 */
1211 			cmn_err(CE_CONT,
1212 			    "opl_fc_ops_free_handle: Unexpected DMA seen!");
1213 			break;
1214 
1215 		case RT_CONTIGIOUS:
1216 			FC_DEBUG2(1, CE_CONT, "opl_fc_ops_free: "
1217 			    "Free claim-memory resource 0x%lx size 0x%x\n",
1218 			    resp->fc_contig_virt, resp->fc_contig_len);
1219 
1220 			(void) ndi_ra_free(ddi_root_node(),
1221 			    (uint64_t)resp->fc_contig_virt,
1222 			    resp->fc_contig_len, "opl-fcodemem",
1223 			    NDI_RA_PASS);
1224 
1225 			break;
1226 
1227 		default:
1228 			cmn_err(CE_CONT, "opl_fc_ops_free: "
1229 			    "unknown resource type %d", resp->type);
1230 			break;
1231 		}
1232 		fc_rem_resource(rp, resp);
1233 		kmem_free(resp, sizeof (struct fc_resource));
1234 	}
1235 
1236 	kmem_free(rp, sizeof (struct fc_resource_list));
1237 }
1238 
1239 int
opl_fc_do_op(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1240 opl_fc_do_op(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1241 {
1242 	opl_fc_ops_t	*op;
1243 	char		*service = fc_cell2ptr(cp->svc_name);
1244 
1245 	ASSERT(rp);
1246 
1247 	FC_DEBUG1(1, CE_CONT, "opl_fc_do_op: <%s>\n", service);
1248 
1249 	/*
1250 	 * First try the generic fc_ops.
1251 	 */
1252 	if (fc_ops(ap, rp->next_handle, cp) == 0)
1253 		return (0);
1254 
1255 	/*
1256 	 * Now try the Jupiter-specific ops.
1257 	 */
1258 	for (op = opl_fc_ops; op->fc_service != NULL; ++op)
1259 		if (strcmp(op->fc_service, service) == 0)
1260 			return (op->fc_op(ap, rp, cp));
1261 
1262 	FC_DEBUG1(9, CE_CONT, "opl_fc_do_op: <%s> not serviced\n", service);
1263 
1264 	return (-1);
1265 }
1266 
1267 /*
1268  * map-in  (phys.lo phys.hi size -- virt)
1269  */
1270 static int
opl_map_in(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1271 opl_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1272 {
1273 	size_t			len;
1274 	int			error;
1275 	caddr_t			virt;
1276 	struct fc_resource	*resp;
1277 	struct regspec		rspec;
1278 	ddi_device_acc_attr_t	acc;
1279 	ddi_acc_handle_t	h;
1280 
1281 	if (fc_cell2int(cp->nargs) != 3)
1282 		return (fc_syntax_error(cp, "nargs must be 3"));
1283 
1284 	if (fc_cell2int(cp->nresults) < 1)
1285 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1286 
1287 	rspec.regspec_size = len = fc_cell2size(fc_arg(cp, 0));
1288 	rspec.regspec_bustype = fc_cell2uint(fc_arg(cp, 1));
1289 	rspec.regspec_addr = fc_cell2uint(fc_arg(cp, 2));
1290 
1291 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1292 	acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
1293 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1294 
1295 	FC_DEBUG3(1, CE_CONT, "opl_map_in: attempting map in "
1296 	    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
1297 	    rspec.regspec_addr, rspec.regspec_size);
1298 
1299 	error = opl_map_phys(rp->child, &rspec, &virt, &acc, &h);
1300 
1301 	if (error)  {
1302 		FC_DEBUG3(1, CE_CONT, "opl_map_in: map in failed - "
1303 		    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
1304 		    rspec.regspec_addr, rspec.regspec_size);
1305 
1306 		return (fc_priv_error(cp, "opl map-in failed"));
1307 	}
1308 
1309 	FC_DEBUG1(3, CE_CONT, "opl_map_in: returning virt %p\n", virt);
1310 
1311 	cp->nresults = fc_int2cell(1);
1312 	fc_result(cp, 0) = fc_ptr2cell(virt);
1313 
1314 	/*
1315 	 * Log this resource ...
1316 	 */
1317 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
1318 	resp->type = RT_MAP;
1319 	resp->fc_map_virt = virt;
1320 	resp->fc_map_len = len;
1321 	resp->fc_map_handle = h;
1322 	fc_add_resource(rp, resp);
1323 
1324 	return (fc_success_op(ap, rp, cp));
1325 }
1326 
1327 /*
1328  * map-out (virt size -- )
1329  */
1330 static int
opl_map_out(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1331 opl_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1332 {
1333 	caddr_t			virt;
1334 	size_t			len;
1335 	struct fc_resource	*resp;
1336 
1337 	if (fc_cell2int(cp->nargs) != 2)
1338 		return (fc_syntax_error(cp, "nargs must be 2"));
1339 
1340 	virt = fc_cell2ptr(fc_arg(cp, 1));
1341 
1342 	len = fc_cell2size(fc_arg(cp, 0));
1343 
1344 	FC_DEBUG2(1, CE_CONT, "opl_map_out: attempting map out %p %x\n",
1345 	    virt, len);
1346 
1347 	/*
1348 	 * Find if this request matches a mapping resource we set up.
1349 	 */
1350 	fc_lock_resource_list(rp);
1351 	for (resp = rp->head; resp != NULL; resp = resp->next) {
1352 		if (resp->type != RT_MAP)
1353 			continue;
1354 		if (resp->fc_map_virt != virt)
1355 			continue;
1356 		if (resp->fc_map_len == len)
1357 			break;
1358 	}
1359 	fc_unlock_resource_list(rp);
1360 
1361 	if (resp == NULL)
1362 		return (fc_priv_error(cp, "request doesn't match a "
1363 		    "known mapping"));
1364 
1365 	opl_unmap_phys(&resp->fc_map_handle);
1366 
1367 	/*
1368 	 * remove the resource from the list and release it.
1369 	 */
1370 	fc_rem_resource(rp, resp);
1371 	kmem_free(resp, sizeof (struct fc_resource));
1372 
1373 	cp->nresults = fc_int2cell(0);
1374 	return (fc_success_op(ap, rp, cp));
1375 }
1376 
1377 static int
opl_register_fetch(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1378 opl_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1379 {
1380 	size_t			len;
1381 	caddr_t			virt;
1382 	int			error = 0;
1383 	uint64_t		v;
1384 	uint64_t		x;
1385 	uint32_t		l;
1386 	uint16_t		w;
1387 	uint8_t			b;
1388 	char			*service = fc_cell2ptr(cp->svc_name);
1389 	struct fc_resource	*resp;
1390 
1391 	if (fc_cell2int(cp->nargs) != 1)
1392 		return (fc_syntax_error(cp, "nargs must be 1"));
1393 
1394 	if (fc_cell2int(cp->nresults) < 1)
1395 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1396 
1397 	virt = fc_cell2ptr(fc_arg(cp, 0));
1398 
1399 	/*
1400 	 * Determine the access width .. we can switch on the 2nd
1401 	 * character of the name which is "rx@", "rl@", "rb@" or "rw@"
1402 	 */
1403 	switch (*(service + 1)) {
1404 	case 'x':	len = sizeof (x); break;
1405 	case 'l':	len = sizeof (l); break;
1406 	case 'w':	len = sizeof (w); break;
1407 	case 'b':	len = sizeof (b); break;
1408 	}
1409 
1410 	/*
1411 	 * Check the alignment ...
1412 	 */
1413 	if (((intptr_t)virt & (len - 1)) != 0)
1414 		return (fc_priv_error(cp, "unaligned access"));
1415 
1416 	/*
1417 	 * Find if this virt is 'within' a request we know about
1418 	 */
1419 	fc_lock_resource_list(rp);
1420 	for (resp = rp->head; resp != NULL; resp = resp->next) {
1421 		if (resp->type == RT_MAP) {
1422 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
1423 			    ((virt + len) <=
1424 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
1425 				break;
1426 		} else if (resp->type == RT_CONTIGIOUS) {
1427 			if ((virt >= (caddr_t)resp->fc_contig_virt) &&
1428 			    ((virt + len) <= ((caddr_t)resp->fc_contig_virt +
1429 			    resp->fc_contig_len)))
1430 				break;
1431 		}
1432 	}
1433 	fc_unlock_resource_list(rp);
1434 
1435 	if (resp == NULL) {
1436 		return (fc_priv_error(cp, "request not within "
1437 		    "known mappings"));
1438 	}
1439 
1440 	switch (len) {
1441 	case sizeof (x):
1442 		if (resp->type == RT_MAP)
1443 			error = ddi_peek64(rp->child, (int64_t *)virt,
1444 			    (int64_t *)&x);
1445 		else /* RT_CONTIGIOUS */
1446 			x = *(int64_t *)virt;
1447 		v = x;
1448 		break;
1449 	case sizeof (l):
1450 		if (resp->type == RT_MAP)
1451 			error = ddi_peek32(rp->child, (int32_t *)virt,
1452 			    (int32_t *)&l);
1453 		else /* RT_CONTIGIOUS */
1454 			l = *(int32_t *)virt;
1455 		v = l;
1456 		break;
1457 	case sizeof (w):
1458 		if (resp->type == RT_MAP)
1459 			error = ddi_peek16(rp->child, (int16_t *)virt,
1460 			    (int16_t *)&w);
1461 		else /* RT_CONTIGIOUS */
1462 			w = *(int16_t *)virt;
1463 		v = w;
1464 		break;
1465 	case sizeof (b):
1466 		if (resp->type == RT_MAP)
1467 			error = ddi_peek8(rp->child, (int8_t *)virt,
1468 			    (int8_t *)&b);
1469 		else /* RT_CONTIGIOUS */
1470 			b = *(int8_t *)virt;
1471 		v = b;
1472 		break;
1473 	}
1474 
1475 	if (error == DDI_FAILURE) {
1476 		FC_DEBUG2(1, CE_CONT, "opl_register_fetch: access error "
1477 		    "accessing virt %p len %d\n", virt, len);
1478 		return (fc_priv_error(cp, "access error"));
1479 	}
1480 
1481 	FC_DEBUG3(1, CE_CONT, "register_fetch (%s) %llx %llx\n",
1482 	    service, virt, v);
1483 
1484 	cp->nresults = fc_int2cell(1);
1485 	switch (len) {
1486 	case sizeof (x): fc_result(cp, 0) = x; break;
1487 	case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break;
1488 	case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break;
1489 	case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break;
1490 	}
1491 	return (fc_success_op(ap, rp, cp));
1492 }
1493 
1494 static int
opl_register_store(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1495 opl_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1496 {
1497 	size_t			len;
1498 	caddr_t			virt;
1499 	uint64_t		v;
1500 	uint64_t		x;
1501 	uint32_t		l;
1502 	uint16_t		w;
1503 	uint8_t			b;
1504 	char			*service = fc_cell2ptr(cp->svc_name);
1505 	struct fc_resource	*resp;
1506 	int			error = 0;
1507 
1508 	if (fc_cell2int(cp->nargs) != 2)
1509 		return (fc_syntax_error(cp, "nargs must be 2"));
1510 
1511 	virt = fc_cell2ptr(fc_arg(cp, 0));
1512 
1513 	/*
1514 	 * Determine the access width .. we can switch on the 2nd
1515 	 * character of the name which is "rx!", "rl!", "rb!" or "rw!"
1516 	 */
1517 	switch (*(service + 1)) {
1518 	case 'x':
1519 		len = sizeof (x);
1520 		x = fc_arg(cp, 1);
1521 		v = x;
1522 		break;
1523 	case 'l':
1524 		len = sizeof (l);
1525 		l = fc_cell2uint32_t(fc_arg(cp, 1));
1526 		v = l;
1527 		break;
1528 	case 'w':
1529 		len = sizeof (w);
1530 		w = fc_cell2uint16_t(fc_arg(cp, 1));
1531 		v = w;
1532 		break;
1533 	case 'b':
1534 		len = sizeof (b);
1535 		b = fc_cell2uint8_t(fc_arg(cp, 1));
1536 		v = b;
1537 		break;
1538 	}
1539 
1540 	FC_DEBUG3(1, CE_CONT, "register_store (%s) %llx %llx\n",
1541 	    service, virt, v);
1542 
1543 	/*
1544 	 * Check the alignment ...
1545 	 */
1546 	if (((intptr_t)virt & (len - 1)) != 0)
1547 		return (fc_priv_error(cp, "unaligned access"));
1548 
1549 	/*
1550 	 * Find if this virt is 'within' a request we know about
1551 	 */
1552 	fc_lock_resource_list(rp);
1553 	for (resp = rp->head; resp != NULL; resp = resp->next) {
1554 		if (resp->type == RT_MAP) {
1555 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
1556 			    ((virt + len) <=
1557 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
1558 				break;
1559 		} else if (resp->type == RT_CONTIGIOUS) {
1560 			if ((virt >= (caddr_t)resp->fc_contig_virt) &&
1561 			    ((virt + len) <= ((caddr_t)resp->fc_contig_virt +
1562 			    resp->fc_contig_len)))
1563 				break;
1564 		}
1565 	}
1566 	fc_unlock_resource_list(rp);
1567 
1568 	if (resp == NULL)
1569 		return (fc_priv_error(cp, "request not within"
1570 		    "known mappings"));
1571 
1572 	switch (len) {
1573 	case sizeof (x):
1574 		if (resp->type == RT_MAP)
1575 			error = ddi_poke64(rp->child, (int64_t *)virt, x);
1576 		else if (resp->type == RT_CONTIGIOUS)
1577 			*(uint64_t *)virt = x;
1578 		break;
1579 	case sizeof (l):
1580 		if (resp->type == RT_MAP)
1581 			error = ddi_poke32(rp->child, (int32_t *)virt, l);
1582 		else if (resp->type == RT_CONTIGIOUS)
1583 			*(uint32_t *)virt = l;
1584 		break;
1585 	case sizeof (w):
1586 		if (resp->type == RT_MAP)
1587 			error = ddi_poke16(rp->child, (int16_t *)virt, w);
1588 		else if (resp->type == RT_CONTIGIOUS)
1589 			*(uint16_t *)virt = w;
1590 		break;
1591 	case sizeof (b):
1592 		if (resp->type == RT_MAP)
1593 			error = ddi_poke8(rp->child, (int8_t *)virt, b);
1594 		else if (resp->type == RT_CONTIGIOUS)
1595 			*(uint8_t *)virt = b;
1596 		break;
1597 	}
1598 
1599 	if (error == DDI_FAILURE) {
1600 		FC_DEBUG2(1, CE_CONT, "opl_register_store: access error "
1601 		    "accessing virt %p len %d\n", virt, len);
1602 		return (fc_priv_error(cp, "access error"));
1603 	}
1604 
1605 	cp->nresults = fc_int2cell(0);
1606 	return (fc_success_op(ap, rp, cp));
1607 }
1608 
1609 /*
1610  * opl_claim_memory
1611  *
1612  * claim-memory (align size vhint -- vaddr)
1613  */
1614 static int
opl_claim_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1615 opl_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1616 {
1617 	int			align, size, vhint;
1618 	uint64_t		answer, alen;
1619 	ndi_ra_request_t	request;
1620 	struct fc_resource	*resp;
1621 
1622 	if (fc_cell2int(cp->nargs) != 3)
1623 		return (fc_syntax_error(cp, "nargs must be 3"));
1624 
1625 	if (fc_cell2int(cp->nresults) < 1)
1626 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1627 
1628 	vhint = fc_cell2int(fc_arg(cp, 2));
1629 	size  = fc_cell2int(fc_arg(cp, 1));
1630 	align = fc_cell2int(fc_arg(cp, 0));
1631 
1632 	FC_DEBUG3(1, CE_CONT, "opl_claim_memory: align=0x%x size=0x%x "
1633 	    "vhint=0x%x\n", align, size, vhint);
1634 
1635 	if (size == 0) {
1636 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
1637 		    "contiguous memory of size zero\n");
1638 		return (fc_priv_error(cp, "allocation error"));
1639 	}
1640 
1641 	if (vhint) {
1642 		cmn_err(CE_WARN, "opl_claim_memory - vhint is not zero "
1643 		    "vhint=0x%x - Ignoring Argument\n", vhint);
1644 	}
1645 
1646 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
1647 	request.ra_flags	= NDI_RA_ALLOC_BOUNDED;
1648 	request.ra_boundbase	= 0;
1649 	request.ra_boundlen	= 0xffffffff;
1650 	request.ra_len		= size;
1651 	request.ra_align_mask	= align - 1;
1652 
1653 	if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen,
1654 	    "opl-fcodemem", NDI_RA_PASS) != NDI_SUCCESS) {
1655 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
1656 		    "contiguous memory\n");
1657 		return (fc_priv_error(cp, "allocation error"));
1658 	}
1659 
1660 	FC_DEBUG2(1, CE_CONT, "opl_claim_memory: address allocated=0x%lx "
1661 	    "size=0x%x\n", answer, alen);
1662 
1663 	cp->nresults = fc_int2cell(1);
1664 	fc_result(cp, 0) = answer;
1665 
1666 	/*
1667 	 * Log this resource ...
1668 	 */
1669 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
1670 	resp->type = RT_CONTIGIOUS;
1671 	resp->fc_contig_virt = (void *)answer;
1672 	resp->fc_contig_len = size;
1673 	fc_add_resource(rp, resp);
1674 
1675 	return (fc_success_op(ap, rp, cp));
1676 }
1677 
1678 /*
1679  * opl_release_memory
1680  *
1681  * release-memory (size vaddr -- )
1682  */
1683 static int
opl_release_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1684 opl_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1685 {
1686 	int32_t			vaddr, size;
1687 	struct fc_resource	*resp;
1688 
1689 	if (fc_cell2int(cp->nargs) != 2)
1690 		return (fc_syntax_error(cp, "nargs must be 2"));
1691 
1692 	if (fc_cell2int(cp->nresults) != 0)
1693 		return (fc_syntax_error(cp, "nresults must be 0"));
1694 
1695 	vaddr = fc_cell2int(fc_arg(cp, 1));
1696 	size  = fc_cell2int(fc_arg(cp, 0));
1697 
1698 	FC_DEBUG2(1, CE_CONT, "opl_release_memory: vaddr=0x%x size=0x%x\n",
1699 	    vaddr, size);
1700 
1701 	/*
1702 	 * Find if this request matches a mapping resource we set up.
1703 	 */
1704 	fc_lock_resource_list(rp);
1705 	for (resp = rp->head; resp != NULL; resp = resp->next) {
1706 		if (resp->type != RT_CONTIGIOUS)
1707 			continue;
1708 		if (resp->fc_contig_virt != (void *)(uintptr_t)vaddr)
1709 			continue;
1710 		if (resp->fc_contig_len == size)
1711 			break;
1712 	}
1713 	fc_unlock_resource_list(rp);
1714 
1715 	if (resp == NULL)
1716 		return (fc_priv_error(cp, "request doesn't match a "
1717 		    "known mapping"));
1718 
1719 	(void) ndi_ra_free(ddi_root_node(), vaddr, size,
1720 	    "opl-fcodemem", NDI_RA_PASS);
1721 
1722 	/*
1723 	 * remove the resource from the list and release it.
1724 	 */
1725 	fc_rem_resource(rp, resp);
1726 	kmem_free(resp, sizeof (struct fc_resource));
1727 
1728 	cp->nresults = fc_int2cell(0);
1729 
1730 	return (fc_success_op(ap, rp, cp));
1731 }
1732 
1733 /*
1734  * opl_vtop
1735  *
1736  * vtop (vaddr -- paddr.lo paddr.hi)
1737  */
1738 static int
opl_vtop(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1739 opl_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1740 {
1741 	int			vaddr;
1742 	uint64_t		paddr;
1743 	struct fc_resource	*resp;
1744 
1745 	if (fc_cell2int(cp->nargs) != 1)
1746 		return (fc_syntax_error(cp, "nargs must be 1"));
1747 
1748 	if (fc_cell2int(cp->nresults) >= 3)
1749 		return (fc_syntax_error(cp, "nresults must be less than 2"));
1750 
1751 	vaddr = fc_cell2int(fc_arg(cp, 0));
1752 
1753 	/*
1754 	 * Find if this request matches a mapping resource we set up.
1755 	 */
1756 	fc_lock_resource_list(rp);
1757 	for (resp = rp->head; resp != NULL; resp = resp->next) {
1758 		if (resp->type != RT_CONTIGIOUS)
1759 			continue;
1760 		if (((uint64_t)resp->fc_contig_virt <= vaddr) &&
1761 		    (vaddr < (uint64_t)resp->fc_contig_virt +
1762 		    resp->fc_contig_len))
1763 			break;
1764 	}
1765 	fc_unlock_resource_list(rp);
1766 
1767 	if (resp == NULL)
1768 		return (fc_priv_error(cp, "request doesn't match a "
1769 		    "known mapping"));
1770 
1771 	paddr = va_to_pa((void *)(uintptr_t)vaddr);
1772 
1773 	FC_DEBUG2(1, CE_CONT, "opl_vtop: vaddr=0x%x paddr=0x%x\n",
1774 	    vaddr, paddr);
1775 
1776 	cp->nresults = fc_int2cell(2);
1777 
1778 	fc_result(cp, 0) = paddr;
1779 	fc_result(cp, 1) = 0;
1780 
1781 	return (fc_success_op(ap, rp, cp));
1782 }
1783 
1784 static int
opl_config_child(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1785 opl_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1786 {
1787 	fc_phandle_t h;
1788 
1789 	if (fc_cell2int(cp->nargs) != 0)
1790 		return (fc_syntax_error(cp, "nargs must be 0"));
1791 
1792 	if (fc_cell2int(cp->nresults) < 1)
1793 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1794 
1795 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child);
1796 
1797 	cp->nresults = fc_int2cell(1);
1798 	fc_result(cp, 0) = fc_phandle2cell(h);
1799 
1800 	return (fc_success_op(ap, rp, cp));
1801 }
1802 
1803 static int
opl_get_fcode(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1804 opl_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1805 {
1806 	caddr_t		dropin_name_virt, fcode_virt;
1807 	char		*dropin_name, *fcode;
1808 	int		fcode_len, status;
1809 
1810 	if (fc_cell2int(cp->nargs) != 3)
1811 		return (fc_syntax_error(cp, "nargs must be 3"));
1812 
1813 	if (fc_cell2int(cp->nresults) < 1)
1814 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1815 
1816 	dropin_name_virt = fc_cell2ptr(fc_arg(cp, 0));
1817 
1818 	fcode_virt = fc_cell2ptr(fc_arg(cp, 1));
1819 
1820 	fcode_len = fc_cell2int(fc_arg(cp, 2));
1821 
1822 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
1823 
1824 	FC_DEBUG2(1, CE_CONT, "get_fcode: %x %d\n", fcode_virt, fcode_len);
1825 
1826 	if (copyinstr(fc_cell2ptr(dropin_name_virt), dropin_name,
1827 	    FC_SVC_NAME_LEN - 1, NULL))  {
1828 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode: "
1829 		    "fault copying in drop in name %p\n", dropin_name_virt);
1830 		status = 0;
1831 	} else {
1832 		FC_DEBUG1(1, CE_CONT, "get_fcode: %s\n", dropin_name);
1833 
1834 		fcode = kmem_zalloc(fcode_len, KM_SLEEP);
1835 
1836 		if ((status = prom_get_fcode(dropin_name, fcode)) != 0) {
1837 
1838 			if (copyout((void *)fcode, (void *)fcode_virt,
1839 			    fcode_len)) {
1840 				cmn_err(CE_WARN, " opl_get_fcode: Unable "
1841 				    "to copy out fcode image");
1842 				status = 0;
1843 			}
1844 		}
1845 
1846 		kmem_free(fcode, fcode_len);
1847 	}
1848 
1849 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
1850 
1851 	cp->nresults = fc_int2cell(1);
1852 	fc_result(cp, 0) = status;
1853 
1854 	return (fc_success_op(ap, rp, cp));
1855 }
1856 
1857 static int
opl_get_fcode_size(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1858 opl_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1859 {
1860 	caddr_t		virt;
1861 	char		*dropin_name;
1862 	int		len;
1863 
1864 	if (fc_cell2int(cp->nargs) != 1)
1865 		return (fc_syntax_error(cp, "nargs must be 1"));
1866 
1867 	if (fc_cell2int(cp->nresults) < 1)
1868 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1869 
1870 	virt = fc_cell2ptr(fc_arg(cp, 0));
1871 
1872 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
1873 
1874 	FC_DEBUG0(1, CE_CONT, "opl_get_fcode_size:\n");
1875 
1876 	if (copyinstr(fc_cell2ptr(virt), dropin_name,
1877 	    FC_SVC_NAME_LEN - 1, NULL))  {
1878 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: "
1879 		    "fault copying in drop in name %p\n", virt);
1880 		len = 0;
1881 	} else {
1882 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: %s\n", dropin_name);
1883 
1884 		len = prom_get_fcode_size(dropin_name);
1885 	}
1886 
1887 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
1888 
1889 	FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: fcode_len = %d\n", len);
1890 
1891 	cp->nresults = fc_int2cell(1);
1892 	fc_result(cp, 0) = len;
1893 
1894 	return (fc_success_op(ap, rp, cp));
1895 }
1896 
1897 static int
opl_map_phys(dev_info_t * dip,struct regspec * phys_spec,caddr_t * addrp,ddi_device_acc_attr_t * accattrp,ddi_acc_handle_t * handlep)1898 opl_map_phys(dev_info_t *dip, struct regspec *phys_spec,
1899     caddr_t *addrp, ddi_device_acc_attr_t *accattrp,
1900     ddi_acc_handle_t *handlep)
1901 {
1902 	ddi_map_req_t	mapreq;
1903 	ddi_acc_hdl_t	*acc_handlep;
1904 	int		result;
1905 	struct regspec	*rspecp;
1906 
1907 	*handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL);
1908 	acc_handlep = impl_acc_hdl_get(*handlep);
1909 	acc_handlep->ah_vers = VERS_ACCHDL;
1910 	acc_handlep->ah_dip = dip;
1911 	acc_handlep->ah_rnumber = 0;
1912 	acc_handlep->ah_offset = 0;
1913 	acc_handlep->ah_len = 0;
1914 	acc_handlep->ah_acc = *accattrp;
1915 	rspecp = kmem_zalloc(sizeof (struct regspec), KM_SLEEP);
1916 	*rspecp = *phys_spec;
1917 	/*
1918 	 * cache a copy of the reg spec
1919 	 */
1920 	acc_handlep->ah_bus_private = rspecp;
1921 
1922 	mapreq.map_op = DDI_MO_MAP_LOCKED;
1923 	mapreq.map_type = DDI_MT_REGSPEC;
1924 	mapreq.map_obj.rp = (struct regspec *)phys_spec;
1925 	mapreq.map_prot = PROT_READ | PROT_WRITE;
1926 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
1927 	mapreq.map_handlep = acc_handlep;
1928 	mapreq.map_vers = DDI_MAP_VERSION;
1929 
1930 	result = ddi_map(dip, &mapreq, 0, 0, addrp);
1931 
1932 	if (result != DDI_SUCCESS) {
1933 		impl_acc_hdl_free(*handlep);
1934 		kmem_free(rspecp, sizeof (struct regspec));
1935 		*handlep = (ddi_acc_handle_t)NULL;
1936 	} else {
1937 		acc_handlep->ah_addr = *addrp;
1938 	}
1939 
1940 	return (result);
1941 }
1942 
1943 static void
opl_unmap_phys(ddi_acc_handle_t * handlep)1944 opl_unmap_phys(ddi_acc_handle_t *handlep)
1945 {
1946 	ddi_map_req_t	mapreq;
1947 	ddi_acc_hdl_t	*acc_handlep;
1948 	struct regspec	*rspecp;
1949 
1950 	acc_handlep = impl_acc_hdl_get(*handlep);
1951 	ASSERT(acc_handlep);
1952 	rspecp = acc_handlep->ah_bus_private;
1953 
1954 	mapreq.map_op = DDI_MO_UNMAP;
1955 	mapreq.map_type = DDI_MT_REGSPEC;
1956 	mapreq.map_obj.rp = (struct regspec *)rspecp;
1957 	mapreq.map_prot = PROT_READ | PROT_WRITE;
1958 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
1959 	mapreq.map_handlep = acc_handlep;
1960 	mapreq.map_vers = DDI_MAP_VERSION;
1961 
1962 	(void) ddi_map(acc_handlep->ah_dip, &mapreq, acc_handlep->ah_offset,
1963 	    acc_handlep->ah_len, &acc_handlep->ah_addr);
1964 
1965 	impl_acc_hdl_free(*handlep);
1966 	/*
1967 	 * Free the cached copy
1968 	 */
1969 	kmem_free(rspecp, sizeof (struct regspec));
1970 	*handlep = (ddi_acc_handle_t)NULL;
1971 }
1972 
1973 static int
opl_get_hwd_va(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)1974 opl_get_hwd_va(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
1975 {
1976 	uint32_t	portid;
1977 	void		*hwd_virt;
1978 	hwd_header_t	*hwd_h = NULL;
1979 	hwd_sb_t	*hwd_sb = NULL;
1980 	int		lsb, ch, leaf;
1981 	int		status = 1;
1982 
1983 	/* Check the argument */
1984 	if (fc_cell2int(cp->nargs) != 2)
1985 		return (fc_syntax_error(cp, "nargs must be 2"));
1986 
1987 	if (fc_cell2int(cp->nresults) < 1)
1988 		return (fc_syntax_error(cp, "nresults must be >= 1"));
1989 
1990 	/* Get the parameters */
1991 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
1992 	hwd_virt = (void *)fc_cell2ptr(fc_arg(cp, 1));
1993 
1994 	/* Get the ID numbers */
1995 	lsb  = OPL_IO_PORTID_TO_LSB(portid);
1996 	ch   = OPL_PORTID_TO_CHANNEL(portid);
1997 	leaf = OPL_PORTID_TO_LEAF(portid);
1998 	ASSERT(OPL_IO_PORTID(lsb, ch, leaf) == portid);
1999 
2000 	/* Set the pointer of hwd. */
2001 	if ((hwd_h = (hwd_header_t *)opl_boards[lsb].cfg_hwd) == NULL) {
2002 		return (fc_priv_error(cp, "null hwd header"));
2003 	}
2004 	/* Set the pointer of hwd sb. */
2005 	if ((hwd_sb = (hwd_sb_t *)((char *)hwd_h + hwd_h->hdr_sb_info_offset))
2006 	    == NULL) {
2007 		return (fc_priv_error(cp, "null hwd sb"));
2008 	}
2009 
2010 	if (ch == OPL_CMU_CHANNEL) {
2011 		/* Copyout CMU-CH HW Descriptor */
2012 		if (copyout((void *)&hwd_sb->sb_cmu.cmu_ch,
2013 		    (void *)hwd_virt, sizeof (hwd_cmu_chan_t))) {
2014 			cmn_err(CE_WARN, "opl_get_hwd_va: "
2015 			"Unable to copy out cmuch descriptor for %x",
2016 			    portid);
2017 			status = 0;
2018 		}
2019 	} else {
2020 		/* Copyout PCI-CH HW Descriptor */
2021 		if (copyout((void *)&hwd_sb->sb_pci_ch[ch].pci_leaf[leaf],
2022 		    (void *)hwd_virt, sizeof (hwd_leaf_t))) {
2023 			cmn_err(CE_WARN, "opl_get_hwd_va: "
2024 			"Unable to copy out pcich descriptor for %x",
2025 			    portid);
2026 			status = 0;
2027 		}
2028 	}
2029 
2030 	cp->nresults = fc_int2cell(1);
2031 	fc_result(cp, 0) = status;
2032 
2033 	return (fc_success_op(ap, rp, cp));
2034 }
2035 
2036 /*
2037  * After Solaris boots, a user can enter OBP using L1A, etc. While in OBP,
2038  * interrupts may be received from PCI devices. These interrupts
2039  * cannot be handled meaningfully since the system is in OBP. These
2040  * interrupts need to be cleared on the CPU side so that the CPU may
2041  * continue with whatever it is doing. Devices that have raised the
2042  * interrupts are expected to reraise the interrupts after sometime
2043  * as they have not been handled. At that time, Solaris will have a
2044  * chance to properly service the interrupts.
2045  *
2046  * The location of the interrupt registers depends on what is present
2047  * at a port. OPL currently supports the Oberon and the CMU channel.
2048  * The following handler handles both kinds of ports and computes
2049  * interrupt register addresses from the specifications and Jupiter Bus
2050  * device bindings.
2051  *
2052  * Fcode drivers install their interrupt handler via a "master-interrupt"
2053  * service. For boot time devices, this takes place within OBP. In the case
2054  * of DR, OPL uses IKP. The Fcode drivers that run within the efcode framework
2055  * attempt to install their handler via the "master-interrupt" service.
2056  * However, we cannot meaningfully install the Fcode driver's handler.
2057  * Instead, we install our own handler in OBP which does the same thing.
2058  *
2059  * Note that the only handling done for interrupts here is to clear it
2060  * on the CPU side. If any device in the future requires more special
2061  * handling, we would have to put in some kind of framework for adding
2062  * device-specific handlers. This is *highly* unlikely, but possible.
2063  *
2064  * Finally, OBP provides a hook called "unix-interrupt-handler" to install
2065  * a Solaris-defined master-interrupt handler for a port. The default
2066  * definition for this method does nothing. Solaris may override this
2067  * with its own definition. This is the way the following handler gets
2068  * control from OBP when interrupts happen at a port after L1A, etc.
2069  */
2070 
2071 static char define_master_interrupt_handler[] =
2072 
2073 /*
2074  * This method translates an Oberon port id to the base (physical) address
2075  * of the interrupt clear registers for that port id.
2076  */
2077 
2078 ": pcich-mid>clear-int-pa   ( mid -- pa ) "
2079 "   dup 1 >> 7 and          ( mid ch# ) "
2080 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
2081 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
2082 "   swap d# 40 << or        ( mid ch# pa ) "
2083 "   swap d# 37 << or        ( mid pa ) "
2084 "   swap 1 and if h# 70.0000 else h# 60.0000 then "
2085 "   or h# 1400 or           ( pa ) "
2086 "; "
2087 
2088 /*
2089  * This method translates a CMU channel port id to the base (physical) address
2090  * of the interrupt clear registers for that port id. There are two classes of
2091  * interrupts that need to be handled for a CMU channel:
2092  *	- obio interrupts
2093  *	- pci interrupts
2094  * So, there are two addresses that need to be computed.
2095  */
2096 
2097 ": cmuch-mid>clear-int-pa   ( mid -- obio-pa pci-pa ) "
2098 "   dup 1 >> 7 and          ( mid ch# ) "
2099 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
2100 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
2101 "   swap d# 40 << or        ( mid ch# pa ) "
2102 "   swap d# 37 << or        ( mid pa ) "
2103 "   nip dup h# 1800 +       ( pa obio-pa ) "
2104 "   swap h# 1400 +          ( obio-pa pci-pa ) "
2105 "; "
2106 
2107 /*
2108  * This method checks if a given I/O port ID is valid or not.
2109  * For a given LSB,
2110  *	Oberon ports range from 0 - 3
2111  *	CMU ch ports range from 4 - 4
2112  *
2113  * Also, the Oberon supports leaves 0 and 1.
2114  * The CMU ch supports only one leaf, leaf 0.
2115  */
2116 
2117 ": valid-io-mid? ( mid -- flag ) "
2118 "   dup 1 >> 7 and                     ( mid ch# ) "
2119 "   dup 4 > if 2drop false exit then   ( mid ch# ) "
2120 "   4 = swap 1 and 1 = and not "
2121 "; "
2122 
2123 /*
2124  * This method checks if a given port id is a CMU ch.
2125  */
2126 
2127 ": cmuch? ( mid -- flag ) 1 >> 7 and 4 = ; "
2128 
2129 /*
2130  * Given the base address of the array of interrupt clear registers for
2131  * a port id, this method iterates over the given interrupt number bitmap
2132  * and resets the interrupt on the CPU side for every interrupt number
2133  * in the bitmap. Note that physical addresses are used to perform the
2134  * writes, not virtual addresses. This allows the handler to work without
2135  * any involvement from Solaris.
2136  */
2137 
2138 ": clear-ints ( pa bitmap count -- ) "
2139 "   0 do                            ( pa bitmap ) "
2140 "      dup 0= if 2drop unloop exit then "
2141 "      tuck                         ( bitmap pa bitmap ) "
2142 "      1 and if                     ( bitmap pa ) "
2143 "	 dup i 8 * + 0 swap         ( bitmap pa 0 pa' ) "
2144 "	 h# 15 spacex!              ( bitmap pa ) "
2145 "      then                         ( bitmap pa ) "
2146 "      swap 1 >>                    ( pa bitmap ) "
2147 "   loop "
2148 "; "
2149 
2150 /*
2151  * This method replaces the master-interrupt handler in OBP. Once
2152  * this method is plumbed into OBP, OBP transfers control to this
2153  * handler while returning to Solaris from OBP after L1A. This method's
2154  * task is to simply reset received interrupts on the CPU side.
2155  * When the devices reassert the interrupts later, Solaris will
2156  * be able to see them and handle them.
2157  *
2158  * For each port ID that has interrupts, this method is called
2159  * once by OBP. The input arguments are:
2160  *	mid	portid
2161  *	bitmap	bitmap of interrupts that have happened
2162  *
2163  * This method returns true, if it is able to handle the interrupts.
2164  * OBP does nothing further.
2165  *
2166  * This method returns false, if it encountered a problem. Currently,
2167  * the only problem could be an invalid port id. OBP needs to do
2168  * its own processing in that case. If this method returns false,
2169  * it preserves the mid and bitmap arguments for OBP.
2170  */
2171 
2172 ": unix-resend-mondos ( mid bitmap -- [ mid bitmap false ] | true ) "
2173 
2174 /*
2175  * Uncomment the following line if you want to display the input arguments.
2176  * This is meant for debugging.
2177  * "   .\" Bitmap=\" dup u. .\" MID=\" over u. cr "
2178  */
2179 
2180 /*
2181  * If the port id is not valid (according to the Oberon and CMU ch
2182  * specifications, then return false to OBP to continue further
2183  * processing.
2184  */
2185 
2186 "   over valid-io-mid? not if       ( mid bitmap ) "
2187 "      false exit "
2188 "   then "
2189 
2190 /*
2191  * If the port is a CMU ch, then the 64-bit bitmap represents
2192  * 2 32-bit bitmaps:
2193  *	- obio interrupt bitmap (20 bits)
2194  *	- pci interrupt bitmap (32 bits)
2195  *
2196  * - Split the bitmap into two
2197  * - Compute the base addresses of the interrupt clear registers
2198  *   for both pci interrupts and obio interrupts
2199  * - Clear obio interrupts
2200  * - Clear pci interrupts
2201  */
2202 
2203 "   over cmuch? if                  ( mid bitmap ) "
2204 "      xlsplit                      ( mid pci-bit obio-bit ) "
2205 "      rot cmuch-mid>clear-int-pa   ( pci-bit obio-bit obio-pa pci-pa ) "
2206 "      >r                           ( pci-bit obio-bit obio-pa ) ( r: pci-pa ) "
2207 "      swap d# 20 clear-ints        ( pci-bit ) ( r: pci-pa ) "
2208 "      r> swap d# 32 clear-ints     (  ) ( r: ) "
2209 
2210 /*
2211  * If the port is an Oberon, then the 64-bit bitmap is used fully.
2212  *
2213  * - Compute the base address of the interrupt clear registers
2214  * - Clear interrupts
2215  */
2216 
2217 "   else                            ( mid bitmap ) "
2218 "      swap pcich-mid>clear-int-pa  ( bitmap pa ) "
2219 "      swap d# 64 clear-ints        (  ) "
2220 "   then "
2221 
2222 /*
2223  * Always return true from here.
2224  */
2225 
2226 "   true                            ( true ) "
2227 "; "
2228 ;
2229 
2230 static char	install_master_interrupt_handler[] =
2231 	"' unix-resend-mondos to unix-interrupt-handler";
2232 static char	handler[] = "unix-interrupt-handler";
2233 static char	handler_defined[] = "p\" %s\" find nip swap l! ";
2234 
2235 /*ARGSUSED*/
2236 static int
master_interrupt_init(uint32_t portid,uint32_t xt)2237 master_interrupt_init(uint32_t portid, uint32_t xt)
2238 {
2239 	uint_t	defined;
2240 	char	buf[sizeof (handler) + sizeof (handler_defined)];
2241 
2242 	if (master_interrupt_inited)
2243 		return (1);
2244 
2245 	/*
2246 	 * Check if the defer word "unix-interrupt-handler" is defined.
2247 	 * This must be defined for OPL systems. So, this is only a
2248 	 * sanity check.
2249 	 */
2250 	(void) sprintf(buf, handler_defined, handler);
2251 	prom_interpret(buf, (uintptr_t)&defined, 0, 0, 0, 0);
2252 	if (!defined) {
2253 		cmn_err(CE_WARN, "master_interrupt_init: "
2254 		    "%s is not defined\n", handler);
2255 		return (0);
2256 	}
2257 
2258 	/*
2259 	 * Install the generic master-interrupt handler. Note that
2260 	 * this is only done one time on the first DR operation.
2261 	 * This is because, for OPL, one, single generic handler
2262 	 * handles all ports (Oberon and CMU channel) and all
2263 	 * interrupt sources within each port.
2264 	 *
2265 	 * The current support is only for the Oberon and CMU-channel.
2266 	 * If any others need to be supported, the handler has to be
2267 	 * modified accordingly.
2268 	 */
2269 
2270 	/*
2271 	 * Define the OPL master interrupt handler
2272 	 */
2273 	prom_interpret(define_master_interrupt_handler, 0, 0, 0, 0, 0);
2274 
2275 	/*
2276 	 * Take over the master interrupt handler from OBP.
2277 	 */
2278 	prom_interpret(install_master_interrupt_handler, 0, 0, 0, 0, 0);
2279 
2280 	master_interrupt_inited = 1;
2281 
2282 	/*
2283 	 * prom_interpret() does not return a status. So, we assume
2284 	 * that the calls succeeded. In reality, the calls may fail
2285 	 * if there is a syntax error, etc in the strings.
2286 	 */
2287 
2288 	return (1);
2289 }
2290 
2291 /*
2292  * Install the master-interrupt handler for a device.
2293  */
2294 static int
opl_master_interrupt(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)2295 opl_master_interrupt(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
2296 {
2297 	uint32_t	portid, xt;
2298 	int		board, channel, leaf;
2299 	int		status;
2300 
2301 	/* Check the argument */
2302 	if (fc_cell2int(cp->nargs) != 2)
2303 		return (fc_syntax_error(cp, "nargs must be 2"));
2304 
2305 	if (fc_cell2int(cp->nresults) < 1)
2306 		return (fc_syntax_error(cp, "nresults must be >= 1"));
2307 
2308 	/* Get the parameters */
2309 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
2310 	xt = fc_cell2uint32_t(fc_arg(cp, 1));
2311 
2312 	board = OPL_IO_PORTID_TO_LSB(portid);
2313 	channel = OPL_PORTID_TO_CHANNEL(portid);
2314 	leaf = OPL_PORTID_TO_LEAF(portid);
2315 
2316 	if ((board >= HWD_SBS_PER_DOMAIN) || !OPL_VALID_CHANNEL(channel) ||
2317 	    (OPL_OBERON_CHANNEL(channel) && !OPL_VALID_LEAF(leaf)) ||
2318 	    ((channel == OPL_CMU_CHANNEL) && (leaf != 0))) {
2319 		FC_DEBUG1(1, CE_CONT, "opl_master_interrupt: invalid port %x\n",
2320 		    portid);
2321 		status = 0;
2322 	} else {
2323 		status = master_interrupt_init(portid, xt);
2324 	}
2325 
2326 	cp->nresults = fc_int2cell(1);
2327 	fc_result(cp, 0) = status;
2328 
2329 	return (fc_success_op(ap, rp, cp));
2330 }
2331 
2332 /*
2333  * Set the properties for a leaf node (Oberon leaf or CMU channel leaf).
2334  */
2335 /*ARGSUSED*/
2336 static int
opl_create_leaf(dev_info_t * node,void * arg,uint_t flags)2337 opl_create_leaf(dev_info_t *node, void *arg, uint_t flags)
2338 {
2339 	int ret;
2340 
2341 	OPL_UPDATE_PROP(string, node, "name", OPL_PCI_LEAF_NODE);
2342 
2343 	OPL_UPDATE_PROP(string, node, "status", "okay");
2344 
2345 	return (DDI_WALK_TERMINATE);
2346 }
2347 
2348 static char *
opl_get_probe_string(opl_probe_t * probe,int channel,int leaf)2349 opl_get_probe_string(opl_probe_t *probe, int channel, int leaf)
2350 {
2351 	char		*probe_string;
2352 	int		portid;
2353 
2354 	probe_string = kmem_zalloc(PROBE_STR_SIZE, KM_SLEEP);
2355 
2356 	if (channel == OPL_CMU_CHANNEL)
2357 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
2358 	else
2359 		portid = probe->
2360 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
2361 
2362 	(void) sprintf(probe_string, "%x", portid);
2363 
2364 	return (probe_string);
2365 }
2366 
2367 static int
opl_probe_leaf(opl_probe_t * probe)2368 opl_probe_leaf(opl_probe_t *probe)
2369 {
2370 	int		channel, leaf, portid, error;
2371 	int		board;
2372 	fco_handle_t	fco_handle, *cfg_handle;
2373 	dev_info_t	*parent, *leaf_node;
2374 	char		unit_address[UNIT_ADDR_SIZE];
2375 	char		*probe_string;
2376 	opl_board_cfg_t	*board_cfg;
2377 
2378 	board = probe->pr_board;
2379 	channel = probe->pr_channel;
2380 	leaf = probe->pr_leaf;
2381 	parent = ddi_root_node();
2382 	board_cfg = &opl_boards[board];
2383 
2384 	ASSERT(OPL_VALID_CHANNEL(channel));
2385 	ASSERT(OPL_VALID_LEAF(leaf));
2386 
2387 	if (channel == OPL_CMU_CHANNEL) {
2388 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
2389 		cfg_handle = &board_cfg->cfg_cmuch_handle;
2390 	} else {
2391 		portid = probe->
2392 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
2393 		cfg_handle = &board_cfg->cfg_pcich_handle[channel][leaf];
2394 	}
2395 
2396 	/*
2397 	 * Prevent any changes to leaf_node until we have bound
2398 	 * it to the correct driver.
2399 	 */
2400 	ndi_devi_enter(parent);
2401 
2402 	/*
2403 	 * Ideally, fcode would be run from the "sid_branch_create"
2404 	 * callback (that is the primary purpose of that callback).
2405 	 * However, the fcode interpreter was written with the
2406 	 * assumption that the "new_child" was linked into the
2407 	 * device tree. The callback is invoked with the devinfo node
2408 	 * in the DS_PROTO state. More investigation is needed before
2409 	 * we can invoke the interpreter from the callback. For now,
2410 	 * we create the "new_child" in the BOUND state, invoke the
2411 	 * fcode interpreter and then rebind the dip to use any
2412 	 * compatible properties created by fcode.
2413 	 */
2414 
2415 	probe->pr_parent = parent;
2416 	probe->pr_create = opl_create_leaf;
2417 	probe->pr_hold = 1;
2418 
2419 	leaf_node = opl_create_node(probe);
2420 	if (leaf_node == NULL) {
2421 
2422 		cmn_err(CE_WARN, "IKP: create leaf (%d-%d-%d) failed",
2423 		    probe->pr_board, probe->pr_channel, probe->pr_leaf);
2424 		ndi_devi_exit(parent);
2425 		return (-1);
2426 	}
2427 
2428 	/*
2429 	 * The platform DR interfaces created the dip in
2430 	 * bound state. Bring devinfo node down to linked
2431 	 * state and hold it there until compatible
2432 	 * properties are created.
2433 	 */
2434 	e_ddi_branch_rele(leaf_node);
2435 	(void) i_ndi_unconfig_node(leaf_node, DS_LINKED, 0);
2436 	ASSERT(i_ddi_node_state(leaf_node) == DS_LINKED);
2437 	e_ddi_branch_hold(leaf_node);
2438 
2439 	mutex_enter(&DEVI(leaf_node)->devi_lock);
2440 	DEVI(leaf_node)->devi_flags |= DEVI_NO_BIND;
2441 	mutex_exit(&DEVI(leaf_node)->devi_lock);
2442 
2443 	/*
2444 	 * Drop the busy-hold on parent before calling
2445 	 * fcode_interpreter to prevent potential deadlocks
2446 	 */
2447 	ndi_devi_exit(parent);
2448 
2449 	(void) sprintf(unit_address, "%x", portid);
2450 
2451 	/*
2452 	 * Get the probe string
2453 	 */
2454 	probe_string = opl_get_probe_string(probe, channel, leaf);
2455 
2456 	/*
2457 	 * The fcode pointer specified here is NULL and the fcode
2458 	 * size specified here is 0. This causes the user-level
2459 	 * fcode interpreter to issue a request to the fcode
2460 	 * driver to get the Oberon/cmu-ch fcode.
2461 	 */
2462 	fco_handle = opl_fc_ops_alloc_handle(parent, leaf_node,
2463 	    NULL, 0, unit_address, probe_string);
2464 
2465 	error = fcode_interpreter(parent, &opl_fc_do_op, fco_handle);
2466 
2467 	if (error != 0) {
2468 		cmn_err(CE_WARN, "IKP: Unable to probe PCI leaf (%d-%d-%d)",
2469 		    probe->pr_board, probe->pr_channel, probe->pr_leaf);
2470 
2471 		opl_fc_ops_free_handle(fco_handle);
2472 
2473 		if (probe_string != NULL)
2474 			kmem_free(probe_string, PROBE_STR_SIZE);
2475 
2476 		(void) opl_destroy_node(leaf_node);
2477 	} else {
2478 		*cfg_handle = fco_handle;
2479 
2480 		if (channel == OPL_CMU_CHANNEL)
2481 			board_cfg->cfg_cmuch_probe_str = probe_string;
2482 		else
2483 			board_cfg->cfg_pcich_probe_str[channel][leaf]
2484 			    = probe_string;
2485 
2486 		/*
2487 		 * Compatible properties (if any) have been created,
2488 		 * so bind driver.
2489 		 */
2490 		ndi_devi_enter(parent);
2491 		ASSERT(i_ddi_node_state(leaf_node) <= DS_LINKED);
2492 
2493 		mutex_enter(&DEVI(leaf_node)->devi_lock);
2494 		DEVI(leaf_node)->devi_flags &= ~DEVI_NO_BIND;
2495 		mutex_exit(&DEVI(leaf_node)->devi_lock);
2496 
2497 		ndi_devi_exit(parent);
2498 
2499 		if (ndi_devi_bind_driver(leaf_node, 0) != DDI_SUCCESS) {
2500 			cmn_err(CE_WARN, "IKP: Unable to bind PCI leaf "
2501 			    "(%d-%d-%d)", probe->pr_board, probe->pr_channel,
2502 			    probe->pr_leaf);
2503 		}
2504 	}
2505 
2506 	if ((error != 0) && (channel == OPL_CMU_CHANNEL))
2507 		return (-1);
2508 
2509 	return (0);
2510 }
2511 
2512 static void
opl_init_leaves(int myboard)2513 opl_init_leaves(int myboard)
2514 {
2515 	dev_info_t	*parent, *node;
2516 	char		*name;
2517 	int		ret;
2518 	int		len, portid, board, channel, leaf;
2519 	opl_board_cfg_t	*cfg;
2520 
2521 	parent = ddi_root_node();
2522 
2523 	/*
2524 	 * Hold parent node busy to walk its child list
2525 	 */
2526 	ndi_devi_enter(parent);
2527 
2528 	for (node = ddi_get_child(parent); (node != NULL); node =
2529 	    ddi_get_next_sibling(node)) {
2530 
2531 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
2532 		if (ret != DDI_PROP_SUCCESS) {
2533 			/*
2534 			 * The property does not exist for this node.
2535 			 */
2536 			continue;
2537 		}
2538 
2539 		if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
2540 
2541 			ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
2542 			if (ret == DDI_PROP_SUCCESS) {
2543 
2544 				ret = OPL_GET_PROP(int, node, "board#",
2545 				    &board, -1);
2546 				if ((ret != DDI_PROP_SUCCESS) ||
2547 				    (board != myboard)) {
2548 					kmem_free(name, len);
2549 					continue;
2550 				}
2551 
2552 				cfg = &opl_boards[board];
2553 				channel = OPL_PORTID_TO_CHANNEL(portid);
2554 				if (channel == OPL_CMU_CHANNEL) {
2555 
2556 					if (cfg->cfg_cmuch_handle != NULL)
2557 						cfg->cfg_cmuch_leaf = node;
2558 
2559 				} else {
2560 
2561 					leaf = OPL_PORTID_TO_LEAF(portid);
2562 					if (cfg->cfg_pcich_handle[
2563 					    channel][leaf] != NULL)
2564 						cfg->cfg_pcich_leaf[
2565 						    channel][leaf] = node;
2566 				}
2567 			}
2568 		}
2569 
2570 		kmem_free(name, len);
2571 		if (ret != DDI_PROP_SUCCESS)
2572 			break;
2573 	}
2574 
2575 	ndi_devi_exit(parent);
2576 }
2577 
2578 /*
2579  * Create "pci" node and hierarchy for the Oberon channels and the
2580  * CMU channel.
2581  */
2582 /*ARGSUSED*/
2583 static int
opl_probe_io(opl_probe_t * probe)2584 opl_probe_io(opl_probe_t *probe)
2585 {
2586 
2587 	int		i, j;
2588 	hwd_pci_ch_t	*channels;
2589 
2590 	if (HWD_STATUS_OK(probe->pr_sb->sb_cmu.cmu_ch.chan_status)) {
2591 
2592 		probe->pr_channel = HWD_CMU_CHANNEL;
2593 		probe->pr_channel_status =
2594 		    probe->pr_sb->sb_cmu.cmu_ch.chan_status;
2595 		probe->pr_leaf = 0;
2596 		probe->pr_leaf_status = probe->pr_channel_status;
2597 
2598 		if (opl_probe_leaf(probe) != 0)
2599 			return (-1);
2600 	}
2601 
2602 	channels = &probe->pr_sb->sb_pci_ch[0];
2603 
2604 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
2605 
2606 		if (!HWD_STATUS_OK(channels[i].pci_status))
2607 			continue;
2608 
2609 		probe->pr_channel = i;
2610 		probe->pr_channel_status = channels[i].pci_status;
2611 
2612 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
2613 
2614 			probe->pr_leaf = j;
2615 			probe->pr_leaf_status =
2616 			    channels[i].pci_leaf[j].leaf_status;
2617 
2618 			if (!HWD_STATUS_OK(probe->pr_leaf_status))
2619 				continue;
2620 
2621 			(void) opl_probe_leaf(probe);
2622 		}
2623 	}
2624 	opl_init_leaves(probe->pr_board);
2625 	return (0);
2626 }
2627 
2628 /*
2629  * Perform the probe in the following order:
2630  *
2631  *	processors
2632  *	memory
2633  *	IO
2634  *
2635  * Each probe function returns 0 on sucess and a non-zero value on failure.
2636  * What is a failure is determined by the implementor of the probe function.
2637  * For example, while probing CPUs, any error encountered during probe
2638  * is considered a failure and causes the whole probe operation to fail.
2639  * However, for I/O, an error encountered while probing one device
2640  * should not prevent other devices from being probed. It should not cause
2641  * the whole probe operation to fail.
2642  */
2643 int
opl_probe_sb(int board,unsigned * cpu_impl)2644 opl_probe_sb(int board, unsigned *cpu_impl)
2645 {
2646 	opl_probe_t	*probe;
2647 	int		ret;
2648 
2649 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
2650 		return (-1);
2651 
2652 	ASSERT(opl_cfg_inited != 0);
2653 
2654 	/*
2655 	 * If the previous probe failed and left a partially configured
2656 	 * board, we need to unprobe the board and start with a clean slate.
2657 	 */
2658 	if ((opl_boards[board].cfg_hwd != NULL) &&
2659 	    (opl_unprobe_sb(board) != 0))
2660 		return (-1);
2661 
2662 	ret = 0;
2663 
2664 	probe = kmem_zalloc(sizeof (opl_probe_t), KM_SLEEP);
2665 	probe->pr_board = board;
2666 
2667 	if ((opl_probe_init(probe) != 0) ||
2668 
2669 	    (opl_probe_cpu_chips(probe) != 0) ||
2670 
2671 	    (opl_probe_memory(probe) != 0) ||
2672 
2673 	    (opl_probe_io(probe) != 0)) {
2674 
2675 		/*
2676 		 * Probe failed. Perform cleanup.
2677 		 */
2678 		(void) opl_unprobe_sb(board);
2679 		ret = -1;
2680 	}
2681 
2682 	*cpu_impl = probe->pr_cpu_impl;
2683 
2684 	kmem_free(probe, sizeof (opl_probe_t));
2685 
2686 	return (ret);
2687 }
2688 
2689 /*
2690  * This unprobing also includes CMU-CH.
2691  */
2692 /*ARGSUSED*/
2693 static int
opl_unprobe_io(int board)2694 opl_unprobe_io(int board)
2695 {
2696 	int		i, j, ret;
2697 	opl_board_cfg_t	*board_cfg;
2698 	dev_info_t	**node;
2699 	fco_handle_t	*hand;
2700 	char		**probe_str;
2701 
2702 	board_cfg = &opl_boards[board];
2703 
2704 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
2705 
2706 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
2707 
2708 			node = &board_cfg->cfg_pcich_leaf[i][j];
2709 			hand = &board_cfg->cfg_pcich_handle[i][j];
2710 			probe_str = &board_cfg->cfg_pcich_probe_str[i][j];
2711 
2712 			if (*node == NULL)
2713 				continue;
2714 
2715 			if (*hand != NULL) {
2716 				opl_fc_ops_free_handle(*hand);
2717 				*hand = NULL;
2718 			}
2719 
2720 			if (*probe_str != NULL) {
2721 				kmem_free(*probe_str, PROBE_STR_SIZE);
2722 				*probe_str = NULL;
2723 			}
2724 
2725 			ret = opl_destroy_node(*node);
2726 			if (ret != 0) {
2727 
2728 				cmn_err(CE_WARN, "IKP: destroy pci (%d-%d-%d) "
2729 				    "failed", board, i, j);
2730 				return (-1);
2731 			}
2732 
2733 			*node = NULL;
2734 
2735 		}
2736 	}
2737 
2738 	node = &board_cfg->cfg_cmuch_leaf;
2739 	hand = &board_cfg->cfg_cmuch_handle;
2740 	probe_str = &board_cfg->cfg_cmuch_probe_str;
2741 
2742 	if (*node == NULL)
2743 		return (0);
2744 
2745 	if (*hand != NULL) {
2746 		opl_fc_ops_free_handle(*hand);
2747 		*hand = NULL;
2748 	}
2749 
2750 	if (*probe_str != NULL) {
2751 		kmem_free(*probe_str, PROBE_STR_SIZE);
2752 		*probe_str = NULL;
2753 	}
2754 
2755 	if (opl_destroy_node(*node) != 0) {
2756 
2757 		cmn_err(CE_WARN, "IKP: destroy pci (%d-%d-%d) failed", board,
2758 		    OPL_CMU_CHANNEL, 0);
2759 		return (-1);
2760 	}
2761 
2762 	*node = NULL;
2763 
2764 	return (0);
2765 }
2766 
2767 /*
2768  * Destroy the "pseudo-mc" node for a board.
2769  */
2770 static int
opl_unprobe_memory(int board)2771 opl_unprobe_memory(int board)
2772 {
2773 	opl_board_cfg_t	*board_cfg;
2774 
2775 	board_cfg = &opl_boards[board];
2776 
2777 	if (board_cfg->cfg_pseudo_mc == NULL)
2778 		return (0);
2779 
2780 	if (opl_destroy_node(board_cfg->cfg_pseudo_mc) != 0) {
2781 
2782 		cmn_err(CE_WARN, "IKP: destroy pseudo-mc (%d) failed", board);
2783 		return (-1);
2784 	}
2785 
2786 	board_cfg->cfg_pseudo_mc = NULL;
2787 
2788 	return (0);
2789 }
2790 
2791 /*
2792  * Destroy the "cmp" nodes for a board. This also destroys the "core"
2793  * and "cpu" nodes below the "cmp" nodes.
2794  */
2795 static int
opl_unprobe_processors(int board)2796 opl_unprobe_processors(int board)
2797 {
2798 	int		i;
2799 	dev_info_t	**cfg_cpu_chips;
2800 
2801 	cfg_cpu_chips = opl_boards[board].cfg_cpu_chips;
2802 
2803 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
2804 
2805 		if (cfg_cpu_chips[i] == NULL)
2806 			continue;
2807 
2808 		if (opl_destroy_node(cfg_cpu_chips[i]) != 0) {
2809 
2810 			cmn_err(CE_WARN, "IKP: destroy chip (%d-%d) failed",
2811 			    board, i);
2812 			return (-1);
2813 		}
2814 
2815 		cfg_cpu_chips[i] = NULL;
2816 	}
2817 
2818 	return (0);
2819 }
2820 
2821 /*
2822  * Perform the unprobe in the following order:
2823  *
2824  *	IO
2825  *	memory
2826  *	processors
2827  */
2828 int
opl_unprobe_sb(int board)2829 opl_unprobe_sb(int board)
2830 {
2831 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
2832 		return (-1);
2833 
2834 	ASSERT(opl_cfg_inited != 0);
2835 
2836 	if ((opl_unprobe_io(board) != 0) ||
2837 
2838 	    (opl_unprobe_memory(board) != 0) ||
2839 
2840 	    (opl_unprobe_processors(board) != 0))
2841 
2842 		return (-1);
2843 
2844 	if (opl_boards[board].cfg_hwd != NULL) {
2845 #ifdef UCTEST
2846 		size_t			size = 0xA000;
2847 #endif
2848 		/* Release the memory for the HWD */
2849 		void *hwdp = opl_boards[board].cfg_hwd;
2850 		opl_boards[board].cfg_hwd = NULL;
2851 #ifdef UCTEST
2852 		hwdp = (void *)((char *)hwdp - 0x1000);
2853 		hat_unload(kas.a_hat, hwdp, size, HAT_UNLOAD_UNLOCK);
2854 		vmem_free(heap_arena, hwdp, size);
2855 #else
2856 		kmem_free(hwdp, HWD_DATA_SIZE);
2857 #endif
2858 	}
2859 	return (0);
2860 }
2861 
2862 /*
2863  * For MAC patrol support, we need to update the PA-related properties
2864  * when there is a copy-rename event.  This should be called after the
2865  * physical copy and rename has been done by DR, and before the MAC
2866  * patrol is restarted.
2867  */
2868 int
oplcfg_pa_swap(int from,int to)2869 oplcfg_pa_swap(int from, int to)
2870 {
2871 	dev_info_t *from_node = opl_boards[from].cfg_pseudo_mc;
2872 	dev_info_t *to_node = opl_boards[to].cfg_pseudo_mc;
2873 	opl_range_t *rangef, *ranget;
2874 	int elems;
2875 	int ret;
2876 
2877 	if ((OPL_GET_PROP_ARRAY(int, from_node, "sb-mem-ranges", rangef,
2878 	    elems) != DDI_SUCCESS) || (elems != 4)) {
2879 		/* XXX -- bad news */
2880 		return (-1);
2881 	}
2882 	if ((OPL_GET_PROP_ARRAY(int, to_node, "sb-mem-ranges", ranget,
2883 	    elems) != DDI_SUCCESS) || (elems != 4)) {
2884 		/* XXX -- bad news */
2885 		return (-1);
2886 	}
2887 	OPL_UPDATE_PROP_ARRAY(int, from_node, "sb-mem-ranges", (int *)ranget,
2888 	    4);
2889 	OPL_UPDATE_PROP_ARRAY(int, to_node, "sb-mem-ranges", (int *)rangef,
2890 	    4);
2891 
2892 	OPL_FREE_PROP(ranget);
2893 	OPL_FREE_PROP(rangef);
2894 
2895 	return (0);
2896 }
2897