xref: /illumos-gate/usr/src/uts/sun4u/io/opl_cfg.c (revision d5ebc4938a50bb2fb1914062e396761dc9161a51)
125cf1a30Sjl139090 /*
225cf1a30Sjl139090  * CDDL HEADER START
325cf1a30Sjl139090  *
425cf1a30Sjl139090  * The contents of this file are subject to the terms of the
525cf1a30Sjl139090  * Common Development and Distribution License (the "License").
625cf1a30Sjl139090  * You may not use this file except in compliance with the License.
725cf1a30Sjl139090  *
825cf1a30Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl139090  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl139090  * See the License for the specific language governing permissions
1125cf1a30Sjl139090  * and limitations under the License.
1225cf1a30Sjl139090  *
1325cf1a30Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl139090  *
1925cf1a30Sjl139090  * CDDL HEADER END
2025cf1a30Sjl139090  */
2125cf1a30Sjl139090 /*
2288294e09SRichard Bean  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2325cf1a30Sjl139090  * Use is subject to license terms.
2448bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
253fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
2625cf1a30Sjl139090  */
2725cf1a30Sjl139090 
2825cf1a30Sjl139090 #include <sys/conf.h>
2925cf1a30Sjl139090 #include <sys/kmem.h>
3025cf1a30Sjl139090 #include <sys/debug.h>
3125cf1a30Sjl139090 #include <sys/modctl.h>
3225cf1a30Sjl139090 #include <sys/autoconf.h>
3325cf1a30Sjl139090 #include <sys/hwconf.h>
3425cf1a30Sjl139090 #include <sys/ddi_impldefs.h>
3525cf1a30Sjl139090 #include <sys/ddi.h>
3625cf1a30Sjl139090 #include <sys/sunddi.h>
3725cf1a30Sjl139090 #include <sys/sunndi.h>
3825cf1a30Sjl139090 #include <sys/ndi_impldefs.h>
3925cf1a30Sjl139090 #include <sys/machsystm.h>
4025cf1a30Sjl139090 #include <sys/fcode.h>
4125cf1a30Sjl139090 #include <sys/promif.h>
4225cf1a30Sjl139090 #include <sys/promimpl.h>
4325cf1a30Sjl139090 #include <sys/opl_cfg.h>
4425cf1a30Sjl139090 #include <sys/scfd/scfostoescf.h>
4525cf1a30Sjl139090 
4625cf1a30Sjl139090 static unsigned int		opl_cfg_inited;
4725cf1a30Sjl139090 static opl_board_cfg_t		opl_boards[HWD_SBS_PER_DOMAIN];
4825cf1a30Sjl139090 
4925cf1a30Sjl139090 /*
5025cf1a30Sjl139090  * Module control operations
5125cf1a30Sjl139090  */
5225cf1a30Sjl139090 
5325cf1a30Sjl139090 extern struct mod_ops mod_miscops;
5425cf1a30Sjl139090 
5525cf1a30Sjl139090 static struct modlmisc modlmisc = {
5625cf1a30Sjl139090 	&mod_miscops,				/* Type of module */
5788294e09SRichard Bean 	"OPL opl_cfg"
5825cf1a30Sjl139090 };
5925cf1a30Sjl139090 
6025cf1a30Sjl139090 static struct modlinkage modlinkage = {
6125cf1a30Sjl139090 	MODREV_1, (void *)&modlmisc, NULL
6225cf1a30Sjl139090 };
6325cf1a30Sjl139090 
6425cf1a30Sjl139090 static int	opl_map_in(dev_info_t *, fco_handle_t, fc_ci_t *);
6525cf1a30Sjl139090 static int	opl_map_out(dev_info_t *, fco_handle_t, fc_ci_t *);
6625cf1a30Sjl139090 static int	opl_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *);
6725cf1a30Sjl139090 static int	opl_register_store(dev_info_t *, fco_handle_t, fc_ci_t *);
6825cf1a30Sjl139090 
6925cf1a30Sjl139090 static int	opl_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
7025cf1a30Sjl139090 static int	opl_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
7125cf1a30Sjl139090 static int	opl_vtop(dev_info_t *, fco_handle_t, fc_ci_t *);
7225cf1a30Sjl139090 
7325cf1a30Sjl139090 static int	opl_config_child(dev_info_t *, fco_handle_t, fc_ci_t *);
7425cf1a30Sjl139090 
7525cf1a30Sjl139090 static int	opl_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *);
7625cf1a30Sjl139090 static int	opl_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *);
7725cf1a30Sjl139090 
7825cf1a30Sjl139090 static int	opl_map_phys(dev_info_t *, struct regspec *,  caddr_t *,
7925cf1a30Sjl139090 				ddi_device_acc_attr_t *, ddi_acc_handle_t *);
8025cf1a30Sjl139090 static void	opl_unmap_phys(ddi_acc_handle_t *);
8125cf1a30Sjl139090 static int	opl_get_hwd_va(dev_info_t *, fco_handle_t, fc_ci_t *);
8253123245Smv143129 static int	opl_master_interrupt(dev_info_t *, fco_handle_t, fc_ci_t *);
8325cf1a30Sjl139090 
8425cf1a30Sjl139090 extern int	prom_get_fcode_size(char *);
8525cf1a30Sjl139090 extern int	prom_get_fcode(char *, char *);
8625cf1a30Sjl139090 
8753123245Smv143129 static int	master_interrupt_init(uint32_t, uint32_t);
8853123245Smv143129 
8925cf1a30Sjl139090 #define	PROBE_STR_SIZE	64
9025cf1a30Sjl139090 #define	UNIT_ADDR_SIZE	64
9125cf1a30Sjl139090 
9225cf1a30Sjl139090 opl_fc_ops_t	opl_fc_ops[] = {
9325cf1a30Sjl139090 
9425cf1a30Sjl139090 	{	FC_MAP_IN,		opl_map_in},
9525cf1a30Sjl139090 	{	FC_MAP_OUT,		opl_map_out},
9625cf1a30Sjl139090 	{	"rx@",			opl_register_fetch},
9725cf1a30Sjl139090 	{	FC_RL_FETCH,		opl_register_fetch},
9825cf1a30Sjl139090 	{	FC_RW_FETCH,		opl_register_fetch},
9925cf1a30Sjl139090 	{	FC_RB_FETCH,		opl_register_fetch},
10025cf1a30Sjl139090 	{	"rx!",			opl_register_store},
10125cf1a30Sjl139090 	{	FC_RL_STORE,		opl_register_store},
10225cf1a30Sjl139090 	{	FC_RW_STORE,		opl_register_store},
10325cf1a30Sjl139090 	{	FC_RB_STORE,		opl_register_store},
10425cf1a30Sjl139090 	{	"claim-memory",		opl_claim_memory},
10525cf1a30Sjl139090 	{	"release-memory",	opl_release_memory},
10625cf1a30Sjl139090 	{	"vtop",			opl_vtop},
10725cf1a30Sjl139090 	{	FC_CONFIG_CHILD,	opl_config_child},
10825cf1a30Sjl139090 	{	FC_GET_FCODE_SIZE,	opl_get_fcode_size},
10925cf1a30Sjl139090 	{	FC_GET_FCODE,		opl_get_fcode},
11025cf1a30Sjl139090 	{	"get-hwd-va",		opl_get_hwd_va},
11153123245Smv143129 	{	"master-interrupt",	opl_master_interrupt},
11225cf1a30Sjl139090 	{	NULL,			NULL}
11325cf1a30Sjl139090 
11425cf1a30Sjl139090 };
11525cf1a30Sjl139090 
11625cf1a30Sjl139090 extern caddr_t	efcode_vaddr;
11725cf1a30Sjl139090 extern int	efcode_size;
11825cf1a30Sjl139090 
11925cf1a30Sjl139090 #ifdef DEBUG
12025cf1a30Sjl139090 #define	HWDDUMP_OFFSETS		1
12125cf1a30Sjl139090 #define	HWDDUMP_ALL_STATUS	2
12225cf1a30Sjl139090 #define	HWDDUMP_CHUNKS		3
12325cf1a30Sjl139090 #define	HWDDUMP_SBP		4
12425cf1a30Sjl139090 
12525cf1a30Sjl139090 int		hwddump_flags = HWDDUMP_SBP | HWDDUMP_CHUNKS;
12625cf1a30Sjl139090 #endif
12725cf1a30Sjl139090 
12853123245Smv143129 static int	master_interrupt_inited = 0;
12953123245Smv143129 
13025cf1a30Sjl139090 int
_init()13125cf1a30Sjl139090 _init()
13225cf1a30Sjl139090 {
13325cf1a30Sjl139090 	int	err = 0;
13425cf1a30Sjl139090 
13525cf1a30Sjl139090 	/*
13625cf1a30Sjl139090 	 * Create a resource map for the contiguous memory allocated
13725cf1a30Sjl139090 	 * at start-of-day in startup.c
13825cf1a30Sjl139090 	 */
13925cf1a30Sjl139090 	err = ndi_ra_map_setup(ddi_root_node(), "opl-fcodemem");
14025cf1a30Sjl139090 	if (err == NDI_FAILURE) {
14125cf1a30Sjl139090 		cmn_err(CE_WARN, "Cannot setup resource map opl-fcodemem\n");
14225cf1a30Sjl139090 		return (1);
14325cf1a30Sjl139090 	}
14425cf1a30Sjl139090 
14525cf1a30Sjl139090 	/*
14625cf1a30Sjl139090 	 * Put the allocated memory into the pool.
14725cf1a30Sjl139090 	 */
14825cf1a30Sjl139090 	(void) ndi_ra_free(ddi_root_node(), (uint64_t)efcode_vaddr,
14925cf1a30Sjl139090 	    (uint64_t)efcode_size, "opl-fcodemem", 0);
15025cf1a30Sjl139090 
15125cf1a30Sjl139090 	if ((err = mod_install(&modlinkage)) != 0) {
15225cf1a30Sjl139090 		cmn_err(CE_WARN, "opl_cfg failed to load, error=%d", err);
15325cf1a30Sjl139090 		(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
15425cf1a30Sjl139090 	}
15525cf1a30Sjl139090 
15625cf1a30Sjl139090 	return (err);
15725cf1a30Sjl139090 }
15825cf1a30Sjl139090 
15925cf1a30Sjl139090 int
_fini(void)16025cf1a30Sjl139090 _fini(void)
16125cf1a30Sjl139090 {
16225cf1a30Sjl139090 	int ret;
16325cf1a30Sjl139090 
16425cf1a30Sjl139090 	ret = (mod_remove(&modlinkage));
16525cf1a30Sjl139090 	if (ret != 0)
16625cf1a30Sjl139090 		return (ret);
16725cf1a30Sjl139090 
16825cf1a30Sjl139090 	(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
16925cf1a30Sjl139090 
17025cf1a30Sjl139090 	return (ret);
17125cf1a30Sjl139090 }
17225cf1a30Sjl139090 
17325cf1a30Sjl139090 int
_info(struct modinfo * modinfop)174*d5ebc493SDan Cross _info(struct modinfo *modinfop)
17525cf1a30Sjl139090 {
17625cf1a30Sjl139090 	return (mod_info(&modlinkage, modinfop));
17725cf1a30Sjl139090 }
17825cf1a30Sjl139090 
17925cf1a30Sjl139090 #ifdef DEBUG
18025cf1a30Sjl139090 static void
opl_dump_hwd(opl_probe_t * probe)18125cf1a30Sjl139090 opl_dump_hwd(opl_probe_t *probe)
18225cf1a30Sjl139090 {
18325cf1a30Sjl139090 	hwd_header_t		*hdrp;
18425cf1a30Sjl139090 	hwd_sb_status_t		*statp;
18525cf1a30Sjl139090 	hwd_domain_info_t	*dinfop;
18625cf1a30Sjl139090 	hwd_sb_t		*sbp;
18725cf1a30Sjl139090 	hwd_cpu_chip_t		*chips;
18825cf1a30Sjl139090 	hwd_pci_ch_t		*channels;
18925cf1a30Sjl139090 	int			board, i, status;
19025cf1a30Sjl139090 
19125cf1a30Sjl139090 	board = probe->pr_board;
19225cf1a30Sjl139090 
19325cf1a30Sjl139090 	hdrp = probe->pr_hdr;
19425cf1a30Sjl139090 	statp = probe->pr_sb_status;
19525cf1a30Sjl139090 	dinfop = probe->pr_dinfo;
19625cf1a30Sjl139090 	sbp = probe->pr_sb;
19725cf1a30Sjl139090 
19825cf1a30Sjl139090 	printf("HWD: board %d\n", board);
19925cf1a30Sjl139090 	printf("HWD:magic = 0x%x\n", hdrp->hdr_magic);
20025cf1a30Sjl139090 	printf("HWD:version = 0x%x.%x\n", hdrp->hdr_version.major,
20125cf1a30Sjl139090 	    hdrp->hdr_version.minor);
20225cf1a30Sjl139090 
20325cf1a30Sjl139090 	if (hwddump_flags & HWDDUMP_OFFSETS) {
20425cf1a30Sjl139090 		printf("HWD:status offset = 0x%x\n",
20525cf1a30Sjl139090 		    hdrp->hdr_sb_status_offset);
20625cf1a30Sjl139090 		printf("HWD:domain offset = 0x%x\n",
20725cf1a30Sjl139090 		    hdrp->hdr_domain_info_offset);
20825cf1a30Sjl139090 		printf("HWD:board offset = 0x%x\n", hdrp->hdr_sb_info_offset);
20925cf1a30Sjl139090 	}
21025cf1a30Sjl139090 
21125cf1a30Sjl139090 	if (hwddump_flags & HWDDUMP_SBP)
21225cf1a30Sjl139090 		printf("HWD:sb_t ptr = 0x%p\n", (void *)probe->pr_sb);
21325cf1a30Sjl139090 
21425cf1a30Sjl139090 	if (hwddump_flags & HWDDUMP_ALL_STATUS) {
21525cf1a30Sjl139090 		int bd;
21625cf1a30Sjl139090 		printf("HWD:board status =");
21725cf1a30Sjl139090 		for (bd = 0; bd < HWD_SBS_PER_DOMAIN; bd++)
21825cf1a30Sjl139090 			printf("%x ", statp->sb_status[bd]);
21925cf1a30Sjl139090 		printf("\n");
22025cf1a30Sjl139090 	} else {
22125cf1a30Sjl139090 		printf("HWD:board status = %d\n", statp->sb_status[board]);
22225cf1a30Sjl139090 	}
22325cf1a30Sjl139090 
22425cf1a30Sjl139090 	printf("HWD:banner name = %s\n", dinfop->dinf_banner_name);
22525cf1a30Sjl139090 	printf("HWD:platform = %s\n", dinfop->dinf_platform_token);
22625cf1a30Sjl139090 
22725cf1a30Sjl139090 	printf("HWD:chip status:\n");
22825cf1a30Sjl139090 	chips = &sbp->sb_cmu.cmu_cpu_chips[0];
22925cf1a30Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
23025cf1a30Sjl139090 
23125cf1a30Sjl139090 		status = chips[i].chip_status;
23225cf1a30Sjl139090 		printf("chip[%d] = ", i);
23325cf1a30Sjl139090 		if (HWD_STATUS_NONE(status))
23425cf1a30Sjl139090 			printf("none");
23525cf1a30Sjl139090 		else if (HWD_STATUS_FAILED(status))
23625cf1a30Sjl139090 			printf("fail");
23725cf1a30Sjl139090 		else if (HWD_STATUS_OK(status))
23825cf1a30Sjl139090 			printf("ok");
23925cf1a30Sjl139090 		printf("\n");
24025cf1a30Sjl139090 	}
24125cf1a30Sjl139090 
24225cf1a30Sjl139090 	if (hwddump_flags & HWDDUMP_CHUNKS) {
24325cf1a30Sjl139090 		int chunk;
24425cf1a30Sjl139090 		hwd_memory_t *mem = &sbp->sb_cmu.cmu_memory;
24525cf1a30Sjl139090 		printf("HWD:chunks:\n");
24625cf1a30Sjl139090 		for (chunk = 0; chunk < HWD_MAX_MEM_CHUNKS; chunk++)
24725cf1a30Sjl139090 			printf("\t%d 0x%lx 0x%lx\n", chunk,
24825cf1a30Sjl139090 			    mem->mem_chunks[chunk].chnk_start_address,
24925cf1a30Sjl139090 			    mem->mem_chunks[chunk].chnk_size);
25025cf1a30Sjl139090 	}
25125cf1a30Sjl139090 
25225cf1a30Sjl139090 	printf("HWD:channel status:\n");
25325cf1a30Sjl139090 	channels = &sbp->sb_pci_ch[0];
25425cf1a30Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
25525cf1a30Sjl139090 
25625cf1a30Sjl139090 		status = channels[i].pci_status;
25725cf1a30Sjl139090 		printf("channels[%d] = ", i);
25825cf1a30Sjl139090 		if (HWD_STATUS_NONE(status))
25925cf1a30Sjl139090 			printf("none");
26025cf1a30Sjl139090 		else if (HWD_STATUS_FAILED(status))
26125cf1a30Sjl139090 			printf("fail");
26225cf1a30Sjl139090 		else if (HWD_STATUS_OK(status))
26325cf1a30Sjl139090 			printf("ok");
26425cf1a30Sjl139090 		printf("\n");
26525cf1a30Sjl139090 	}
26625cf1a30Sjl139090 	printf("channels[%d] = ", i);
26725cf1a30Sjl139090 	status = sbp->sb_cmu.cmu_ch.chan_status;
26825cf1a30Sjl139090 	if (HWD_STATUS_NONE(status))
26925cf1a30Sjl139090 		printf("none");
27025cf1a30Sjl139090 	else if (HWD_STATUS_FAILED(status))
27125cf1a30Sjl139090 		printf("fail");
27225cf1a30Sjl139090 	else if (HWD_STATUS_OK(status))
27325cf1a30Sjl139090 		printf("ok");
27425cf1a30Sjl139090 	printf("\n");
27525cf1a30Sjl139090 }
27625cf1a30Sjl139090 #endif /* DEBUG */
27725cf1a30Sjl139090 
27825cf1a30Sjl139090 #ifdef UCTEST
27925cf1a30Sjl139090 	/*
28025cf1a30Sjl139090 	 * For SesamI debugging, just map the SRAM directly to a kernel
28125cf1a30Sjl139090 	 * VA and read it out from there
28225cf1a30Sjl139090 	 */
28325cf1a30Sjl139090 
28425cf1a30Sjl139090 #include <sys/vmem.h>
28525cf1a30Sjl139090 #include <vm/seg_kmem.h>
28625cf1a30Sjl139090 
28725cf1a30Sjl139090 /*
28825cf1a30Sjl139090  * 0x4081F1323000LL is the HWD base address for LSB 0. But we need to map
28925cf1a30Sjl139090  * at page boundaries. So, we use a base address of 0x4081F1322000LL.
29025cf1a30Sjl139090  * Note that this has to match the HWD base pa set in .sesami-common-defs.
29125cf1a30Sjl139090  *
29225cf1a30Sjl139090  * The size specified for the HWD in the SCF spec is 36K. But since
29325cf1a30Sjl139090  * we adjusted the base address by 4K, we need to use 40K for the
29425cf1a30Sjl139090  * mapping size to cover the HWD. And 40K is also a multiple of the
29525cf1a30Sjl139090  * base page size.
29625cf1a30Sjl139090  */
29725cf1a30Sjl139090 #define	OPL_HWD_BASE(lsb)       \
29825cf1a30Sjl139090 (0x4081F1322000LL | (((uint64_t)(lsb)) << 40))
29925cf1a30Sjl139090 
30025cf1a30Sjl139090 	void    *opl_hwd_vaddr;
30125cf1a30Sjl139090 #endif /* UCTEST */
30225cf1a30Sjl139090 
30325cf1a30Sjl139090 /*
30425cf1a30Sjl139090  * Get the hardware descriptor from SCF.
30525cf1a30Sjl139090  */
30625cf1a30Sjl139090 
30725cf1a30Sjl139090 /*ARGSUSED*/
30825cf1a30Sjl139090 int
opl_read_hwd(int board,hwd_header_t ** hdrp,hwd_sb_status_t ** statp,hwd_domain_info_t ** dinfop,hwd_sb_t ** sbp)30925cf1a30Sjl139090 opl_read_hwd(int board, hwd_header_t **hdrp, hwd_sb_status_t **statp,
31025cf1a30Sjl139090     hwd_domain_info_t **dinfop, hwd_sb_t **sbp)
31125cf1a30Sjl139090 {
31225cf1a30Sjl139090 	static int (*getinfop)(uint32_t, uint8_t, uint32_t, uint32_t *,
31325cf1a30Sjl139090 	    void *) = NULL;
31425cf1a30Sjl139090 	void *hwdp;
31525cf1a30Sjl139090 
31625cf1a30Sjl139090 	uint32_t key = KEY_ESCF;	/* required value */
31725cf1a30Sjl139090 	uint8_t  type = 0x40;		/* SUB_OS_RECEIVE_HWD */
31825cf1a30Sjl139090 	uint32_t transid = board;
31925cf1a30Sjl139090 	uint32_t datasize = HWD_DATA_SIZE;
32025cf1a30Sjl139090 
32125cf1a30Sjl139090 	hwd_header_t		*hd;
32225cf1a30Sjl139090 	hwd_sb_status_t		*st;
32325cf1a30Sjl139090 	hwd_domain_info_t	*di;
32425cf1a30Sjl139090 	hwd_sb_t		*sb;
32525cf1a30Sjl139090 
32625cf1a30Sjl139090 	int	ret;
32725cf1a30Sjl139090 
32825cf1a30Sjl139090 	if (opl_boards[board].cfg_hwd == NULL) {
32925cf1a30Sjl139090 #ifdef UCTEST
33025cf1a30Sjl139090 		/*
33125cf1a30Sjl139090 		 * Just map the HWD in SRAM to a kernel VA
33225cf1a30Sjl139090 		 */
33325cf1a30Sjl139090 
33425cf1a30Sjl139090 		size_t			size;
33525cf1a30Sjl139090 		pfn_t			pfn;
33625cf1a30Sjl139090 
33725cf1a30Sjl139090 		size = 0xA000;
33825cf1a30Sjl139090 
33925cf1a30Sjl139090 		opl_hwd_vaddr = vmem_alloc(heap_arena, size, VM_SLEEP);
34025cf1a30Sjl139090 		if (opl_hwd_vaddr == NULL) {
34125cf1a30Sjl139090 			cmn_err(CE_NOTE, "No space for HWD");
34225cf1a30Sjl139090 			return (-1);
34325cf1a30Sjl139090 		}
34425cf1a30Sjl139090 
34525cf1a30Sjl139090 		pfn = btop(OPL_HWD_BASE(board));
34625cf1a30Sjl139090 		hat_devload(kas.a_hat, opl_hwd_vaddr, size, pfn, PROT_READ,
34725cf1a30Sjl139090 		    HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK);
34825cf1a30Sjl139090 
34925cf1a30Sjl139090 		hwdp = (void *)((char *)opl_hwd_vaddr + 0x1000);
35025cf1a30Sjl139090 		opl_boards[board].cfg_hwd = hwdp;
35125cf1a30Sjl139090 		ret = 0;
35225cf1a30Sjl139090 #else
35325cf1a30Sjl139090 
35425cf1a30Sjl139090 		/* find the scf_service_getinfo() function */
35525cf1a30Sjl139090 		if (getinfop == NULL)
35625cf1a30Sjl139090 			getinfop = (int (*)(uint32_t, uint8_t, uint32_t,
35725cf1a30Sjl139090 			    uint32_t *,
35825cf1a30Sjl139090 			    void *))modgetsymvalue("scf_service_getinfo", 0);
35925cf1a30Sjl139090 
36025cf1a30Sjl139090 		if (getinfop == NULL)
36125cf1a30Sjl139090 			return (-1);
36225cf1a30Sjl139090 
36325cf1a30Sjl139090 		/* allocate memory to receive the data */
36425cf1a30Sjl139090 		hwdp = kmem_alloc(HWD_DATA_SIZE, KM_SLEEP);
36525cf1a30Sjl139090 
36625cf1a30Sjl139090 		/* get the HWD */
36725cf1a30Sjl139090 		ret = (*getinfop)(key, type, transid, &datasize, hwdp);
36825cf1a30Sjl139090 		if (ret == 0)
36925cf1a30Sjl139090 			opl_boards[board].cfg_hwd = hwdp;
37025cf1a30Sjl139090 		else
37125cf1a30Sjl139090 			kmem_free(hwdp, HWD_DATA_SIZE);
37225cf1a30Sjl139090 #endif
37325cf1a30Sjl139090 	} else {
37425cf1a30Sjl139090 		hwdp = opl_boards[board].cfg_hwd;
37525cf1a30Sjl139090 		ret = 0;
37625cf1a30Sjl139090 	}
37725cf1a30Sjl139090 
37825cf1a30Sjl139090 	/* copy the data to the destination */
37925cf1a30Sjl139090 	if (ret == 0) {
38025cf1a30Sjl139090 		hd = (hwd_header_t *)hwdp;
38125cf1a30Sjl139090 		st = (hwd_sb_status_t *)
38225cf1a30Sjl139090 		    ((char *)hwdp + hd->hdr_sb_status_offset);
38325cf1a30Sjl139090 		di = (hwd_domain_info_t *)
38425cf1a30Sjl139090 		    ((char *)hwdp + hd->hdr_domain_info_offset);
38525cf1a30Sjl139090 		sb = (hwd_sb_t *)
38625cf1a30Sjl139090 		    ((char *)hwdp + hd->hdr_sb_info_offset);
38725cf1a30Sjl139090 		if (hdrp != NULL)
38825cf1a30Sjl139090 			*hdrp = hd;
38925cf1a30Sjl139090 		if (statp != NULL)
39025cf1a30Sjl139090 			*statp = st;
39125cf1a30Sjl139090 		if (dinfop != NULL)
39225cf1a30Sjl139090 			*dinfop = di;
39325cf1a30Sjl139090 		if (sbp != NULL)
39425cf1a30Sjl139090 			*sbp = sb;
39525cf1a30Sjl139090 	}
39625cf1a30Sjl139090 
39725cf1a30Sjl139090 	return (ret);
39825cf1a30Sjl139090 }
39925cf1a30Sjl139090 
40025cf1a30Sjl139090 /*
40125cf1a30Sjl139090  * The opl_probe_t probe structure is used to pass all sorts of parameters
40225cf1a30Sjl139090  * to callback functions during probing. It also contains a snapshot of
40325cf1a30Sjl139090  * the hardware descriptor that is taken at the beginning of a probe.
40425cf1a30Sjl139090  */
40525cf1a30Sjl139090 static int
opl_probe_init(opl_probe_t * probe)40625cf1a30Sjl139090 opl_probe_init(opl_probe_t *probe)
40725cf1a30Sjl139090 {
40825cf1a30Sjl139090 	hwd_header_t		**hdrp;
40925cf1a30Sjl139090 	hwd_sb_status_t		**statp;
41025cf1a30Sjl139090 	hwd_domain_info_t	**dinfop;
41125cf1a30Sjl139090 	hwd_sb_t		**sbp;
41225cf1a30Sjl139090 	int			board, ret;
41325cf1a30Sjl139090 
41425cf1a30Sjl139090 	board = probe->pr_board;
41525cf1a30Sjl139090 
41625cf1a30Sjl139090 	hdrp = &probe->pr_hdr;
41725cf1a30Sjl139090 	statp = &probe->pr_sb_status;
41825cf1a30Sjl139090 	dinfop = &probe->pr_dinfo;
41925cf1a30Sjl139090 	sbp = &probe->pr_sb;
42025cf1a30Sjl139090 
42125cf1a30Sjl139090 	/*
42225cf1a30Sjl139090 	 * Read the hardware descriptor.
42325cf1a30Sjl139090 	 */
42425cf1a30Sjl139090 	ret = opl_read_hwd(board, hdrp, statp, dinfop, sbp);
42525cf1a30Sjl139090 	if (ret != 0) {
42625cf1a30Sjl139090 
42725cf1a30Sjl139090 		cmn_err(CE_WARN, "IKP: failed to read HWD header");
42825cf1a30Sjl139090 		return (-1);
42925cf1a30Sjl139090 	}
43025cf1a30Sjl139090 
43125cf1a30Sjl139090 #ifdef DEBUG
43225cf1a30Sjl139090 	opl_dump_hwd(probe);
43325cf1a30Sjl139090 #endif
43425cf1a30Sjl139090 	return (0);
43525cf1a30Sjl139090 }
43625cf1a30Sjl139090 
43725cf1a30Sjl139090 /*
43825cf1a30Sjl139090  * This function is used to obtain pointers to relevant device nodes
43925cf1a30Sjl139090  * which are created by Solaris at boot time.
44025cf1a30Sjl139090  *
44125cf1a30Sjl139090  * This function walks the child nodes of a given node, extracts
44225cf1a30Sjl139090  * the "name" property, if it exists, and passes the node to a
44325cf1a30Sjl139090  * callback init function. The callback determines if this node is
44425cf1a30Sjl139090  * interesting or not. If it is, then a pointer to the node is
44525cf1a30Sjl139090  * stored away by the callback for use during unprobe.
44625cf1a30Sjl139090  *
44725cf1a30Sjl139090  * The DDI get property function allocates storage for the name
44825cf1a30Sjl139090  * property. That needs to be freed within this function.
44925cf1a30Sjl139090  */
45025cf1a30Sjl139090 static int
opl_init_nodes(dev_info_t * parent,opl_init_func_t init)45125cf1a30Sjl139090 opl_init_nodes(dev_info_t *parent, opl_init_func_t init)
45225cf1a30Sjl139090 {
45325cf1a30Sjl139090 	dev_info_t	*node;
45425cf1a30Sjl139090 	char		*name;
4553fe80ca4SDan Cross 	int		ret;
45625cf1a30Sjl139090 	int		len;
45725cf1a30Sjl139090 
45825cf1a30Sjl139090 	ASSERT(parent != NULL);
45925cf1a30Sjl139090 
46025cf1a30Sjl139090 	/*
46125cf1a30Sjl139090 	 * Hold parent node busy to walk its child list
46225cf1a30Sjl139090 	 */
4633fe80ca4SDan Cross 	ndi_devi_enter(parent);
46425cf1a30Sjl139090 	node = ddi_get_child(parent);
46525cf1a30Sjl139090 
46625cf1a30Sjl139090 	while (node != NULL) {
46725cf1a30Sjl139090 
46825cf1a30Sjl139090 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
46925cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS) {
47025cf1a30Sjl139090 			/*
47125cf1a30Sjl139090 			 * The property does not exist for this node.
47225cf1a30Sjl139090 			 */
47325cf1a30Sjl139090 			node = ddi_get_next_sibling(node);
47425cf1a30Sjl139090 			continue;
47525cf1a30Sjl139090 		}
47625cf1a30Sjl139090 
47725cf1a30Sjl139090 		ret = init(node, name, len);
47825cf1a30Sjl139090 		kmem_free(name, len);
47925cf1a30Sjl139090 		if (ret != 0) {
48025cf1a30Sjl139090 
4813fe80ca4SDan Cross 			ndi_devi_exit(parent);
48225cf1a30Sjl139090 			return (-1);
48325cf1a30Sjl139090 		}
48425cf1a30Sjl139090 
48525cf1a30Sjl139090 		node = ddi_get_next_sibling(node);
48625cf1a30Sjl139090 	}
48725cf1a30Sjl139090 
4883fe80ca4SDan Cross 	ndi_devi_exit(parent);
48925cf1a30Sjl139090 
49025cf1a30Sjl139090 	return (0);
49125cf1a30Sjl139090 }
49225cf1a30Sjl139090 
49325cf1a30Sjl139090 /*
49425cf1a30Sjl139090  * This init function finds all the interesting nodes under the
49525cf1a30Sjl139090  * root node and stores pointers to them. The following nodes
49625cf1a30Sjl139090  * are considered interesting by this implementation:
49725cf1a30Sjl139090  *
49825cf1a30Sjl139090  *	"cmp"
49925cf1a30Sjl139090  *		These are nodes that represent processor chips.
50025cf1a30Sjl139090  *
50125cf1a30Sjl139090  *	"pci"
50225cf1a30Sjl139090  *		These are nodes that represent PCI leaves.
50325cf1a30Sjl139090  *
50425cf1a30Sjl139090  *	"pseudo-mc"
50525cf1a30Sjl139090  *		These are nodes that contain memory information.
50625cf1a30Sjl139090  */
50725cf1a30Sjl139090 static int
opl_init_root_nodes(dev_info_t * node,char * name,int len)50825cf1a30Sjl139090 opl_init_root_nodes(dev_info_t *node, char *name, int len)
50925cf1a30Sjl139090 {
51025cf1a30Sjl139090 	int		portid, board, chip, channel, leaf;
51125cf1a30Sjl139090 	int		ret;
51225cf1a30Sjl139090 
51325cf1a30Sjl139090 	if (strncmp(name, OPL_CPU_CHIP_NODE, len) == 0) {
51425cf1a30Sjl139090 
51525cf1a30Sjl139090 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
51625cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS)
51725cf1a30Sjl139090 			return (-1);
51825cf1a30Sjl139090 
51925cf1a30Sjl139090 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
52025cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS)
52125cf1a30Sjl139090 			return (-1);
52225cf1a30Sjl139090 
52325cf1a30Sjl139090 		chip = OPL_CPU_CHIP(portid);
52425cf1a30Sjl139090 		opl_boards[board].cfg_cpu_chips[chip] = node;
52525cf1a30Sjl139090 
52625cf1a30Sjl139090 	} else if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
52725cf1a30Sjl139090 
52825cf1a30Sjl139090 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
52925cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS)
53025cf1a30Sjl139090 			return (-1);
53125cf1a30Sjl139090 
53225cf1a30Sjl139090 		board = OPL_IO_PORTID_TO_LSB(portid);
53325cf1a30Sjl139090 		channel = OPL_PORTID_TO_CHANNEL(portid);
53425cf1a30Sjl139090 
53525cf1a30Sjl139090 		if (channel == OPL_CMU_CHANNEL) {
53625cf1a30Sjl139090 
53725cf1a30Sjl139090 			opl_boards[board].cfg_cmuch_leaf = node;
53825cf1a30Sjl139090 
53925cf1a30Sjl139090 		} else {
54025cf1a30Sjl139090 
54125cf1a30Sjl139090 			leaf = OPL_PORTID_TO_LEAF(portid);
54225cf1a30Sjl139090 			opl_boards[board].cfg_pcich_leaf[channel][leaf] = node;
54325cf1a30Sjl139090 		}
54425cf1a30Sjl139090 	} else if (strncmp(name, OPL_PSEUDO_MC_NODE, len) == 0) {
54525cf1a30Sjl139090 
54625cf1a30Sjl139090 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
54725cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS)
54825cf1a30Sjl139090 			return (-1);
54925cf1a30Sjl139090 
55025cf1a30Sjl139090 		ASSERT((board >= 0) && (board < HWD_SBS_PER_DOMAIN));
55125cf1a30Sjl139090 
55225cf1a30Sjl139090 		opl_boards[board].cfg_pseudo_mc = node;
55325cf1a30Sjl139090 	}
55425cf1a30Sjl139090 
55525cf1a30Sjl139090 	return (0);
55625cf1a30Sjl139090 }
55725cf1a30Sjl139090 
55825cf1a30Sjl139090 /*
55925cf1a30Sjl139090  * This function initializes the OPL IKP feature. Currently, all it does
56025cf1a30Sjl139090  * is find the interesting nodes that Solaris has created at boot time
56125cf1a30Sjl139090  * for boards present at boot time and store pointers to them. This
56225cf1a30Sjl139090  * is useful if those boards are unprobed by DR.
56325cf1a30Sjl139090  */
56425cf1a30Sjl139090 int
opl_init_cfg()56525cf1a30Sjl139090 opl_init_cfg()
56625cf1a30Sjl139090 {
56725cf1a30Sjl139090 	dev_info_t	*root;
56825cf1a30Sjl139090 
56925cf1a30Sjl139090 	if (opl_cfg_inited == 0) {
57025cf1a30Sjl139090 
57125cf1a30Sjl139090 		root = ddi_root_node();
57225cf1a30Sjl139090 		if ((opl_init_nodes(root, opl_init_root_nodes) != 0)) {
57325cf1a30Sjl139090 			cmn_err(CE_WARN, "IKP: init failed");
57425cf1a30Sjl139090 			return (1);
57525cf1a30Sjl139090 		}
57625cf1a30Sjl139090 
57725cf1a30Sjl139090 		opl_cfg_inited = 1;
57825cf1a30Sjl139090 	}
57925cf1a30Sjl139090 
58025cf1a30Sjl139090 	return (0);
58125cf1a30Sjl139090 }
58225cf1a30Sjl139090 
58325cf1a30Sjl139090 /*
58425cf1a30Sjl139090  * When DR is initialized, we walk the device tree and acquire a hold on
58525cf1a30Sjl139090  * all the nodes that are interesting to IKP. This is so that the corresponding
58625cf1a30Sjl139090  * branches cannot be deleted.
58725cf1a30Sjl139090  *
58825cf1a30Sjl139090  * The following function informs the walk about which nodes are interesting
58925cf1a30Sjl139090  * so that it can hold the corresponding branches.
59025cf1a30Sjl139090  */
59125cf1a30Sjl139090 static int
opl_hold_node(char * name)59225cf1a30Sjl139090 opl_hold_node(char *name)
59325cf1a30Sjl139090 {
59425cf1a30Sjl139090 	/*
59525cf1a30Sjl139090 	 * We only need to hold/release the following nodes which
59625cf1a30Sjl139090 	 * represent separate branches that must be managed.
59725cf1a30Sjl139090 	 */
59825cf1a30Sjl139090 	return ((strcmp(name, OPL_CPU_CHIP_NODE) == 0) ||
59925cf1a30Sjl139090 	    (strcmp(name, OPL_PSEUDO_MC_NODE) == 0) ||
60025cf1a30Sjl139090 	    (strcmp(name, OPL_PCI_LEAF_NODE) == 0));
60125cf1a30Sjl139090 }
60225cf1a30Sjl139090 
60325cf1a30Sjl139090 static int
opl_hold_rele_devtree(dev_info_t * rdip,void * arg)60425cf1a30Sjl139090 opl_hold_rele_devtree(dev_info_t *rdip, void *arg)
60525cf1a30Sjl139090 {
60625cf1a30Sjl139090 
60725cf1a30Sjl139090 	int	*holdp = (int *)arg;
60825cf1a30Sjl139090 	char	*name = ddi_node_name(rdip);
60925cf1a30Sjl139090 
61025cf1a30Sjl139090 	/*
61125cf1a30Sjl139090 	 * We only need to hold/release the following nodes which
61225cf1a30Sjl139090 	 * represent separate branches that must be managed.
61325cf1a30Sjl139090 	 */
61425cf1a30Sjl139090 	if (opl_hold_node(name) == 0) {
61525cf1a30Sjl139090 		/* Not of interest to us */
61625cf1a30Sjl139090 		return (DDI_WALK_PRUNECHILD);
61725cf1a30Sjl139090 	}
61825cf1a30Sjl139090 	if (*holdp) {
61925cf1a30Sjl139090 		ASSERT(!e_ddi_branch_held(rdip));
62025cf1a30Sjl139090 		e_ddi_branch_hold(rdip);
62125cf1a30Sjl139090 	} else {
62225cf1a30Sjl139090 		ASSERT(e_ddi_branch_held(rdip));
62325cf1a30Sjl139090 		e_ddi_branch_rele(rdip);
62425cf1a30Sjl139090 	}
62525cf1a30Sjl139090 
62625cf1a30Sjl139090 	return (DDI_WALK_PRUNECHILD);
62725cf1a30Sjl139090 }
62825cf1a30Sjl139090 
62925cf1a30Sjl139090 void
opl_hold_devtree()63025cf1a30Sjl139090 opl_hold_devtree()
63125cf1a30Sjl139090 {
63225cf1a30Sjl139090 	dev_info_t *dip;
63325cf1a30Sjl139090 	int hold = 1;
63425cf1a30Sjl139090 
63525cf1a30Sjl139090 	dip = ddi_root_node();
6363fe80ca4SDan Cross 	ndi_devi_enter(dip);
63725cf1a30Sjl139090 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
6383fe80ca4SDan Cross 	ndi_devi_exit(dip);
63925cf1a30Sjl139090 }
64025cf1a30Sjl139090 
64125cf1a30Sjl139090 void
opl_release_devtree()64225cf1a30Sjl139090 opl_release_devtree()
64325cf1a30Sjl139090 {
64425cf1a30Sjl139090 	dev_info_t *dip;
64525cf1a30Sjl139090 	int hold = 0;
64625cf1a30Sjl139090 
64725cf1a30Sjl139090 	dip = ddi_root_node();
6483fe80ca4SDan Cross 	ndi_devi_enter(dip);
64925cf1a30Sjl139090 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
6503fe80ca4SDan Cross 	ndi_devi_exit(dip);
65125cf1a30Sjl139090 }
65225cf1a30Sjl139090 
65325cf1a30Sjl139090 /*
65425cf1a30Sjl139090  * This is a helper function that allows opl_create_node() to return a
65525cf1a30Sjl139090  * pointer to a newly created node to its caller.
65625cf1a30Sjl139090  */
65725cf1a30Sjl139090 /*ARGSUSED*/
65825cf1a30Sjl139090 static void
opl_set_node(dev_info_t * node,void * arg,uint_t flags)65925cf1a30Sjl139090 opl_set_node(dev_info_t *node, void *arg, uint_t flags)
66025cf1a30Sjl139090 {
66125cf1a30Sjl139090 	opl_probe_t	*probe;
66225cf1a30Sjl139090 
66325cf1a30Sjl139090 	probe = arg;
66425cf1a30Sjl139090 	probe->pr_node = node;
66525cf1a30Sjl139090 }
66625cf1a30Sjl139090 
66725cf1a30Sjl139090 /*
66825cf1a30Sjl139090  * Function to create a node in the device tree under a specified parent.
66925cf1a30Sjl139090  *
67025cf1a30Sjl139090  * e_ddi_branch_create() allows the creation of a whole branch with a
67125cf1a30Sjl139090  * single call of the function. However, we only use it to create one node
67225cf1a30Sjl139090  * at a time in the case of non-I/O device nodes. In other words, we
67325cf1a30Sjl139090  * create branches by repeatedly using this function. This makes the
67425cf1a30Sjl139090  * code more readable.
67525cf1a30Sjl139090  *
67625cf1a30Sjl139090  * The branch descriptor passed to e_ddi_branch_create() takes two
67725cf1a30Sjl139090  * callbacks. The create() callback is used to set the properties of a
67825cf1a30Sjl139090  * newly created node. The other callback is used to return a pointer
67925cf1a30Sjl139090  * to the newly created node. The create() callback is passed by the
68048bbca81SDaniel Hoffman  * caller of this function based on the kind of node it wishes to
68125cf1a30Sjl139090  * create.
68225cf1a30Sjl139090  *
68325cf1a30Sjl139090  * e_ddi_branch_create() returns with the newly created node held. We
68425cf1a30Sjl139090  * only need to hold the top nodes of the branches we create. We release
68525cf1a30Sjl139090  * the hold for the others. E.g., the "cmp" node needs to be held. Since
68625cf1a30Sjl139090  * we hold the "cmp" node, there is no need to hold the "core" and "cpu"
68725cf1a30Sjl139090  * nodes below it.
68825cf1a30Sjl139090  */
68925cf1a30Sjl139090 static dev_info_t *
opl_create_node(opl_probe_t * probe)69025cf1a30Sjl139090 opl_create_node(opl_probe_t *probe)
69125cf1a30Sjl139090 {
69225cf1a30Sjl139090 	devi_branch_t	branch;
69325cf1a30Sjl139090 
69425cf1a30Sjl139090 	probe->pr_node = NULL;
69525cf1a30Sjl139090 
69625cf1a30Sjl139090 	branch.arg = probe;
69725cf1a30Sjl139090 	branch.type = DEVI_BRANCH_SID;
69825cf1a30Sjl139090 	branch.create.sid_branch_create = probe->pr_create;
69925cf1a30Sjl139090 	branch.devi_branch_callback = opl_set_node;
70025cf1a30Sjl139090 
70125cf1a30Sjl139090 	if (e_ddi_branch_create(probe->pr_parent, &branch, NULL, 0) != 0)
70225cf1a30Sjl139090 		return (NULL);
70325cf1a30Sjl139090 
70425cf1a30Sjl139090 	ASSERT(probe->pr_node != NULL);
70525cf1a30Sjl139090 
70625cf1a30Sjl139090 	if (probe->pr_hold == 0)
70725cf1a30Sjl139090 		e_ddi_branch_rele(probe->pr_node);
70825cf1a30Sjl139090 
70925cf1a30Sjl139090 	return (probe->pr_node);
71025cf1a30Sjl139090 }
71125cf1a30Sjl139090 
71225cf1a30Sjl139090 /*
71325cf1a30Sjl139090  * Function to tear down a whole branch rooted at the specified node.
71425cf1a30Sjl139090  *
71525cf1a30Sjl139090  * Although we create each node of a branch individually, we destroy
71625cf1a30Sjl139090  * a whole branch in one call. This is more efficient.
71725cf1a30Sjl139090  */
71825cf1a30Sjl139090 static int
opl_destroy_node(dev_info_t * node)71925cf1a30Sjl139090 opl_destroy_node(dev_info_t *node)
72025cf1a30Sjl139090 {
72168ac2337Sjl139090 	if (e_ddi_branch_destroy(node, NULL, 0) != 0) {
72268ac2337Sjl139090 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
72368ac2337Sjl139090 		(void) ddi_pathname(node, path);
724e98fafb9Sjl139090 		cmn_err(CE_WARN, "OPL node removal failed: %s (%p)", path,
725e98fafb9Sjl139090 		    (void *)node);
72668ac2337Sjl139090 		kmem_free(path, MAXPATHLEN);
72725cf1a30Sjl139090 		return (-1);
72868ac2337Sjl139090 	}
72925cf1a30Sjl139090 
73025cf1a30Sjl139090 	return (0);
73125cf1a30Sjl139090 }
73225cf1a30Sjl139090 
73325cf1a30Sjl139090 /*
73425cf1a30Sjl139090  * Set the properties for a "cpu" node.
73525cf1a30Sjl139090  */
73625cf1a30Sjl139090 /*ARGSUSED*/
73725cf1a30Sjl139090 static int
opl_create_cpu(dev_info_t * node,void * arg,uint_t flags)73825cf1a30Sjl139090 opl_create_cpu(dev_info_t *node, void *arg, uint_t flags)
73925cf1a30Sjl139090 {
74025cf1a30Sjl139090 	opl_probe_t	*probe;
74125cf1a30Sjl139090 	hwd_cpu_chip_t	*chip;
74225cf1a30Sjl139090 	hwd_core_t	*core;
74325cf1a30Sjl139090 	hwd_cpu_t	*cpu;
74425cf1a30Sjl139090 	int		ret;
74525cf1a30Sjl139090 
74625cf1a30Sjl139090 	probe = arg;
74725cf1a30Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
74825cf1a30Sjl139090 	core = &chip->chip_cores[probe->pr_core];
74925cf1a30Sjl139090 	cpu = &core->core_cpus[probe->pr_cpu];
75025cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_NODE);
75125cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CPU_NODE);
75225cf1a30Sjl139090 
75325cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "cpuid", cpu->cpu_cpuid);
75425cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_cpu);
75525cf1a30Sjl139090 
75625cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
75725cf1a30Sjl139090 
75825cf1a30Sjl139090 	return (DDI_WALK_TERMINATE);
75925cf1a30Sjl139090 }
76025cf1a30Sjl139090 
76125cf1a30Sjl139090 /*
76225cf1a30Sjl139090  * Create "cpu" nodes as child nodes of a given "core" node.
76325cf1a30Sjl139090  */
76425cf1a30Sjl139090 static int
opl_probe_cpus(opl_probe_t * probe)76525cf1a30Sjl139090 opl_probe_cpus(opl_probe_t *probe)
76625cf1a30Sjl139090 {
76725cf1a30Sjl139090 	int		i;
76825cf1a30Sjl139090 	hwd_cpu_chip_t	*chip;
76925cf1a30Sjl139090 	hwd_core_t	*core;
77025cf1a30Sjl139090 	hwd_cpu_t	*cpus;
77125cf1a30Sjl139090 
77225cf1a30Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
77325cf1a30Sjl139090 	core = &chip->chip_cores[probe->pr_core];
77425cf1a30Sjl139090 	cpus = &core->core_cpus[0];
77525cf1a30Sjl139090 
77625cf1a30Sjl139090 	for (i = 0; i < HWD_CPUS_PER_CORE; i++) {
77725cf1a30Sjl139090 
77825cf1a30Sjl139090 		/*
77925cf1a30Sjl139090 		 * Olympus-C has 2 cpus per core.
78025cf1a30Sjl139090 		 * Jupiter has 4 cpus per core.
78125cf1a30Sjl139090 		 * For the Olympus-C based platform, we expect the cpu_status
78225cf1a30Sjl139090 		 * of the non-existent cpus to be set to missing.
78325cf1a30Sjl139090 		 */
78425cf1a30Sjl139090 		if (!HWD_STATUS_OK(cpus[i].cpu_status))
78525cf1a30Sjl139090 			continue;
78625cf1a30Sjl139090 
78725cf1a30Sjl139090 		probe->pr_create = opl_create_cpu;
78825cf1a30Sjl139090 		probe->pr_cpu = i;
78925cf1a30Sjl139090 		if (opl_create_node(probe) == NULL) {
79025cf1a30Sjl139090 
79125cf1a30Sjl139090 			cmn_err(CE_WARN, "IKP: create cpu (%d-%d-%d-%d) failed",
792e98fafb9Sjl139090 			    probe->pr_board, probe->pr_cpu_chip, probe->pr_core,
793e98fafb9Sjl139090 			    probe->pr_cpu);
79425cf1a30Sjl139090 			return (-1);
79525cf1a30Sjl139090 		}
79625cf1a30Sjl139090 	}
79725cf1a30Sjl139090 
79825cf1a30Sjl139090 	return (0);
79925cf1a30Sjl139090 }
80025cf1a30Sjl139090 
80125cf1a30Sjl139090 /*
80225cf1a30Sjl139090  * Set the properties for a "core" node.
80325cf1a30Sjl139090  */
80425cf1a30Sjl139090 /*ARGSUSED*/
80525cf1a30Sjl139090 static int
opl_create_core(dev_info_t * node,void * arg,uint_t flags)80625cf1a30Sjl139090 opl_create_core(dev_info_t *node, void *arg, uint_t flags)
80725cf1a30Sjl139090 {
80825cf1a30Sjl139090 	opl_probe_t	*probe;
80925cf1a30Sjl139090 	hwd_cpu_chip_t	*chip;
81025cf1a30Sjl139090 	hwd_core_t	*core;
81125cf1a30Sjl139090 	int		sharing[2];
81225cf1a30Sjl139090 	int		ret;
81325cf1a30Sjl139090 
81425cf1a30Sjl139090 	probe = arg;
81525cf1a30Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
81625cf1a30Sjl139090 	core = &chip->chip_cores[probe->pr_core];
81725cf1a30Sjl139090 
81825cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CORE_NODE);
81925cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CORE_NODE);
82025cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "compatible", chip->chip_compatible);
82125cf1a30Sjl139090 
82225cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_core);
82325cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "manufacturer#", core->core_manufacturer);
82425cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "implementation#",
82525cf1a30Sjl139090 	    core->core_implementation);
82625cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "mask#", core->core_mask);
82725cf1a30Sjl139090 
8280e9f8900Sjimand 	OPL_UPDATE_PROP(int, node, "sparc-version", 9);
82925cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "clock-frequency", core->core_frequency);
83025cf1a30Sjl139090 
83125cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-size", core->core_l1_icache_size);
83225cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-line-size",
83325cf1a30Sjl139090 	    core->core_l1_icache_line_size);
83425cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-associativity",
83525cf1a30Sjl139090 	    core->core_l1_icache_associativity);
83625cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#itlb-entries",
83725cf1a30Sjl139090 	    core->core_num_itlb_entries);
83825cf1a30Sjl139090 
83925cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-size", core->core_l1_dcache_size);
84025cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-line-size",
84125cf1a30Sjl139090 	    core->core_l1_dcache_line_size);
84225cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-associativity",
84325cf1a30Sjl139090 	    core->core_l1_dcache_associativity);
84425cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#dtlb-entries",
84525cf1a30Sjl139090 	    core->core_num_dtlb_entries);
84625cf1a30Sjl139090 
84725cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-size", core->core_l2_cache_size);
84825cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-line-size",
84925cf1a30Sjl139090 	    core->core_l2_cache_line_size);
85025cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-associativity",
85125cf1a30Sjl139090 	    core->core_l2_cache_associativity);
85225cf1a30Sjl139090 	sharing[0] = 0;
85325cf1a30Sjl139090 	sharing[1] = core->core_l2_cache_sharing;
85425cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "l2-cache-sharing", sharing, 2);
85525cf1a30Sjl139090 
85625cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
85725cf1a30Sjl139090 
85825cf1a30Sjl139090 	return (DDI_WALK_TERMINATE);
85925cf1a30Sjl139090 }
86025cf1a30Sjl139090 
86125cf1a30Sjl139090 /*
86225cf1a30Sjl139090  * Create "core" nodes as child nodes of a given "cmp" node.
86325cf1a30Sjl139090  *
86425cf1a30Sjl139090  * Create the branch below each "core" node".
86525cf1a30Sjl139090  */
86625cf1a30Sjl139090 static int
opl_probe_cores(opl_probe_t * probe)86725cf1a30Sjl139090 opl_probe_cores(opl_probe_t *probe)
86825cf1a30Sjl139090 {
86925cf1a30Sjl139090 	int		i;
87025cf1a30Sjl139090 	hwd_cpu_chip_t	*chip;
87125cf1a30Sjl139090 	hwd_core_t	*cores;
87225cf1a30Sjl139090 	dev_info_t	*parent, *node;
87325cf1a30Sjl139090 
87425cf1a30Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
87525cf1a30Sjl139090 	cores = &chip->chip_cores[0];
87625cf1a30Sjl139090 	parent = probe->pr_parent;
87725cf1a30Sjl139090 
87825cf1a30Sjl139090 	for (i = 0; i < HWD_CORES_PER_CPU_CHIP; i++) {
87925cf1a30Sjl139090 
88025cf1a30Sjl139090 		if (!HWD_STATUS_OK(cores[i].core_status))
88125cf1a30Sjl139090 			continue;
88225cf1a30Sjl139090 
88325cf1a30Sjl139090 		probe->pr_parent = parent;
88425cf1a30Sjl139090 		probe->pr_create = opl_create_core;
88525cf1a30Sjl139090 		probe->pr_core = i;
88625cf1a30Sjl139090 		node = opl_create_node(probe);
88725cf1a30Sjl139090 		if (node == NULL) {
88825cf1a30Sjl139090 
88925cf1a30Sjl139090 			cmn_err(CE_WARN, "IKP: create core (%d-%d-%d) failed",
89025cf1a30Sjl139090 			    probe->pr_board, probe->pr_cpu_chip,
89125cf1a30Sjl139090 			    probe->pr_core);
89225cf1a30Sjl139090 			return (-1);
89325cf1a30Sjl139090 		}
89425cf1a30Sjl139090 
89525cf1a30Sjl139090 		/*
89625cf1a30Sjl139090 		 * Create "cpu" nodes below "core".
89725cf1a30Sjl139090 		 */
89825cf1a30Sjl139090 		probe->pr_parent = node;
89925cf1a30Sjl139090 		if (opl_probe_cpus(probe) != 0)
90025cf1a30Sjl139090 			return (-1);
901e98fafb9Sjl139090 		probe->pr_cpu_impl |= (1 << cores[i].core_implementation);
90225cf1a30Sjl139090 	}
90325cf1a30Sjl139090 
90425cf1a30Sjl139090 	return (0);
90525cf1a30Sjl139090 }
90625cf1a30Sjl139090 
90725cf1a30Sjl139090 /*
90825cf1a30Sjl139090  * Set the properties for a "cmp" node.
90925cf1a30Sjl139090  */
91025cf1a30Sjl139090 /*ARGSUSED*/
91125cf1a30Sjl139090 static int
opl_create_cpu_chip(dev_info_t * node,void * arg,uint_t flags)91225cf1a30Sjl139090 opl_create_cpu_chip(dev_info_t *node, void *arg, uint_t flags)
91325cf1a30Sjl139090 {
91425cf1a30Sjl139090 	opl_probe_t	*probe;
91525cf1a30Sjl139090 	hwd_cpu_chip_t	*chip;
91625cf1a30Sjl139090 	opl_range_t	range;
91725cf1a30Sjl139090 	uint64_t	dummy_addr;
91825cf1a30Sjl139090 	int		ret;
91925cf1a30Sjl139090 
92025cf1a30Sjl139090 	probe = arg;
92125cf1a30Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
92225cf1a30Sjl139090 
92325cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_CHIP_NODE);
92425cf1a30Sjl139090 
92525cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "portid", chip->chip_portid);
92625cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "board#", probe->pr_board);
92725cf1a30Sjl139090 
92825cf1a30Sjl139090 	dummy_addr = OPL_PROC_AS(probe->pr_board, probe->pr_cpu_chip);
92925cf1a30Sjl139090 	range.rg_addr_hi = OPL_HI(dummy_addr);
93025cf1a30Sjl139090 	range.rg_addr_lo = OPL_LO(dummy_addr);
93125cf1a30Sjl139090 	range.rg_size_hi = 0;
93225cf1a30Sjl139090 	range.rg_size_lo = 0;
93325cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
93425cf1a30Sjl139090 
93525cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
93625cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#size-cells", 0);
93725cf1a30Sjl139090 
93825cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
93925cf1a30Sjl139090 
94025cf1a30Sjl139090 	return (DDI_WALK_TERMINATE);
94125cf1a30Sjl139090 }
94225cf1a30Sjl139090 
94325cf1a30Sjl139090 /*
94425cf1a30Sjl139090  * Create "cmp" nodes as child nodes of the root node.
94525cf1a30Sjl139090  *
94625cf1a30Sjl139090  * Create the branch below each "cmp" node.
94725cf1a30Sjl139090  */
94825cf1a30Sjl139090 static int
opl_probe_cpu_chips(opl_probe_t * probe)94925cf1a30Sjl139090 opl_probe_cpu_chips(opl_probe_t *probe)
95025cf1a30Sjl139090 {
95125cf1a30Sjl139090 	int		i;
95225cf1a30Sjl139090 	dev_info_t	**cfg_cpu_chips;
95325cf1a30Sjl139090 	hwd_cpu_chip_t	*chips;
95425cf1a30Sjl139090 	dev_info_t	*node;
95525cf1a30Sjl139090 
95625cf1a30Sjl139090 	cfg_cpu_chips = opl_boards[probe->pr_board].cfg_cpu_chips;
95725cf1a30Sjl139090 	chips = &probe->pr_sb->sb_cmu.cmu_cpu_chips[0];
95825cf1a30Sjl139090 
95925cf1a30Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
96025cf1a30Sjl139090 
96125cf1a30Sjl139090 		ASSERT(cfg_cpu_chips[i] == NULL);
96225cf1a30Sjl139090 
96325cf1a30Sjl139090 		if (!HWD_STATUS_OK(chips[i].chip_status))
96425cf1a30Sjl139090 			continue;
96525cf1a30Sjl139090 
96625cf1a30Sjl139090 		probe->pr_parent = ddi_root_node();
96725cf1a30Sjl139090 		probe->pr_create = opl_create_cpu_chip;
96825cf1a30Sjl139090 		probe->pr_cpu_chip = i;
96925cf1a30Sjl139090 		probe->pr_hold = 1;
97025cf1a30Sjl139090 		node = opl_create_node(probe);
97125cf1a30Sjl139090 		if (node == NULL) {
97225cf1a30Sjl139090 
97325cf1a30Sjl139090 			cmn_err(CE_WARN, "IKP: create chip (%d-%d) failed",
97425cf1a30Sjl139090 			    probe->pr_board, probe->pr_cpu_chip);
97525cf1a30Sjl139090 			return (-1);
97625cf1a30Sjl139090 		}
97725cf1a30Sjl139090 
97825cf1a30Sjl139090 		cfg_cpu_chips[i] = node;
97925cf1a30Sjl139090 
98025cf1a30Sjl139090 		/*
98125cf1a30Sjl139090 		 * Create "core" nodes below "cmp".
98225cf1a30Sjl139090 		 * We hold the "cmp" node. So, there is no need to hold
98325cf1a30Sjl139090 		 * the "core" and "cpu" nodes below it.
98425cf1a30Sjl139090 		 */
98525cf1a30Sjl139090 		probe->pr_parent = node;
98625cf1a30Sjl139090 		probe->pr_hold = 0;
98725cf1a30Sjl139090 		if (opl_probe_cores(probe) != 0)
98825cf1a30Sjl139090 			return (-1);
98925cf1a30Sjl139090 	}
99025cf1a30Sjl139090 
99125cf1a30Sjl139090 	return (0);
99225cf1a30Sjl139090 }
99325cf1a30Sjl139090 
99425cf1a30Sjl139090 /*
99525cf1a30Sjl139090  * Set the properties for a "pseudo-mc" node.
99625cf1a30Sjl139090  */
99725cf1a30Sjl139090 /*ARGSUSED*/
99825cf1a30Sjl139090 static int
opl_create_pseudo_mc(dev_info_t * node,void * arg,uint_t flags)99925cf1a30Sjl139090 opl_create_pseudo_mc(dev_info_t *node, void *arg, uint_t flags)
100025cf1a30Sjl139090 {
100125cf1a30Sjl139090 	opl_probe_t	*probe;
100225cf1a30Sjl139090 	int		board, portid;
100325cf1a30Sjl139090 	hwd_bank_t	*bank;
100425cf1a30Sjl139090 	hwd_memory_t	*mem;
100525cf1a30Sjl139090 	opl_range_t	range;
100625cf1a30Sjl139090 	opl_mc_addr_t	mc[HWD_BANKS_PER_CMU];
100725cf1a30Sjl139090 	int		status[2][7];
100825cf1a30Sjl139090 	int		i, j;
100925cf1a30Sjl139090 	int		ret;
101025cf1a30Sjl139090 
101125cf1a30Sjl139090 	probe = arg;
101225cf1a30Sjl139090 	board = probe->pr_board;
101325cf1a30Sjl139090 
101425cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_PSEUDO_MC_NODE);
101525cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", "memory-controller");
101625cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "compatible", "FJSV,oplmc");
101725cf1a30Sjl139090 
101825cf1a30Sjl139090 	portid = OPL_LSB_TO_PSEUDOMC_PORTID(board);
101925cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "portid", portid);
102025cf1a30Sjl139090 
102125cf1a30Sjl139090 	range.rg_addr_hi = OPL_HI(OPL_MC_AS(board));
102225cf1a30Sjl139090 	range.rg_addr_lo = 0x200;
102325cf1a30Sjl139090 	range.rg_size_hi = 0;
102425cf1a30Sjl139090 	range.rg_size_lo = 0;
102525cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
102625cf1a30Sjl139090 
102725cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "board#", board);
102825cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "physical-board#",
102925cf1a30Sjl139090 	    probe->pr_sb->sb_psb_number);
103025cf1a30Sjl139090 
103125cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
103225cf1a30Sjl139090 	OPL_UPDATE_PROP(int, node, "#size-cells", 2);
103325cf1a30Sjl139090 
103425cf1a30Sjl139090 	mem = &probe->pr_sb->sb_cmu.cmu_memory;
103525cf1a30Sjl139090 
103625cf1a30Sjl139090 	range.rg_addr_hi = OPL_HI(mem->mem_start_address);
103725cf1a30Sjl139090 	range.rg_addr_lo = OPL_LO(mem->mem_start_address);
103825cf1a30Sjl139090 	range.rg_size_hi = OPL_HI(mem->mem_size);
103925cf1a30Sjl139090 	range.rg_size_lo = OPL_LO(mem->mem_size);
104025cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "sb-mem-ranges", (int *)&range, 4);
104125cf1a30Sjl139090 
104225cf1a30Sjl139090 	bank = probe->pr_sb->sb_cmu.cmu_memory.mem_banks;
104325cf1a30Sjl139090 	for (i = 0, j = 0; i < HWD_BANKS_PER_CMU; i++) {
104425cf1a30Sjl139090 
104525cf1a30Sjl139090 		if (!HWD_STATUS_OK(bank[i].bank_status))
104625cf1a30Sjl139090 			continue;
104725cf1a30Sjl139090 
104825cf1a30Sjl139090 		mc[j].mc_bank = i;
104925cf1a30Sjl139090 		mc[j].mc_hi = OPL_HI(bank[i].bank_register_address);
105025cf1a30Sjl139090 		mc[j].mc_lo = OPL_LO(bank[i].bank_register_address);
105125cf1a30Sjl139090 		j++;
105225cf1a30Sjl139090 	}
105368ac2337Sjl139090 
105468ac2337Sjl139090 	if (j > 0) {
105525cf1a30Sjl139090 		OPL_UPDATE_PROP_ARRAY(int, node, "mc-addr", (int *)mc, j*3);
105668ac2337Sjl139090 	} else {
105768ac2337Sjl139090 		/*
105868ac2337Sjl139090 		 * If there is no memory, we need the mc-addr property, but
105968ac2337Sjl139090 		 * it is length 0.  The only way to do this using ndi seems
106068ac2337Sjl139090 		 * to be by creating a boolean property.
106168ac2337Sjl139090 		 */
106268ac2337Sjl139090 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node, "mc-addr");
106368ac2337Sjl139090 		OPL_UPDATE_PROP_ERR(ret, "mc-addr");
106468ac2337Sjl139090 	}
106525cf1a30Sjl139090 
106625cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs0-mc-pa-trans-table",
106725cf1a30Sjl139090 	    mem->mem_cs[0].cs_pa_mac_table, 64);
106825cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs1-mc-pa-trans-table",
106925cf1a30Sjl139090 	    mem->mem_cs[1].cs_pa_mac_table, 64);
107025cf1a30Sjl139090 
107125cf1a30Sjl139090 #define	CS_PER_MEM 2
107225cf1a30Sjl139090 
107325cf1a30Sjl139090 	for (i = 0, j = 0; i < CS_PER_MEM; i++) {
107425cf1a30Sjl139090 		if (HWD_STATUS_OK(mem->mem_cs[i].cs_status) ||
107525cf1a30Sjl139090 		    HWD_STATUS_FAILED(mem->mem_cs[i].cs_status)) {
107625cf1a30Sjl139090 			status[j][0] = i;
107725cf1a30Sjl139090 			if (HWD_STATUS_OK(mem->mem_cs[i].cs_status))
107825cf1a30Sjl139090 				status[j][1] = 0;
107925cf1a30Sjl139090 			else
108025cf1a30Sjl139090 				status[j][1] = 1;
108125cf1a30Sjl139090 			status[j][2] =
108225cf1a30Sjl139090 			    OPL_HI(mem->mem_cs[i].cs_available_capacity);
108325cf1a30Sjl139090 			status[j][3] =
108425cf1a30Sjl139090 			    OPL_LO(mem->mem_cs[i].cs_available_capacity);
108525cf1a30Sjl139090 			status[j][4] = OPL_HI(mem->mem_cs[i].cs_dimm_capacity);
108625cf1a30Sjl139090 			status[j][5] = OPL_LO(mem->mem_cs[i].cs_dimm_capacity);
108725cf1a30Sjl139090 			status[j][6] = mem->mem_cs[i].cs_number_of_dimms;
108825cf1a30Sjl139090 			j++;
108925cf1a30Sjl139090 		}
109025cf1a30Sjl139090 	}
109168ac2337Sjl139090 
109268ac2337Sjl139090 	if (j > 0) {
109325cf1a30Sjl139090 		OPL_UPDATE_PROP_ARRAY(int, node, "cs-status", (int *)status,
109425cf1a30Sjl139090 		    j*7);
109568ac2337Sjl139090 	} else {
109668ac2337Sjl139090 		/*
109768ac2337Sjl139090 		 * If there is no memory, we need the cs-status property, but
109868ac2337Sjl139090 		 * it is length 0.  The only way to do this using ndi seems
109968ac2337Sjl139090 		 * to be by creating a boolean property.
110068ac2337Sjl139090 		 */
110168ac2337Sjl139090 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node,
110268ac2337Sjl139090 		    "cs-status");
110368ac2337Sjl139090 		OPL_UPDATE_PROP_ERR(ret, "cs-status");
110468ac2337Sjl139090 	}
110525cf1a30Sjl139090 
110625cf1a30Sjl139090 	return (DDI_WALK_TERMINATE);
110725cf1a30Sjl139090 }
110825cf1a30Sjl139090 
110925cf1a30Sjl139090 /*
111025cf1a30Sjl139090  * Create "pseudo-mc" nodes
111125cf1a30Sjl139090  */
111225cf1a30Sjl139090 static int
opl_probe_memory(opl_probe_t * probe)111325cf1a30Sjl139090 opl_probe_memory(opl_probe_t *probe)
111425cf1a30Sjl139090 {
111525cf1a30Sjl139090 	int		board;
111625cf1a30Sjl139090 	opl_board_cfg_t	*board_cfg;
111725cf1a30Sjl139090 	dev_info_t	*node;
111825cf1a30Sjl139090 
111925cf1a30Sjl139090 	board = probe->pr_board;
112025cf1a30Sjl139090 	board_cfg = &opl_boards[board];
112125cf1a30Sjl139090 
112225cf1a30Sjl139090 	ASSERT(board_cfg->cfg_pseudo_mc == NULL);
112325cf1a30Sjl139090 
112425cf1a30Sjl139090 	probe->pr_parent = ddi_root_node();
112525cf1a30Sjl139090 	probe->pr_create = opl_create_pseudo_mc;
112625cf1a30Sjl139090 	probe->pr_hold = 1;
112725cf1a30Sjl139090 	node = opl_create_node(probe);
112825cf1a30Sjl139090 	if (node == NULL) {
112925cf1a30Sjl139090 
113025cf1a30Sjl139090 		cmn_err(CE_WARN, "IKP: create pseudo-mc (%d) failed", board);
113125cf1a30Sjl139090 		return (-1);
113225cf1a30Sjl139090 	}
113325cf1a30Sjl139090 
113425cf1a30Sjl139090 	board_cfg->cfg_pseudo_mc = node;
113525cf1a30Sjl139090 
113625cf1a30Sjl139090 	return (0);
113725cf1a30Sjl139090 }
113825cf1a30Sjl139090 
113925cf1a30Sjl139090 /*
114025cf1a30Sjl139090  * Allocate the fcode ops handle.
114125cf1a30Sjl139090  */
114225cf1a30Sjl139090 /*ARGSUSED*/
114325cf1a30Sjl139090 static
114425cf1a30Sjl139090 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)114525cf1a30Sjl139090 opl_fc_ops_alloc_handle(dev_info_t *parent, dev_info_t *child,
114625cf1a30Sjl139090     void *fcode, size_t fcode_size, char *unit_address,
114725cf1a30Sjl139090     char *my_args)
114825cf1a30Sjl139090 {
114925cf1a30Sjl139090 	fco_handle_t	rp;
115025cf1a30Sjl139090 	phandle_t	h;
115125cf1a30Sjl139090 	char		*buf;
115225cf1a30Sjl139090 
115325cf1a30Sjl139090 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
115425cf1a30Sjl139090 	rp->next_handle = fc_ops_alloc_handle(parent, child, fcode, fcode_size,
115525cf1a30Sjl139090 	    unit_address, NULL);
115625cf1a30Sjl139090 	rp->ap = parent;
115725cf1a30Sjl139090 	rp->child = child;
115825cf1a30Sjl139090 	rp->fcode = fcode;
115925cf1a30Sjl139090 	rp->fcode_size = fcode_size;
116025cf1a30Sjl139090 	rp->my_args = my_args;
116125cf1a30Sjl139090 
116225cf1a30Sjl139090 	if (unit_address) {
116325cf1a30Sjl139090 		buf = kmem_zalloc(UNIT_ADDR_SIZE, KM_SLEEP);
116425cf1a30Sjl139090 		(void) strcpy(buf, unit_address);
116525cf1a30Sjl139090 		rp->unit_address = buf;
116625cf1a30Sjl139090 	}
116725cf1a30Sjl139090 
116825cf1a30Sjl139090 	/*
116925cf1a30Sjl139090 	 * Add the child's nodeid to our table...
117025cf1a30Sjl139090 	 */
117125cf1a30Sjl139090 	h = ddi_get_nodeid(rp->child);
117225cf1a30Sjl139090 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h);
117325cf1a30Sjl139090 
117425cf1a30Sjl139090 	return (rp);
117525cf1a30Sjl139090 }
117625cf1a30Sjl139090 
117725cf1a30Sjl139090 
117825cf1a30Sjl139090 static void
opl_fc_ops_free_handle(fco_handle_t rp)117925cf1a30Sjl139090 opl_fc_ops_free_handle(fco_handle_t rp)
118025cf1a30Sjl139090 {
118125cf1a30Sjl139090 	struct fc_resource	*resp, *nresp;
118225cf1a30Sjl139090 
118325cf1a30Sjl139090 	ASSERT(rp);
118425cf1a30Sjl139090 
118525cf1a30Sjl139090 	if (rp->next_handle)
118625cf1a30Sjl139090 		fc_ops_free_handle(rp->next_handle);
118725cf1a30Sjl139090 	if (rp->unit_address)
118825cf1a30Sjl139090 		kmem_free(rp->unit_address, UNIT_ADDR_SIZE);
118925cf1a30Sjl139090 
119025cf1a30Sjl139090 	/*
119125cf1a30Sjl139090 	 * Release all the resources from the resource list
119225cf1a30Sjl139090 	 */
119325cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = nresp) {
119425cf1a30Sjl139090 		nresp = resp->next;
119525cf1a30Sjl139090 		switch (resp->type) {
119625cf1a30Sjl139090 
119725cf1a30Sjl139090 		case RT_MAP:
119868ac2337Sjl139090 			/*
119968ac2337Sjl139090 			 * If this is still mapped, we'd better unmap it now,
120068ac2337Sjl139090 			 * or all our structures that are tracking it will
120168ac2337Sjl139090 			 * be leaked.
120268ac2337Sjl139090 			 */
120368ac2337Sjl139090 			if (resp->fc_map_handle != NULL)
120468ac2337Sjl139090 				opl_unmap_phys(&resp->fc_map_handle);
120525cf1a30Sjl139090 			break;
120625cf1a30Sjl139090 
120725cf1a30Sjl139090 		case RT_DMA:
120825cf1a30Sjl139090 			/*
120925cf1a30Sjl139090 			 * DMA has to be freed up at exit time.
121025cf1a30Sjl139090 			 */
121125cf1a30Sjl139090 			cmn_err(CE_CONT,
121225cf1a30Sjl139090 			    "opl_fc_ops_free_handle: Unexpected DMA seen!");
121325cf1a30Sjl139090 			break;
121425cf1a30Sjl139090 
121525cf1a30Sjl139090 		case RT_CONTIGIOUS:
121625cf1a30Sjl139090 			FC_DEBUG2(1, CE_CONT, "opl_fc_ops_free: "
121725cf1a30Sjl139090 			    "Free claim-memory resource 0x%lx size 0x%x\n",
121825cf1a30Sjl139090 			    resp->fc_contig_virt, resp->fc_contig_len);
121925cf1a30Sjl139090 
122025cf1a30Sjl139090 			(void) ndi_ra_free(ddi_root_node(),
122125cf1a30Sjl139090 			    (uint64_t)resp->fc_contig_virt,
122225cf1a30Sjl139090 			    resp->fc_contig_len, "opl-fcodemem",
122325cf1a30Sjl139090 			    NDI_RA_PASS);
122425cf1a30Sjl139090 
122525cf1a30Sjl139090 			break;
122625cf1a30Sjl139090 
122725cf1a30Sjl139090 		default:
122825cf1a30Sjl139090 			cmn_err(CE_CONT, "opl_fc_ops_free: "
122925cf1a30Sjl139090 			    "unknown resource type %d", resp->type);
123025cf1a30Sjl139090 			break;
123125cf1a30Sjl139090 		}
123225cf1a30Sjl139090 		fc_rem_resource(rp, resp);
123325cf1a30Sjl139090 		kmem_free(resp, sizeof (struct fc_resource));
123425cf1a30Sjl139090 	}
123525cf1a30Sjl139090 
123625cf1a30Sjl139090 	kmem_free(rp, sizeof (struct fc_resource_list));
123725cf1a30Sjl139090 }
123825cf1a30Sjl139090 
123925cf1a30Sjl139090 int
opl_fc_do_op(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)124025cf1a30Sjl139090 opl_fc_do_op(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
124125cf1a30Sjl139090 {
124225cf1a30Sjl139090 	opl_fc_ops_t	*op;
124325cf1a30Sjl139090 	char		*service = fc_cell2ptr(cp->svc_name);
124425cf1a30Sjl139090 
124525cf1a30Sjl139090 	ASSERT(rp);
124625cf1a30Sjl139090 
124725cf1a30Sjl139090 	FC_DEBUG1(1, CE_CONT, "opl_fc_do_op: <%s>\n", service);
124825cf1a30Sjl139090 
124925cf1a30Sjl139090 	/*
125025cf1a30Sjl139090 	 * First try the generic fc_ops.
125125cf1a30Sjl139090 	 */
125225cf1a30Sjl139090 	if (fc_ops(ap, rp->next_handle, cp) == 0)
125325cf1a30Sjl139090 		return (0);
125425cf1a30Sjl139090 
125525cf1a30Sjl139090 	/*
125625cf1a30Sjl139090 	 * Now try the Jupiter-specific ops.
125725cf1a30Sjl139090 	 */
125825cf1a30Sjl139090 	for (op = opl_fc_ops; op->fc_service != NULL; ++op)
125925cf1a30Sjl139090 		if (strcmp(op->fc_service, service) == 0)
126025cf1a30Sjl139090 			return (op->fc_op(ap, rp, cp));
126125cf1a30Sjl139090 
126225cf1a30Sjl139090 	FC_DEBUG1(9, CE_CONT, "opl_fc_do_op: <%s> not serviced\n", service);
126325cf1a30Sjl139090 
126425cf1a30Sjl139090 	return (-1);
126525cf1a30Sjl139090 }
126625cf1a30Sjl139090 
126725cf1a30Sjl139090 /*
126825cf1a30Sjl139090  * map-in  (phys.lo phys.hi size -- virt)
126925cf1a30Sjl139090  */
127025cf1a30Sjl139090 static int
opl_map_in(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)127125cf1a30Sjl139090 opl_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
127225cf1a30Sjl139090 {
127325cf1a30Sjl139090 	size_t			len;
127425cf1a30Sjl139090 	int			error;
127525cf1a30Sjl139090 	caddr_t			virt;
127625cf1a30Sjl139090 	struct fc_resource	*resp;
127725cf1a30Sjl139090 	struct regspec		rspec;
127825cf1a30Sjl139090 	ddi_device_acc_attr_t	acc;
127925cf1a30Sjl139090 	ddi_acc_handle_t	h;
128025cf1a30Sjl139090 
128125cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
128225cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
128325cf1a30Sjl139090 
128425cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
128525cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
128625cf1a30Sjl139090 
128725cf1a30Sjl139090 	rspec.regspec_size = len = fc_cell2size(fc_arg(cp, 0));
128825cf1a30Sjl139090 	rspec.regspec_bustype = fc_cell2uint(fc_arg(cp, 1));
128925cf1a30Sjl139090 	rspec.regspec_addr = fc_cell2uint(fc_arg(cp, 2));
129025cf1a30Sjl139090 
129125cf1a30Sjl139090 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
129225cf1a30Sjl139090 	acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
129325cf1a30Sjl139090 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
129425cf1a30Sjl139090 
129525cf1a30Sjl139090 	FC_DEBUG3(1, CE_CONT, "opl_map_in: attempting map in "
129625cf1a30Sjl139090 	    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
129725cf1a30Sjl139090 	    rspec.regspec_addr, rspec.regspec_size);
129825cf1a30Sjl139090 
129925cf1a30Sjl139090 	error = opl_map_phys(rp->child, &rspec, &virt, &acc, &h);
130025cf1a30Sjl139090 
130125cf1a30Sjl139090 	if (error)  {
130225cf1a30Sjl139090 		FC_DEBUG3(1, CE_CONT, "opl_map_in: map in failed - "
130325cf1a30Sjl139090 		    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
130425cf1a30Sjl139090 		    rspec.regspec_addr, rspec.regspec_size);
130525cf1a30Sjl139090 
130625cf1a30Sjl139090 		return (fc_priv_error(cp, "opl map-in failed"));
130725cf1a30Sjl139090 	}
130825cf1a30Sjl139090 
130925cf1a30Sjl139090 	FC_DEBUG1(3, CE_CONT, "opl_map_in: returning virt %p\n", virt);
131025cf1a30Sjl139090 
131125cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
131225cf1a30Sjl139090 	fc_result(cp, 0) = fc_ptr2cell(virt);
131325cf1a30Sjl139090 
131425cf1a30Sjl139090 	/*
131525cf1a30Sjl139090 	 * Log this resource ...
131625cf1a30Sjl139090 	 */
131725cf1a30Sjl139090 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
131825cf1a30Sjl139090 	resp->type = RT_MAP;
131925cf1a30Sjl139090 	resp->fc_map_virt = virt;
132025cf1a30Sjl139090 	resp->fc_map_len = len;
132125cf1a30Sjl139090 	resp->fc_map_handle = h;
132225cf1a30Sjl139090 	fc_add_resource(rp, resp);
132325cf1a30Sjl139090 
132425cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
132525cf1a30Sjl139090 }
132625cf1a30Sjl139090 
132725cf1a30Sjl139090 /*
132825cf1a30Sjl139090  * map-out (virt size -- )
132925cf1a30Sjl139090  */
133025cf1a30Sjl139090 static int
opl_map_out(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)133125cf1a30Sjl139090 opl_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
133225cf1a30Sjl139090 {
133325cf1a30Sjl139090 	caddr_t			virt;
133425cf1a30Sjl139090 	size_t			len;
133525cf1a30Sjl139090 	struct fc_resource	*resp;
133625cf1a30Sjl139090 
133725cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
133825cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
133925cf1a30Sjl139090 
134025cf1a30Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 1));
134125cf1a30Sjl139090 
134225cf1a30Sjl139090 	len = fc_cell2size(fc_arg(cp, 0));
134325cf1a30Sjl139090 
134425cf1a30Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_map_out: attempting map out %p %x\n",
134525cf1a30Sjl139090 	    virt, len);
134625cf1a30Sjl139090 
134725cf1a30Sjl139090 	/*
134825cf1a30Sjl139090 	 * Find if this request matches a mapping resource we set up.
134925cf1a30Sjl139090 	 */
135025cf1a30Sjl139090 	fc_lock_resource_list(rp);
135125cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
135225cf1a30Sjl139090 		if (resp->type != RT_MAP)
135325cf1a30Sjl139090 			continue;
135425cf1a30Sjl139090 		if (resp->fc_map_virt != virt)
135525cf1a30Sjl139090 			continue;
135625cf1a30Sjl139090 		if (resp->fc_map_len == len)
135725cf1a30Sjl139090 			break;
135825cf1a30Sjl139090 	}
135925cf1a30Sjl139090 	fc_unlock_resource_list(rp);
136025cf1a30Sjl139090 
136125cf1a30Sjl139090 	if (resp == NULL)
136225cf1a30Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
136325cf1a30Sjl139090 		    "known mapping"));
136425cf1a30Sjl139090 
136525cf1a30Sjl139090 	opl_unmap_phys(&resp->fc_map_handle);
136625cf1a30Sjl139090 
136725cf1a30Sjl139090 	/*
136825cf1a30Sjl139090 	 * remove the resource from the list and release it.
136925cf1a30Sjl139090 	 */
137025cf1a30Sjl139090 	fc_rem_resource(rp, resp);
137125cf1a30Sjl139090 	kmem_free(resp, sizeof (struct fc_resource));
137225cf1a30Sjl139090 
137325cf1a30Sjl139090 	cp->nresults = fc_int2cell(0);
137425cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
137525cf1a30Sjl139090 }
137625cf1a30Sjl139090 
137725cf1a30Sjl139090 static int
opl_register_fetch(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)137825cf1a30Sjl139090 opl_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
137925cf1a30Sjl139090 {
138025cf1a30Sjl139090 	size_t			len;
138125cf1a30Sjl139090 	caddr_t			virt;
138225cf1a30Sjl139090 	int			error = 0;
138325cf1a30Sjl139090 	uint64_t		v;
138425cf1a30Sjl139090 	uint64_t		x;
138525cf1a30Sjl139090 	uint32_t		l;
138625cf1a30Sjl139090 	uint16_t		w;
138725cf1a30Sjl139090 	uint8_t			b;
138825cf1a30Sjl139090 	char			*service = fc_cell2ptr(cp->svc_name);
138925cf1a30Sjl139090 	struct fc_resource	*resp;
139025cf1a30Sjl139090 
139125cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
139225cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
139325cf1a30Sjl139090 
139425cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
139525cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
139625cf1a30Sjl139090 
139725cf1a30Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
139825cf1a30Sjl139090 
139925cf1a30Sjl139090 	/*
140025cf1a30Sjl139090 	 * Determine the access width .. we can switch on the 2nd
140125cf1a30Sjl139090 	 * character of the name which is "rx@", "rl@", "rb@" or "rw@"
140225cf1a30Sjl139090 	 */
140325cf1a30Sjl139090 	switch (*(service + 1)) {
140425cf1a30Sjl139090 	case 'x':	len = sizeof (x); break;
140525cf1a30Sjl139090 	case 'l':	len = sizeof (l); break;
140625cf1a30Sjl139090 	case 'w':	len = sizeof (w); break;
140725cf1a30Sjl139090 	case 'b':	len = sizeof (b); break;
140825cf1a30Sjl139090 	}
140925cf1a30Sjl139090 
141025cf1a30Sjl139090 	/*
141125cf1a30Sjl139090 	 * Check the alignment ...
141225cf1a30Sjl139090 	 */
141325cf1a30Sjl139090 	if (((intptr_t)virt & (len - 1)) != 0)
141425cf1a30Sjl139090 		return (fc_priv_error(cp, "unaligned access"));
141525cf1a30Sjl139090 
141625cf1a30Sjl139090 	/*
141725cf1a30Sjl139090 	 * Find if this virt is 'within' a request we know about
141825cf1a30Sjl139090 	 */
141925cf1a30Sjl139090 	fc_lock_resource_list(rp);
142025cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
142125cf1a30Sjl139090 		if (resp->type == RT_MAP) {
142225cf1a30Sjl139090 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
142325cf1a30Sjl139090 			    ((virt + len) <=
142425cf1a30Sjl139090 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
142525cf1a30Sjl139090 				break;
142625cf1a30Sjl139090 		} else if (resp->type == RT_CONTIGIOUS) {
1427e98fafb9Sjl139090 			if ((virt >= (caddr_t)resp->fc_contig_virt) &&
1428e98fafb9Sjl139090 			    ((virt + len) <= ((caddr_t)resp->fc_contig_virt +
142925cf1a30Sjl139090 			    resp->fc_contig_len)))
143025cf1a30Sjl139090 				break;
143125cf1a30Sjl139090 		}
143225cf1a30Sjl139090 	}
143325cf1a30Sjl139090 	fc_unlock_resource_list(rp);
143425cf1a30Sjl139090 
143525cf1a30Sjl139090 	if (resp == NULL) {
143625cf1a30Sjl139090 		return (fc_priv_error(cp, "request not within "
143725cf1a30Sjl139090 		    "known mappings"));
143825cf1a30Sjl139090 	}
143925cf1a30Sjl139090 
144025cf1a30Sjl139090 	switch (len) {
144125cf1a30Sjl139090 	case sizeof (x):
144225cf1a30Sjl139090 		if (resp->type == RT_MAP)
1443e98fafb9Sjl139090 			error = ddi_peek64(rp->child, (int64_t *)virt,
1444e98fafb9Sjl139090 			    (int64_t *)&x);
144525cf1a30Sjl139090 		else /* RT_CONTIGIOUS */
144625cf1a30Sjl139090 			x = *(int64_t *)virt;
144725cf1a30Sjl139090 		v = x;
144825cf1a30Sjl139090 		break;
144925cf1a30Sjl139090 	case sizeof (l):
145025cf1a30Sjl139090 		if (resp->type == RT_MAP)
1451e98fafb9Sjl139090 			error = ddi_peek32(rp->child, (int32_t *)virt,
1452e98fafb9Sjl139090 			    (int32_t *)&l);
145325cf1a30Sjl139090 		else /* RT_CONTIGIOUS */
145425cf1a30Sjl139090 			l = *(int32_t *)virt;
145525cf1a30Sjl139090 		v = l;
145625cf1a30Sjl139090 		break;
145725cf1a30Sjl139090 	case sizeof (w):
145825cf1a30Sjl139090 		if (resp->type == RT_MAP)
1459e98fafb9Sjl139090 			error = ddi_peek16(rp->child, (int16_t *)virt,
1460e98fafb9Sjl139090 			    (int16_t *)&w);
146125cf1a30Sjl139090 		else /* RT_CONTIGIOUS */
146225cf1a30Sjl139090 			w = *(int16_t *)virt;
146325cf1a30Sjl139090 		v = w;
146425cf1a30Sjl139090 		break;
146525cf1a30Sjl139090 	case sizeof (b):
146625cf1a30Sjl139090 		if (resp->type == RT_MAP)
1467e98fafb9Sjl139090 			error = ddi_peek8(rp->child, (int8_t *)virt,
1468e98fafb9Sjl139090 			    (int8_t *)&b);
146925cf1a30Sjl139090 		else /* RT_CONTIGIOUS */
147025cf1a30Sjl139090 			b = *(int8_t *)virt;
147125cf1a30Sjl139090 		v = b;
147225cf1a30Sjl139090 		break;
147325cf1a30Sjl139090 	}
147425cf1a30Sjl139090 
147525cf1a30Sjl139090 	if (error == DDI_FAILURE) {
147625cf1a30Sjl139090 		FC_DEBUG2(1, CE_CONT, "opl_register_fetch: access error "
147725cf1a30Sjl139090 		    "accessing virt %p len %d\n", virt, len);
147825cf1a30Sjl139090 		return (fc_priv_error(cp, "access error"));
147925cf1a30Sjl139090 	}
148025cf1a30Sjl139090 
148125cf1a30Sjl139090 	FC_DEBUG3(1, CE_CONT, "register_fetch (%s) %llx %llx\n",
148225cf1a30Sjl139090 	    service, virt, v);
148325cf1a30Sjl139090 
148425cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
148525cf1a30Sjl139090 	switch (len) {
148625cf1a30Sjl139090 	case sizeof (x): fc_result(cp, 0) = x; break;
148725cf1a30Sjl139090 	case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break;
148825cf1a30Sjl139090 	case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break;
148925cf1a30Sjl139090 	case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break;
149025cf1a30Sjl139090 	}
149125cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
149225cf1a30Sjl139090 }
149325cf1a30Sjl139090 
149425cf1a30Sjl139090 static int
opl_register_store(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)149525cf1a30Sjl139090 opl_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
149625cf1a30Sjl139090 {
149725cf1a30Sjl139090 	size_t			len;
149825cf1a30Sjl139090 	caddr_t			virt;
149925cf1a30Sjl139090 	uint64_t		v;
150025cf1a30Sjl139090 	uint64_t		x;
150125cf1a30Sjl139090 	uint32_t		l;
150225cf1a30Sjl139090 	uint16_t		w;
150325cf1a30Sjl139090 	uint8_t			b;
150425cf1a30Sjl139090 	char			*service = fc_cell2ptr(cp->svc_name);
150525cf1a30Sjl139090 	struct fc_resource	*resp;
150625cf1a30Sjl139090 	int			error = 0;
150725cf1a30Sjl139090 
150825cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
150925cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
151025cf1a30Sjl139090 
151125cf1a30Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
151225cf1a30Sjl139090 
151325cf1a30Sjl139090 	/*
151425cf1a30Sjl139090 	 * Determine the access width .. we can switch on the 2nd
151525cf1a30Sjl139090 	 * character of the name which is "rx!", "rl!", "rb!" or "rw!"
151625cf1a30Sjl139090 	 */
151725cf1a30Sjl139090 	switch (*(service + 1)) {
151825cf1a30Sjl139090 	case 'x':
151925cf1a30Sjl139090 		len = sizeof (x);
152025cf1a30Sjl139090 		x = fc_arg(cp, 1);
152125cf1a30Sjl139090 		v = x;
152225cf1a30Sjl139090 		break;
152325cf1a30Sjl139090 	case 'l':
152425cf1a30Sjl139090 		len = sizeof (l);
152525cf1a30Sjl139090 		l = fc_cell2uint32_t(fc_arg(cp, 1));
152625cf1a30Sjl139090 		v = l;
152725cf1a30Sjl139090 		break;
152825cf1a30Sjl139090 	case 'w':
152925cf1a30Sjl139090 		len = sizeof (w);
153025cf1a30Sjl139090 		w = fc_cell2uint16_t(fc_arg(cp, 1));
153125cf1a30Sjl139090 		v = w;
153225cf1a30Sjl139090 		break;
153325cf1a30Sjl139090 	case 'b':
153425cf1a30Sjl139090 		len = sizeof (b);
153525cf1a30Sjl139090 		b = fc_cell2uint8_t(fc_arg(cp, 1));
153625cf1a30Sjl139090 		v = b;
153725cf1a30Sjl139090 		break;
153825cf1a30Sjl139090 	}
153925cf1a30Sjl139090 
154025cf1a30Sjl139090 	FC_DEBUG3(1, CE_CONT, "register_store (%s) %llx %llx\n",
154125cf1a30Sjl139090 	    service, virt, v);
154225cf1a30Sjl139090 
154325cf1a30Sjl139090 	/*
154425cf1a30Sjl139090 	 * Check the alignment ...
154525cf1a30Sjl139090 	 */
154625cf1a30Sjl139090 	if (((intptr_t)virt & (len - 1)) != 0)
154725cf1a30Sjl139090 		return (fc_priv_error(cp, "unaligned access"));
154825cf1a30Sjl139090 
154925cf1a30Sjl139090 	/*
155025cf1a30Sjl139090 	 * Find if this virt is 'within' a request we know about
155125cf1a30Sjl139090 	 */
155225cf1a30Sjl139090 	fc_lock_resource_list(rp);
155325cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
155425cf1a30Sjl139090 		if (resp->type == RT_MAP) {
155525cf1a30Sjl139090 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
155625cf1a30Sjl139090 			    ((virt + len) <=
155725cf1a30Sjl139090 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
155825cf1a30Sjl139090 				break;
155925cf1a30Sjl139090 		} else if (resp->type == RT_CONTIGIOUS) {
1560e98fafb9Sjl139090 			if ((virt >= (caddr_t)resp->fc_contig_virt) &&
1561e98fafb9Sjl139090 			    ((virt + len) <= ((caddr_t)resp->fc_contig_virt +
156225cf1a30Sjl139090 			    resp->fc_contig_len)))
156325cf1a30Sjl139090 				break;
156425cf1a30Sjl139090 		}
156525cf1a30Sjl139090 	}
156625cf1a30Sjl139090 	fc_unlock_resource_list(rp);
156725cf1a30Sjl139090 
156825cf1a30Sjl139090 	if (resp == NULL)
156925cf1a30Sjl139090 		return (fc_priv_error(cp, "request not within"
157025cf1a30Sjl139090 		    "known mappings"));
157125cf1a30Sjl139090 
157225cf1a30Sjl139090 	switch (len) {
157325cf1a30Sjl139090 	case sizeof (x):
157425cf1a30Sjl139090 		if (resp->type == RT_MAP)
157525cf1a30Sjl139090 			error = ddi_poke64(rp->child, (int64_t *)virt, x);
157625cf1a30Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
157725cf1a30Sjl139090 			*(uint64_t *)virt = x;
157825cf1a30Sjl139090 		break;
157925cf1a30Sjl139090 	case sizeof (l):
158025cf1a30Sjl139090 		if (resp->type == RT_MAP)
158125cf1a30Sjl139090 			error = ddi_poke32(rp->child, (int32_t *)virt, l);
158225cf1a30Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
158325cf1a30Sjl139090 			*(uint32_t *)virt = l;
158425cf1a30Sjl139090 		break;
158525cf1a30Sjl139090 	case sizeof (w):
158625cf1a30Sjl139090 		if (resp->type == RT_MAP)
158725cf1a30Sjl139090 			error = ddi_poke16(rp->child, (int16_t *)virt, w);
158825cf1a30Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
158925cf1a30Sjl139090 			*(uint16_t *)virt = w;
159025cf1a30Sjl139090 		break;
159125cf1a30Sjl139090 	case sizeof (b):
159225cf1a30Sjl139090 		if (resp->type == RT_MAP)
159325cf1a30Sjl139090 			error = ddi_poke8(rp->child, (int8_t *)virt, b);
159425cf1a30Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
159525cf1a30Sjl139090 			*(uint8_t *)virt = b;
159625cf1a30Sjl139090 		break;
159725cf1a30Sjl139090 	}
159825cf1a30Sjl139090 
159925cf1a30Sjl139090 	if (error == DDI_FAILURE) {
160025cf1a30Sjl139090 		FC_DEBUG2(1, CE_CONT, "opl_register_store: access error "
160125cf1a30Sjl139090 		    "accessing virt %p len %d\n", virt, len);
160225cf1a30Sjl139090 		return (fc_priv_error(cp, "access error"));
160325cf1a30Sjl139090 	}
160425cf1a30Sjl139090 
160525cf1a30Sjl139090 	cp->nresults = fc_int2cell(0);
160625cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
160725cf1a30Sjl139090 }
160825cf1a30Sjl139090 
160925cf1a30Sjl139090 /*
161025cf1a30Sjl139090  * opl_claim_memory
161125cf1a30Sjl139090  *
161225cf1a30Sjl139090  * claim-memory (align size vhint -- vaddr)
161325cf1a30Sjl139090  */
161425cf1a30Sjl139090 static int
opl_claim_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)161525cf1a30Sjl139090 opl_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
161625cf1a30Sjl139090 {
161725cf1a30Sjl139090 	int			align, size, vhint;
161825cf1a30Sjl139090 	uint64_t		answer, alen;
161925cf1a30Sjl139090 	ndi_ra_request_t	request;
162025cf1a30Sjl139090 	struct fc_resource	*resp;
162125cf1a30Sjl139090 
162225cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
162325cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
162425cf1a30Sjl139090 
162525cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
162625cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
162725cf1a30Sjl139090 
162825cf1a30Sjl139090 	vhint = fc_cell2int(fc_arg(cp, 2));
162925cf1a30Sjl139090 	size  = fc_cell2int(fc_arg(cp, 1));
163025cf1a30Sjl139090 	align = fc_cell2int(fc_arg(cp, 0));
163125cf1a30Sjl139090 
163225cf1a30Sjl139090 	FC_DEBUG3(1, CE_CONT, "opl_claim_memory: align=0x%x size=0x%x "
163325cf1a30Sjl139090 	    "vhint=0x%x\n", align, size, vhint);
163425cf1a30Sjl139090 
163525cf1a30Sjl139090 	if (size == 0) {
163625cf1a30Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
163725cf1a30Sjl139090 		    "contiguous memory of size zero\n");
163825cf1a30Sjl139090 		return (fc_priv_error(cp, "allocation error"));
163925cf1a30Sjl139090 	}
164025cf1a30Sjl139090 
164125cf1a30Sjl139090 	if (vhint) {
164225cf1a30Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - vhint is not zero "
164325cf1a30Sjl139090 		    "vhint=0x%x - Ignoring Argument\n", vhint);
164425cf1a30Sjl139090 	}
164525cf1a30Sjl139090 
164625cf1a30Sjl139090 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
164725cf1a30Sjl139090 	request.ra_flags	= NDI_RA_ALLOC_BOUNDED;
164825cf1a30Sjl139090 	request.ra_boundbase	= 0;
164925cf1a30Sjl139090 	request.ra_boundlen	= 0xffffffff;
165025cf1a30Sjl139090 	request.ra_len		= size;
165125cf1a30Sjl139090 	request.ra_align_mask	= align - 1;
165225cf1a30Sjl139090 
165325cf1a30Sjl139090 	if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen,
165425cf1a30Sjl139090 	    "opl-fcodemem", NDI_RA_PASS) != NDI_SUCCESS) {
165525cf1a30Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
165625cf1a30Sjl139090 		    "contiguous memory\n");
165725cf1a30Sjl139090 		return (fc_priv_error(cp, "allocation error"));
165825cf1a30Sjl139090 	}
165925cf1a30Sjl139090 
166025cf1a30Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_claim_memory: address allocated=0x%lx "
166125cf1a30Sjl139090 	    "size=0x%x\n", answer, alen);
166225cf1a30Sjl139090 
166325cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
166425cf1a30Sjl139090 	fc_result(cp, 0) = answer;
166525cf1a30Sjl139090 
166625cf1a30Sjl139090 	/*
166725cf1a30Sjl139090 	 * Log this resource ...
166825cf1a30Sjl139090 	 */
166925cf1a30Sjl139090 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
167025cf1a30Sjl139090 	resp->type = RT_CONTIGIOUS;
167125cf1a30Sjl139090 	resp->fc_contig_virt = (void *)answer;
167225cf1a30Sjl139090 	resp->fc_contig_len = size;
167325cf1a30Sjl139090 	fc_add_resource(rp, resp);
167425cf1a30Sjl139090 
167525cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
167625cf1a30Sjl139090 }
167725cf1a30Sjl139090 
167825cf1a30Sjl139090 /*
167925cf1a30Sjl139090  * opl_release_memory
168025cf1a30Sjl139090  *
168125cf1a30Sjl139090  * release-memory (size vaddr -- )
168225cf1a30Sjl139090  */
168325cf1a30Sjl139090 static int
opl_release_memory(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)168425cf1a30Sjl139090 opl_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
168525cf1a30Sjl139090 {
168625cf1a30Sjl139090 	int32_t			vaddr, size;
168725cf1a30Sjl139090 	struct fc_resource	*resp;
168825cf1a30Sjl139090 
168925cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
169025cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
169125cf1a30Sjl139090 
169225cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) != 0)
169325cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be 0"));
169425cf1a30Sjl139090 
169525cf1a30Sjl139090 	vaddr = fc_cell2int(fc_arg(cp, 1));
169625cf1a30Sjl139090 	size  = fc_cell2int(fc_arg(cp, 0));
169725cf1a30Sjl139090 
169825cf1a30Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_release_memory: vaddr=0x%x size=0x%x\n",
169925cf1a30Sjl139090 	    vaddr, size);
170025cf1a30Sjl139090 
170125cf1a30Sjl139090 	/*
170225cf1a30Sjl139090 	 * Find if this request matches a mapping resource we set up.
170325cf1a30Sjl139090 	 */
170425cf1a30Sjl139090 	fc_lock_resource_list(rp);
170525cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
170625cf1a30Sjl139090 		if (resp->type != RT_CONTIGIOUS)
170725cf1a30Sjl139090 			continue;
170825cf1a30Sjl139090 		if (resp->fc_contig_virt != (void *)(uintptr_t)vaddr)
170925cf1a30Sjl139090 			continue;
171025cf1a30Sjl139090 		if (resp->fc_contig_len == size)
171125cf1a30Sjl139090 			break;
171225cf1a30Sjl139090 	}
171325cf1a30Sjl139090 	fc_unlock_resource_list(rp);
171425cf1a30Sjl139090 
171525cf1a30Sjl139090 	if (resp == NULL)
171625cf1a30Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
171725cf1a30Sjl139090 		    "known mapping"));
171825cf1a30Sjl139090 
171925cf1a30Sjl139090 	(void) ndi_ra_free(ddi_root_node(), vaddr, size,
172025cf1a30Sjl139090 	    "opl-fcodemem", NDI_RA_PASS);
172125cf1a30Sjl139090 
172225cf1a30Sjl139090 	/*
172325cf1a30Sjl139090 	 * remove the resource from the list and release it.
172425cf1a30Sjl139090 	 */
172525cf1a30Sjl139090 	fc_rem_resource(rp, resp);
172625cf1a30Sjl139090 	kmem_free(resp, sizeof (struct fc_resource));
172725cf1a30Sjl139090 
172825cf1a30Sjl139090 	cp->nresults = fc_int2cell(0);
172925cf1a30Sjl139090 
173025cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
173125cf1a30Sjl139090 }
173225cf1a30Sjl139090 
173325cf1a30Sjl139090 /*
173425cf1a30Sjl139090  * opl_vtop
173525cf1a30Sjl139090  *
173625cf1a30Sjl139090  * vtop (vaddr -- paddr.lo paddr.hi)
173725cf1a30Sjl139090  */
173825cf1a30Sjl139090 static int
opl_vtop(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)173925cf1a30Sjl139090 opl_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
174025cf1a30Sjl139090 {
174125cf1a30Sjl139090 	int			vaddr;
174225cf1a30Sjl139090 	uint64_t		paddr;
174325cf1a30Sjl139090 	struct fc_resource	*resp;
174425cf1a30Sjl139090 
174525cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
174625cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
174725cf1a30Sjl139090 
174825cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) >= 3)
174925cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be less than 2"));
175025cf1a30Sjl139090 
175125cf1a30Sjl139090 	vaddr = fc_cell2int(fc_arg(cp, 0));
175225cf1a30Sjl139090 
175325cf1a30Sjl139090 	/*
175425cf1a30Sjl139090 	 * Find if this request matches a mapping resource we set up.
175525cf1a30Sjl139090 	 */
175625cf1a30Sjl139090 	fc_lock_resource_list(rp);
175725cf1a30Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
175825cf1a30Sjl139090 		if (resp->type != RT_CONTIGIOUS)
175925cf1a30Sjl139090 			continue;
1760431161ebSbm42561 		if (((uint64_t)resp->fc_contig_virt <= vaddr) &&
1761431161ebSbm42561 		    (vaddr < (uint64_t)resp->fc_contig_virt +
1762431161ebSbm42561 		    resp->fc_contig_len))
176325cf1a30Sjl139090 			break;
176425cf1a30Sjl139090 	}
176525cf1a30Sjl139090 	fc_unlock_resource_list(rp);
176625cf1a30Sjl139090 
176725cf1a30Sjl139090 	if (resp == NULL)
176825cf1a30Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
176925cf1a30Sjl139090 		    "known mapping"));
177025cf1a30Sjl139090 
177125cf1a30Sjl139090 	paddr = va_to_pa((void *)(uintptr_t)vaddr);
177225cf1a30Sjl139090 
177325cf1a30Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_vtop: vaddr=0x%x paddr=0x%x\n",
177425cf1a30Sjl139090 	    vaddr, paddr);
177525cf1a30Sjl139090 
177625cf1a30Sjl139090 	cp->nresults = fc_int2cell(2);
177725cf1a30Sjl139090 
177825cf1a30Sjl139090 	fc_result(cp, 0) = paddr;
177925cf1a30Sjl139090 	fc_result(cp, 1) = 0;
178025cf1a30Sjl139090 
178125cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
178225cf1a30Sjl139090 }
178325cf1a30Sjl139090 
178425cf1a30Sjl139090 static int
opl_config_child(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)178525cf1a30Sjl139090 opl_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
178625cf1a30Sjl139090 {
178725cf1a30Sjl139090 	fc_phandle_t h;
178825cf1a30Sjl139090 
178925cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 0)
179025cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 0"));
179125cf1a30Sjl139090 
179225cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
179325cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
179425cf1a30Sjl139090 
179525cf1a30Sjl139090 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child);
179625cf1a30Sjl139090 
179725cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
179825cf1a30Sjl139090 	fc_result(cp, 0) = fc_phandle2cell(h);
179925cf1a30Sjl139090 
180025cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
180125cf1a30Sjl139090 }
180225cf1a30Sjl139090 
180325cf1a30Sjl139090 static int
opl_get_fcode(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)180425cf1a30Sjl139090 opl_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
180525cf1a30Sjl139090 {
180625cf1a30Sjl139090 	caddr_t		dropin_name_virt, fcode_virt;
180725cf1a30Sjl139090 	char		*dropin_name, *fcode;
180825cf1a30Sjl139090 	int		fcode_len, status;
180925cf1a30Sjl139090 
181025cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
181125cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
181225cf1a30Sjl139090 
181325cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
181425cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
181525cf1a30Sjl139090 
181625cf1a30Sjl139090 	dropin_name_virt = fc_cell2ptr(fc_arg(cp, 0));
181725cf1a30Sjl139090 
181825cf1a30Sjl139090 	fcode_virt = fc_cell2ptr(fc_arg(cp, 1));
181925cf1a30Sjl139090 
182025cf1a30Sjl139090 	fcode_len = fc_cell2int(fc_arg(cp, 2));
182125cf1a30Sjl139090 
182225cf1a30Sjl139090 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
182325cf1a30Sjl139090 
182425cf1a30Sjl139090 	FC_DEBUG2(1, CE_CONT, "get_fcode: %x %d\n", fcode_virt, fcode_len);
182525cf1a30Sjl139090 
182625cf1a30Sjl139090 	if (copyinstr(fc_cell2ptr(dropin_name_virt), dropin_name,
182725cf1a30Sjl139090 	    FC_SVC_NAME_LEN - 1, NULL))  {
182825cf1a30Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode: "
182925cf1a30Sjl139090 		    "fault copying in drop in name %p\n", dropin_name_virt);
183025cf1a30Sjl139090 		status = 0;
183125cf1a30Sjl139090 	} else {
183225cf1a30Sjl139090 		FC_DEBUG1(1, CE_CONT, "get_fcode: %s\n", dropin_name);
183325cf1a30Sjl139090 
183425cf1a30Sjl139090 		fcode = kmem_zalloc(fcode_len, KM_SLEEP);
183525cf1a30Sjl139090 
183625cf1a30Sjl139090 		if ((status = prom_get_fcode(dropin_name, fcode)) != 0) {
183725cf1a30Sjl139090 
183825cf1a30Sjl139090 			if (copyout((void *)fcode, (void *)fcode_virt,
183925cf1a30Sjl139090 			    fcode_len)) {
184025cf1a30Sjl139090 				cmn_err(CE_WARN, " opl_get_fcode: Unable "
184125cf1a30Sjl139090 				    "to copy out fcode image");
184225cf1a30Sjl139090 				status = 0;
184325cf1a30Sjl139090 			}
184425cf1a30Sjl139090 		}
184525cf1a30Sjl139090 
184625cf1a30Sjl139090 		kmem_free(fcode, fcode_len);
184725cf1a30Sjl139090 	}
184825cf1a30Sjl139090 
184925cf1a30Sjl139090 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
185025cf1a30Sjl139090 
185125cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
185225cf1a30Sjl139090 	fc_result(cp, 0) = status;
185325cf1a30Sjl139090 
185425cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
185525cf1a30Sjl139090 }
185625cf1a30Sjl139090 
185725cf1a30Sjl139090 static int
opl_get_fcode_size(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)185825cf1a30Sjl139090 opl_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
185925cf1a30Sjl139090 {
186025cf1a30Sjl139090 	caddr_t		virt;
186125cf1a30Sjl139090 	char		*dropin_name;
186225cf1a30Sjl139090 	int		len;
186325cf1a30Sjl139090 
186425cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
186525cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
186625cf1a30Sjl139090 
186725cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
186825cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
186925cf1a30Sjl139090 
187025cf1a30Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
187125cf1a30Sjl139090 
187225cf1a30Sjl139090 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
187325cf1a30Sjl139090 
187425cf1a30Sjl139090 	FC_DEBUG0(1, CE_CONT, "opl_get_fcode_size:\n");
187525cf1a30Sjl139090 
187625cf1a30Sjl139090 	if (copyinstr(fc_cell2ptr(virt), dropin_name,
187725cf1a30Sjl139090 	    FC_SVC_NAME_LEN - 1, NULL))  {
187825cf1a30Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: "
187925cf1a30Sjl139090 		    "fault copying in drop in name %p\n", virt);
188025cf1a30Sjl139090 		len = 0;
188125cf1a30Sjl139090 	} else {
188225cf1a30Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: %s\n", dropin_name);
188325cf1a30Sjl139090 
188425cf1a30Sjl139090 		len = prom_get_fcode_size(dropin_name);
188525cf1a30Sjl139090 	}
188625cf1a30Sjl139090 
188725cf1a30Sjl139090 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
188825cf1a30Sjl139090 
188925cf1a30Sjl139090 	FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: fcode_len = %d\n", len);
189025cf1a30Sjl139090 
189125cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
189225cf1a30Sjl139090 	fc_result(cp, 0) = len;
189325cf1a30Sjl139090 
189425cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
189525cf1a30Sjl139090 }
189625cf1a30Sjl139090 
189725cf1a30Sjl139090 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)189825cf1a30Sjl139090 opl_map_phys(dev_info_t *dip, struct regspec *phys_spec,
189925cf1a30Sjl139090     caddr_t *addrp, ddi_device_acc_attr_t *accattrp,
190025cf1a30Sjl139090     ddi_acc_handle_t *handlep)
190125cf1a30Sjl139090 {
190225cf1a30Sjl139090 	ddi_map_req_t	mapreq;
190325cf1a30Sjl139090 	ddi_acc_hdl_t	*acc_handlep;
190425cf1a30Sjl139090 	int		result;
190525cf1a30Sjl139090 	struct regspec	*rspecp;
190625cf1a30Sjl139090 
190725cf1a30Sjl139090 	*handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL);
190825cf1a30Sjl139090 	acc_handlep = impl_acc_hdl_get(*handlep);
190925cf1a30Sjl139090 	acc_handlep->ah_vers = VERS_ACCHDL;
191025cf1a30Sjl139090 	acc_handlep->ah_dip = dip;
191125cf1a30Sjl139090 	acc_handlep->ah_rnumber = 0;
191225cf1a30Sjl139090 	acc_handlep->ah_offset = 0;
191325cf1a30Sjl139090 	acc_handlep->ah_len = 0;
191425cf1a30Sjl139090 	acc_handlep->ah_acc = *accattrp;
191525cf1a30Sjl139090 	rspecp = kmem_zalloc(sizeof (struct regspec), KM_SLEEP);
191625cf1a30Sjl139090 	*rspecp = *phys_spec;
191725cf1a30Sjl139090 	/*
191825cf1a30Sjl139090 	 * cache a copy of the reg spec
191925cf1a30Sjl139090 	 */
192025cf1a30Sjl139090 	acc_handlep->ah_bus_private = rspecp;
192125cf1a30Sjl139090 
192225cf1a30Sjl139090 	mapreq.map_op = DDI_MO_MAP_LOCKED;
192325cf1a30Sjl139090 	mapreq.map_type = DDI_MT_REGSPEC;
192425cf1a30Sjl139090 	mapreq.map_obj.rp = (struct regspec *)phys_spec;
192525cf1a30Sjl139090 	mapreq.map_prot = PROT_READ | PROT_WRITE;
192625cf1a30Sjl139090 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
192725cf1a30Sjl139090 	mapreq.map_handlep = acc_handlep;
192825cf1a30Sjl139090 	mapreq.map_vers = DDI_MAP_VERSION;
192925cf1a30Sjl139090 
193025cf1a30Sjl139090 	result = ddi_map(dip, &mapreq, 0, 0, addrp);
193125cf1a30Sjl139090 
193225cf1a30Sjl139090 	if (result != DDI_SUCCESS) {
193325cf1a30Sjl139090 		impl_acc_hdl_free(*handlep);
193468ac2337Sjl139090 		kmem_free(rspecp, sizeof (struct regspec));
193525cf1a30Sjl139090 		*handlep = (ddi_acc_handle_t)NULL;
193625cf1a30Sjl139090 	} else {
193725cf1a30Sjl139090 		acc_handlep->ah_addr = *addrp;
193825cf1a30Sjl139090 	}
193925cf1a30Sjl139090 
194025cf1a30Sjl139090 	return (result);
194125cf1a30Sjl139090 }
194225cf1a30Sjl139090 
194325cf1a30Sjl139090 static void
opl_unmap_phys(ddi_acc_handle_t * handlep)194425cf1a30Sjl139090 opl_unmap_phys(ddi_acc_handle_t *handlep)
194525cf1a30Sjl139090 {
194625cf1a30Sjl139090 	ddi_map_req_t	mapreq;
194725cf1a30Sjl139090 	ddi_acc_hdl_t	*acc_handlep;
194825cf1a30Sjl139090 	struct regspec	*rspecp;
194925cf1a30Sjl139090 
195025cf1a30Sjl139090 	acc_handlep = impl_acc_hdl_get(*handlep);
195125cf1a30Sjl139090 	ASSERT(acc_handlep);
195225cf1a30Sjl139090 	rspecp = acc_handlep->ah_bus_private;
195325cf1a30Sjl139090 
195425cf1a30Sjl139090 	mapreq.map_op = DDI_MO_UNMAP;
195525cf1a30Sjl139090 	mapreq.map_type = DDI_MT_REGSPEC;
195625cf1a30Sjl139090 	mapreq.map_obj.rp = (struct regspec *)rspecp;
195725cf1a30Sjl139090 	mapreq.map_prot = PROT_READ | PROT_WRITE;
195825cf1a30Sjl139090 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
195925cf1a30Sjl139090 	mapreq.map_handlep = acc_handlep;
196025cf1a30Sjl139090 	mapreq.map_vers = DDI_MAP_VERSION;
196125cf1a30Sjl139090 
196225cf1a30Sjl139090 	(void) ddi_map(acc_handlep->ah_dip, &mapreq, acc_handlep->ah_offset,
196325cf1a30Sjl139090 	    acc_handlep->ah_len, &acc_handlep->ah_addr);
196425cf1a30Sjl139090 
196525cf1a30Sjl139090 	impl_acc_hdl_free(*handlep);
196625cf1a30Sjl139090 	/*
196725cf1a30Sjl139090 	 * Free the cached copy
196825cf1a30Sjl139090 	 */
196925cf1a30Sjl139090 	kmem_free(rspecp, sizeof (struct regspec));
197025cf1a30Sjl139090 	*handlep = (ddi_acc_handle_t)NULL;
197125cf1a30Sjl139090 }
197225cf1a30Sjl139090 
197325cf1a30Sjl139090 static int
opl_get_hwd_va(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)197425cf1a30Sjl139090 opl_get_hwd_va(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
197525cf1a30Sjl139090 {
197625cf1a30Sjl139090 	uint32_t	portid;
197725cf1a30Sjl139090 	void		*hwd_virt;
197825cf1a30Sjl139090 	hwd_header_t	*hwd_h = NULL;
197925cf1a30Sjl139090 	hwd_sb_t	*hwd_sb = NULL;
198025cf1a30Sjl139090 	int		lsb, ch, leaf;
198125cf1a30Sjl139090 	int		status = 1;
198225cf1a30Sjl139090 
198325cf1a30Sjl139090 	/* Check the argument */
198425cf1a30Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
198525cf1a30Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
198625cf1a30Sjl139090 
198725cf1a30Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
198825cf1a30Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
198925cf1a30Sjl139090 
199025cf1a30Sjl139090 	/* Get the parameters */
199125cf1a30Sjl139090 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
199225cf1a30Sjl139090 	hwd_virt = (void *)fc_cell2ptr(fc_arg(cp, 1));
199325cf1a30Sjl139090 
199425cf1a30Sjl139090 	/* Get the ID numbers */
199525cf1a30Sjl139090 	lsb  = OPL_IO_PORTID_TO_LSB(portid);
199625cf1a30Sjl139090 	ch   = OPL_PORTID_TO_CHANNEL(portid);
199725cf1a30Sjl139090 	leaf = OPL_PORTID_TO_LEAF(portid);
199825cf1a30Sjl139090 	ASSERT(OPL_IO_PORTID(lsb, ch, leaf) == portid);
199925cf1a30Sjl139090 
200025cf1a30Sjl139090 	/* Set the pointer of hwd. */
200125cf1a30Sjl139090 	if ((hwd_h = (hwd_header_t *)opl_boards[lsb].cfg_hwd) == NULL) {
200225cf1a30Sjl139090 		return (fc_priv_error(cp, "null hwd header"));
200325cf1a30Sjl139090 	}
200425cf1a30Sjl139090 	/* Set the pointer of hwd sb. */
200525cf1a30Sjl139090 	if ((hwd_sb = (hwd_sb_t *)((char *)hwd_h + hwd_h->hdr_sb_info_offset))
200625cf1a30Sjl139090 	    == NULL) {
200725cf1a30Sjl139090 		return (fc_priv_error(cp, "null hwd sb"));
200825cf1a30Sjl139090 	}
200925cf1a30Sjl139090 
201025cf1a30Sjl139090 	if (ch == OPL_CMU_CHANNEL) {
201125cf1a30Sjl139090 		/* Copyout CMU-CH HW Descriptor */
201225cf1a30Sjl139090 		if (copyout((void *)&hwd_sb->sb_cmu.cmu_ch,
201325cf1a30Sjl139090 		    (void *)hwd_virt, sizeof (hwd_cmu_chan_t))) {
201425cf1a30Sjl139090 			cmn_err(CE_WARN, "opl_get_hwd_va: "
201525cf1a30Sjl139090 			"Unable to copy out cmuch descriptor for %x",
201625cf1a30Sjl139090 			    portid);
201725cf1a30Sjl139090 			status = 0;
201825cf1a30Sjl139090 		}
201925cf1a30Sjl139090 	} else {
202025cf1a30Sjl139090 		/* Copyout PCI-CH HW Descriptor */
202125cf1a30Sjl139090 		if (copyout((void *)&hwd_sb->sb_pci_ch[ch].pci_leaf[leaf],
202225cf1a30Sjl139090 		    (void *)hwd_virt, sizeof (hwd_leaf_t))) {
202325cf1a30Sjl139090 			cmn_err(CE_WARN, "opl_get_hwd_va: "
202425cf1a30Sjl139090 			"Unable to copy out pcich descriptor for %x",
202525cf1a30Sjl139090 			    portid);
202625cf1a30Sjl139090 			status = 0;
202725cf1a30Sjl139090 		}
202825cf1a30Sjl139090 	}
202925cf1a30Sjl139090 
203025cf1a30Sjl139090 	cp->nresults = fc_int2cell(1);
203125cf1a30Sjl139090 	fc_result(cp, 0) = status;
203225cf1a30Sjl139090 
203325cf1a30Sjl139090 	return (fc_success_op(ap, rp, cp));
203425cf1a30Sjl139090 }
203525cf1a30Sjl139090 
203625cf1a30Sjl139090 /*
203753123245Smv143129  * After Solaris boots, a user can enter OBP using L1A, etc. While in OBP,
203853123245Smv143129  * interrupts may be received from PCI devices. These interrupts
203953123245Smv143129  * cannot be handled meaningfully since the system is in OBP. These
204053123245Smv143129  * interrupts need to be cleared on the CPU side so that the CPU may
204153123245Smv143129  * continue with whatever it is doing. Devices that have raised the
204253123245Smv143129  * interrupts are expected to reraise the interrupts after sometime
204353123245Smv143129  * as they have not been handled. At that time, Solaris will have a
204453123245Smv143129  * chance to properly service the interrupts.
204553123245Smv143129  *
204653123245Smv143129  * The location of the interrupt registers depends on what is present
204753123245Smv143129  * at a port. OPL currently supports the Oberon and the CMU channel.
204853123245Smv143129  * The following handler handles both kinds of ports and computes
204953123245Smv143129  * interrupt register addresses from the specifications and Jupiter Bus
205053123245Smv143129  * device bindings.
205153123245Smv143129  *
205253123245Smv143129  * Fcode drivers install their interrupt handler via a "master-interrupt"
205353123245Smv143129  * service. For boot time devices, this takes place within OBP. In the case
205453123245Smv143129  * of DR, OPL uses IKP. The Fcode drivers that run within the efcode framework
205553123245Smv143129  * attempt to install their handler via the "master-interrupt" service.
205653123245Smv143129  * However, we cannot meaningfully install the Fcode driver's handler.
205753123245Smv143129  * Instead, we install our own handler in OBP which does the same thing.
205853123245Smv143129  *
205953123245Smv143129  * Note that the only handling done for interrupts here is to clear it
206053123245Smv143129  * on the CPU side. If any device in the future requires more special
206153123245Smv143129  * handling, we would have to put in some kind of framework for adding
206253123245Smv143129  * device-specific handlers. This is *highly* unlikely, but possible.
206353123245Smv143129  *
206453123245Smv143129  * Finally, OBP provides a hook called "unix-interrupt-handler" to install
206553123245Smv143129  * a Solaris-defined master-interrupt handler for a port. The default
206653123245Smv143129  * definition for this method does nothing. Solaris may override this
206753123245Smv143129  * with its own definition. This is the way the following handler gets
206853123245Smv143129  * control from OBP when interrupts happen at a port after L1A, etc.
206953123245Smv143129  */
207053123245Smv143129 
207153123245Smv143129 static char define_master_interrupt_handler[] =
207253123245Smv143129 
207353123245Smv143129 /*
207453123245Smv143129  * This method translates an Oberon port id to the base (physical) address
207553123245Smv143129  * of the interrupt clear registers for that port id.
207653123245Smv143129  */
207753123245Smv143129 
207853123245Smv143129 ": pcich-mid>clear-int-pa   ( mid -- pa ) "
207953123245Smv143129 "   dup 1 >> 7 and          ( mid ch# ) "
208053123245Smv143129 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
208153123245Smv143129 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
208253123245Smv143129 "   swap d# 40 << or        ( mid ch# pa ) "
208353123245Smv143129 "   swap d# 37 << or        ( mid pa ) "
208453123245Smv143129 "   swap 1 and if h# 70.0000 else h# 60.0000 then "
208553123245Smv143129 "   or h# 1400 or           ( pa ) "
208653123245Smv143129 "; "
208753123245Smv143129 
208853123245Smv143129 /*
208953123245Smv143129  * This method translates a CMU channel port id to the base (physical) address
209053123245Smv143129  * of the interrupt clear registers for that port id. There are two classes of
209153123245Smv143129  * interrupts that need to be handled for a CMU channel:
209253123245Smv143129  *	- obio interrupts
209353123245Smv143129  *	- pci interrupts
209453123245Smv143129  * So, there are two addresses that need to be computed.
209553123245Smv143129  */
209653123245Smv143129 
209753123245Smv143129 ": cmuch-mid>clear-int-pa   ( mid -- obio-pa pci-pa ) "
209853123245Smv143129 "   dup 1 >> 7 and          ( mid ch# ) "
209953123245Smv143129 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
210053123245Smv143129 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
210153123245Smv143129 "   swap d# 40 << or        ( mid ch# pa ) "
210253123245Smv143129 "   swap d# 37 << or        ( mid pa ) "
210353123245Smv143129 "   nip dup h# 1800 +       ( pa obio-pa ) "
210453123245Smv143129 "   swap h# 1400 +          ( obio-pa pci-pa ) "
210553123245Smv143129 "; "
210653123245Smv143129 
210753123245Smv143129 /*
210853123245Smv143129  * This method checks if a given I/O port ID is valid or not.
210953123245Smv143129  * For a given LSB,
211053123245Smv143129  *	Oberon ports range from 0 - 3
211153123245Smv143129  *	CMU ch ports range from 4 - 4
211253123245Smv143129  *
211353123245Smv143129  * Also, the Oberon supports leaves 0 and 1.
211453123245Smv143129  * The CMU ch supports only one leaf, leaf 0.
211553123245Smv143129  */
211653123245Smv143129 
211753123245Smv143129 ": valid-io-mid? ( mid -- flag ) "
211853123245Smv143129 "   dup 1 >> 7 and                     ( mid ch# ) "
211953123245Smv143129 "   dup 4 > if 2drop false exit then   ( mid ch# ) "
212053123245Smv143129 "   4 = swap 1 and 1 = and not "
212153123245Smv143129 "; "
212253123245Smv143129 
212353123245Smv143129 /*
212453123245Smv143129  * This method checks if a given port id is a CMU ch.
212553123245Smv143129  */
212653123245Smv143129 
212753123245Smv143129 ": cmuch? ( mid -- flag ) 1 >> 7 and 4 = ; "
212853123245Smv143129 
212953123245Smv143129 /*
213053123245Smv143129  * Given the base address of the array of interrupt clear registers for
213153123245Smv143129  * a port id, this method iterates over the given interrupt number bitmap
213253123245Smv143129  * and resets the interrupt on the CPU side for every interrupt number
213353123245Smv143129  * in the bitmap. Note that physical addresses are used to perform the
213453123245Smv143129  * writes, not virtual addresses. This allows the handler to work without
213553123245Smv143129  * any involvement from Solaris.
213653123245Smv143129  */
213753123245Smv143129 
213853123245Smv143129 ": clear-ints ( pa bitmap count -- ) "
213953123245Smv143129 "   0 do                            ( pa bitmap ) "
214053123245Smv143129 "      dup 0= if 2drop unloop exit then "
214153123245Smv143129 "      tuck                         ( bitmap pa bitmap ) "
214253123245Smv143129 "      1 and if                     ( bitmap pa ) "
214353123245Smv143129 "	 dup i 8 * + 0 swap         ( bitmap pa 0 pa' ) "
214453123245Smv143129 "	 h# 15 spacex!              ( bitmap pa ) "
214553123245Smv143129 "      then                         ( bitmap pa ) "
214653123245Smv143129 "      swap 1 >>                    ( pa bitmap ) "
214753123245Smv143129 "   loop "
214853123245Smv143129 "; "
214953123245Smv143129 
215053123245Smv143129 /*
215153123245Smv143129  * This method replaces the master-interrupt handler in OBP. Once
215253123245Smv143129  * this method is plumbed into OBP, OBP transfers control to this
215353123245Smv143129  * handler while returning to Solaris from OBP after L1A. This method's
215453123245Smv143129  * task is to simply reset received interrupts on the CPU side.
215553123245Smv143129  * When the devices reassert the interrupts later, Solaris will
215653123245Smv143129  * be able to see them and handle them.
215753123245Smv143129  *
215853123245Smv143129  * For each port ID that has interrupts, this method is called
215953123245Smv143129  * once by OBP. The input arguments are:
216053123245Smv143129  *	mid	portid
216153123245Smv143129  *	bitmap	bitmap of interrupts that have happened
216253123245Smv143129  *
216353123245Smv143129  * This method returns true, if it is able to handle the interrupts.
216453123245Smv143129  * OBP does nothing further.
216553123245Smv143129  *
216653123245Smv143129  * This method returns false, if it encountered a problem. Currently,
216753123245Smv143129  * the only problem could be an invalid port id. OBP needs to do
216853123245Smv143129  * its own processing in that case. If this method returns false,
216953123245Smv143129  * it preserves the mid and bitmap arguments for OBP.
217053123245Smv143129  */
217153123245Smv143129 
217253123245Smv143129 ": unix-resend-mondos ( mid bitmap -- [ mid bitmap false ] | true ) "
217353123245Smv143129 
217453123245Smv143129 /*
217553123245Smv143129  * Uncomment the following line if you want to display the input arguments.
217653123245Smv143129  * This is meant for debugging.
217753123245Smv143129  * "   .\" Bitmap=\" dup u. .\" MID=\" over u. cr "
217853123245Smv143129  */
217953123245Smv143129 
218053123245Smv143129 /*
218153123245Smv143129  * If the port id is not valid (according to the Oberon and CMU ch
218253123245Smv143129  * specifications, then return false to OBP to continue further
218353123245Smv143129  * processing.
218453123245Smv143129  */
218553123245Smv143129 
218653123245Smv143129 "   over valid-io-mid? not if       ( mid bitmap ) "
218753123245Smv143129 "      false exit "
218853123245Smv143129 "   then "
218953123245Smv143129 
219053123245Smv143129 /*
219153123245Smv143129  * If the port is a CMU ch, then the 64-bit bitmap represents
219253123245Smv143129  * 2 32-bit bitmaps:
219353123245Smv143129  *	- obio interrupt bitmap (20 bits)
219453123245Smv143129  *	- pci interrupt bitmap (32 bits)
219553123245Smv143129  *
219653123245Smv143129  * - Split the bitmap into two
219753123245Smv143129  * - Compute the base addresses of the interrupt clear registers
219853123245Smv143129  *   for both pci interrupts and obio interrupts
219953123245Smv143129  * - Clear obio interrupts
220053123245Smv143129  * - Clear pci interrupts
220153123245Smv143129  */
220253123245Smv143129 
220353123245Smv143129 "   over cmuch? if                  ( mid bitmap ) "
220453123245Smv143129 "      xlsplit                      ( mid pci-bit obio-bit ) "
220553123245Smv143129 "      rot cmuch-mid>clear-int-pa   ( pci-bit obio-bit obio-pa pci-pa ) "
220653123245Smv143129 "      >r                           ( pci-bit obio-bit obio-pa ) ( r: pci-pa ) "
220753123245Smv143129 "      swap d# 20 clear-ints        ( pci-bit ) ( r: pci-pa ) "
220853123245Smv143129 "      r> swap d# 32 clear-ints     (  ) ( r: ) "
220953123245Smv143129 
221053123245Smv143129 /*
221153123245Smv143129  * If the port is an Oberon, then the 64-bit bitmap is used fully.
221253123245Smv143129  *
221353123245Smv143129  * - Compute the base address of the interrupt clear registers
221453123245Smv143129  * - Clear interrupts
221553123245Smv143129  */
221653123245Smv143129 
221753123245Smv143129 "   else                            ( mid bitmap ) "
221853123245Smv143129 "      swap pcich-mid>clear-int-pa  ( bitmap pa ) "
221953123245Smv143129 "      swap d# 64 clear-ints        (  ) "
222053123245Smv143129 "   then "
222153123245Smv143129 
222253123245Smv143129 /*
222353123245Smv143129  * Always return true from here.
222453123245Smv143129  */
222553123245Smv143129 
222653123245Smv143129 "   true                            ( true ) "
222753123245Smv143129 "; "
222853123245Smv143129 ;
222953123245Smv143129 
223053123245Smv143129 static char	install_master_interrupt_handler[] =
223153123245Smv143129 	"' unix-resend-mondos to unix-interrupt-handler";
223253123245Smv143129 static char	handler[] = "unix-interrupt-handler";
223353123245Smv143129 static char	handler_defined[] = "p\" %s\" find nip swap l! ";
223453123245Smv143129 
223553123245Smv143129 /*ARGSUSED*/
223653123245Smv143129 static int
master_interrupt_init(uint32_t portid,uint32_t xt)223753123245Smv143129 master_interrupt_init(uint32_t portid, uint32_t xt)
223853123245Smv143129 {
223953123245Smv143129 	uint_t	defined;
224053123245Smv143129 	char	buf[sizeof (handler) + sizeof (handler_defined)];
224153123245Smv143129 
224253123245Smv143129 	if (master_interrupt_inited)
224353123245Smv143129 		return (1);
224453123245Smv143129 
224553123245Smv143129 	/*
224653123245Smv143129 	 * Check if the defer word "unix-interrupt-handler" is defined.
224753123245Smv143129 	 * This must be defined for OPL systems. So, this is only a
224853123245Smv143129 	 * sanity check.
224953123245Smv143129 	 */
225053123245Smv143129 	(void) sprintf(buf, handler_defined, handler);
225153123245Smv143129 	prom_interpret(buf, (uintptr_t)&defined, 0, 0, 0, 0);
225253123245Smv143129 	if (!defined) {
225353123245Smv143129 		cmn_err(CE_WARN, "master_interrupt_init: "
225453123245Smv143129 		    "%s is not defined\n", handler);
225553123245Smv143129 		return (0);
225653123245Smv143129 	}
225753123245Smv143129 
225853123245Smv143129 	/*
225953123245Smv143129 	 * Install the generic master-interrupt handler. Note that
226053123245Smv143129 	 * this is only done one time on the first DR operation.
226153123245Smv143129 	 * This is because, for OPL, one, single generic handler
226253123245Smv143129 	 * handles all ports (Oberon and CMU channel) and all
226353123245Smv143129 	 * interrupt sources within each port.
226453123245Smv143129 	 *
226553123245Smv143129 	 * The current support is only for the Oberon and CMU-channel.
226653123245Smv143129 	 * If any others need to be supported, the handler has to be
226753123245Smv143129 	 * modified accordingly.
226853123245Smv143129 	 */
226953123245Smv143129 
227053123245Smv143129 	/*
227153123245Smv143129 	 * Define the OPL master interrupt handler
227253123245Smv143129 	 */
227353123245Smv143129 	prom_interpret(define_master_interrupt_handler, 0, 0, 0, 0, 0);
227453123245Smv143129 
227553123245Smv143129 	/*
227653123245Smv143129 	 * Take over the master interrupt handler from OBP.
227753123245Smv143129 	 */
227853123245Smv143129 	prom_interpret(install_master_interrupt_handler, 0, 0, 0, 0, 0);
227953123245Smv143129 
228053123245Smv143129 	master_interrupt_inited = 1;
228153123245Smv143129 
228253123245Smv143129 	/*
228353123245Smv143129 	 * prom_interpret() does not return a status. So, we assume
228453123245Smv143129 	 * that the calls succeeded. In reality, the calls may fail
228553123245Smv143129 	 * if there is a syntax error, etc in the strings.
228653123245Smv143129 	 */
228753123245Smv143129 
228853123245Smv143129 	return (1);
228953123245Smv143129 }
229053123245Smv143129 
229153123245Smv143129 /*
229253123245Smv143129  * Install the master-interrupt handler for a device.
229353123245Smv143129  */
229453123245Smv143129 static int
opl_master_interrupt(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)229553123245Smv143129 opl_master_interrupt(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
229653123245Smv143129 {
229753123245Smv143129 	uint32_t	portid, xt;
229853123245Smv143129 	int		board, channel, leaf;
229953123245Smv143129 	int		status;
230053123245Smv143129 
230153123245Smv143129 	/* Check the argument */
230253123245Smv143129 	if (fc_cell2int(cp->nargs) != 2)
230353123245Smv143129 		return (fc_syntax_error(cp, "nargs must be 2"));
230453123245Smv143129 
230553123245Smv143129 	if (fc_cell2int(cp->nresults) < 1)
230653123245Smv143129 		return (fc_syntax_error(cp, "nresults must be >= 1"));
230753123245Smv143129 
230853123245Smv143129 	/* Get the parameters */
230953123245Smv143129 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
231053123245Smv143129 	xt = fc_cell2uint32_t(fc_arg(cp, 1));
231153123245Smv143129 
231253123245Smv143129 	board = OPL_IO_PORTID_TO_LSB(portid);
231353123245Smv143129 	channel = OPL_PORTID_TO_CHANNEL(portid);
231453123245Smv143129 	leaf = OPL_PORTID_TO_LEAF(portid);
231553123245Smv143129 
231653123245Smv143129 	if ((board >= HWD_SBS_PER_DOMAIN) || !OPL_VALID_CHANNEL(channel) ||
231753123245Smv143129 	    (OPL_OBERON_CHANNEL(channel) && !OPL_VALID_LEAF(leaf)) ||
231853123245Smv143129 	    ((channel == OPL_CMU_CHANNEL) && (leaf != 0))) {
231953123245Smv143129 		FC_DEBUG1(1, CE_CONT, "opl_master_interrupt: invalid port %x\n",
232053123245Smv143129 		    portid);
232153123245Smv143129 		status = 0;
232253123245Smv143129 	} else {
232353123245Smv143129 		status = master_interrupt_init(portid, xt);
232453123245Smv143129 	}
232553123245Smv143129 
232653123245Smv143129 	cp->nresults = fc_int2cell(1);
232753123245Smv143129 	fc_result(cp, 0) = status;
232853123245Smv143129 
232953123245Smv143129 	return (fc_success_op(ap, rp, cp));
233053123245Smv143129 }
233153123245Smv143129 
233253123245Smv143129 /*
233325cf1a30Sjl139090  * Set the properties for a leaf node (Oberon leaf or CMU channel leaf).
233425cf1a30Sjl139090  */
233525cf1a30Sjl139090 /*ARGSUSED*/
233625cf1a30Sjl139090 static int
opl_create_leaf(dev_info_t * node,void * arg,uint_t flags)233725cf1a30Sjl139090 opl_create_leaf(dev_info_t *node, void *arg, uint_t flags)
233825cf1a30Sjl139090 {
233925cf1a30Sjl139090 	int ret;
234025cf1a30Sjl139090 
234125cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_PCI_LEAF_NODE);
234225cf1a30Sjl139090 
234325cf1a30Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
234425cf1a30Sjl139090 
234525cf1a30Sjl139090 	return (DDI_WALK_TERMINATE);
234625cf1a30Sjl139090 }
234725cf1a30Sjl139090 
234825cf1a30Sjl139090 static char *
opl_get_probe_string(opl_probe_t * probe,int channel,int leaf)234925cf1a30Sjl139090 opl_get_probe_string(opl_probe_t *probe, int channel, int leaf)
235025cf1a30Sjl139090 {
235125cf1a30Sjl139090 	char		*probe_string;
235225cf1a30Sjl139090 	int		portid;
235325cf1a30Sjl139090 
235425cf1a30Sjl139090 	probe_string = kmem_zalloc(PROBE_STR_SIZE, KM_SLEEP);
235525cf1a30Sjl139090 
235625cf1a30Sjl139090 	if (channel == OPL_CMU_CHANNEL)
235725cf1a30Sjl139090 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
235825cf1a30Sjl139090 	else
235925cf1a30Sjl139090 		portid = probe->
236025cf1a30Sjl139090 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
236125cf1a30Sjl139090 
236225cf1a30Sjl139090 	(void) sprintf(probe_string, "%x", portid);
236325cf1a30Sjl139090 
236425cf1a30Sjl139090 	return (probe_string);
236525cf1a30Sjl139090 }
236625cf1a30Sjl139090 
236725cf1a30Sjl139090 static int
opl_probe_leaf(opl_probe_t * probe)236825cf1a30Sjl139090 opl_probe_leaf(opl_probe_t *probe)
236925cf1a30Sjl139090 {
23703fe80ca4SDan Cross 	int		channel, leaf, portid, error;
237125cf1a30Sjl139090 	int		board;
237225cf1a30Sjl139090 	fco_handle_t	fco_handle, *cfg_handle;
237325cf1a30Sjl139090 	dev_info_t	*parent, *leaf_node;
237425cf1a30Sjl139090 	char		unit_address[UNIT_ADDR_SIZE];
237525cf1a30Sjl139090 	char		*probe_string;
237625cf1a30Sjl139090 	opl_board_cfg_t	*board_cfg;
237725cf1a30Sjl139090 
237825cf1a30Sjl139090 	board = probe->pr_board;
237925cf1a30Sjl139090 	channel = probe->pr_channel;
238025cf1a30Sjl139090 	leaf = probe->pr_leaf;
238125cf1a30Sjl139090 	parent = ddi_root_node();
238225cf1a30Sjl139090 	board_cfg = &opl_boards[board];
238325cf1a30Sjl139090 
238425cf1a30Sjl139090 	ASSERT(OPL_VALID_CHANNEL(channel));
238525cf1a30Sjl139090 	ASSERT(OPL_VALID_LEAF(leaf));
238625cf1a30Sjl139090 
238725cf1a30Sjl139090 	if (channel == OPL_CMU_CHANNEL) {
238825cf1a30Sjl139090 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
238925cf1a30Sjl139090 		cfg_handle = &board_cfg->cfg_cmuch_handle;
239025cf1a30Sjl139090 	} else {
239125cf1a30Sjl139090 		portid = probe->
239225cf1a30Sjl139090 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
239325cf1a30Sjl139090 		cfg_handle = &board_cfg->cfg_pcich_handle[channel][leaf];
239425cf1a30Sjl139090 	}
239525cf1a30Sjl139090 
239625cf1a30Sjl139090 	/*
239725cf1a30Sjl139090 	 * Prevent any changes to leaf_node until we have bound
239825cf1a30Sjl139090 	 * it to the correct driver.
239925cf1a30Sjl139090 	 */
24003fe80ca4SDan Cross 	ndi_devi_enter(parent);
240125cf1a30Sjl139090 
240225cf1a30Sjl139090 	/*
240325cf1a30Sjl139090 	 * Ideally, fcode would be run from the "sid_branch_create"
240425cf1a30Sjl139090 	 * callback (that is the primary purpose of that callback).
240525cf1a30Sjl139090 	 * However, the fcode interpreter was written with the
240625cf1a30Sjl139090 	 * assumption that the "new_child" was linked into the
240725cf1a30Sjl139090 	 * device tree. The callback is invoked with the devinfo node
240825cf1a30Sjl139090 	 * in the DS_PROTO state. More investigation is needed before
240925cf1a30Sjl139090 	 * we can invoke the interpreter from the callback. For now,
241025cf1a30Sjl139090 	 * we create the "new_child" in the BOUND state, invoke the
241125cf1a30Sjl139090 	 * fcode interpreter and then rebind the dip to use any
241225cf1a30Sjl139090 	 * compatible properties created by fcode.
241325cf1a30Sjl139090 	 */
241425cf1a30Sjl139090 
241525cf1a30Sjl139090 	probe->pr_parent = parent;
241625cf1a30Sjl139090 	probe->pr_create = opl_create_leaf;
241725cf1a30Sjl139090 	probe->pr_hold = 1;
241825cf1a30Sjl139090 
241925cf1a30Sjl139090 	leaf_node = opl_create_node(probe);
242025cf1a30Sjl139090 	if (leaf_node == NULL) {
242125cf1a30Sjl139090 
242225cf1a30Sjl139090 		cmn_err(CE_WARN, "IKP: create leaf (%d-%d-%d) failed",
242325cf1a30Sjl139090 		    probe->pr_board, probe->pr_channel, probe->pr_leaf);
24243fe80ca4SDan Cross 		ndi_devi_exit(parent);
242525cf1a30Sjl139090 		return (-1);
242625cf1a30Sjl139090 	}
242725cf1a30Sjl139090 
242825cf1a30Sjl139090 	/*
242925cf1a30Sjl139090 	 * The platform DR interfaces created the dip in
243025cf1a30Sjl139090 	 * bound state. Bring devinfo node down to linked
243125cf1a30Sjl139090 	 * state and hold it there until compatible
243225cf1a30Sjl139090 	 * properties are created.
243325cf1a30Sjl139090 	 */
243425cf1a30Sjl139090 	e_ddi_branch_rele(leaf_node);
243525cf1a30Sjl139090 	(void) i_ndi_unconfig_node(leaf_node, DS_LINKED, 0);
243625cf1a30Sjl139090 	ASSERT(i_ddi_node_state(leaf_node) == DS_LINKED);
243725cf1a30Sjl139090 	e_ddi_branch_hold(leaf_node);
243825cf1a30Sjl139090 
243925cf1a30Sjl139090 	mutex_enter(&DEVI(leaf_node)->devi_lock);
244025cf1a30Sjl139090 	DEVI(leaf_node)->devi_flags |= DEVI_NO_BIND;
244125cf1a30Sjl139090 	mutex_exit(&DEVI(leaf_node)->devi_lock);
244225cf1a30Sjl139090 
244325cf1a30Sjl139090 	/*
244425cf1a30Sjl139090 	 * Drop the busy-hold on parent before calling
244525cf1a30Sjl139090 	 * fcode_interpreter to prevent potential deadlocks
244625cf1a30Sjl139090 	 */
24473fe80ca4SDan Cross 	ndi_devi_exit(parent);
244825cf1a30Sjl139090 
244925cf1a30Sjl139090 	(void) sprintf(unit_address, "%x", portid);
245025cf1a30Sjl139090 
245125cf1a30Sjl139090 	/*
245225cf1a30Sjl139090 	 * Get the probe string
245325cf1a30Sjl139090 	 */
245425cf1a30Sjl139090 	probe_string = opl_get_probe_string(probe, channel, leaf);
245525cf1a30Sjl139090 
245625cf1a30Sjl139090 	/*
245725cf1a30Sjl139090 	 * The fcode pointer specified here is NULL and the fcode
245825cf1a30Sjl139090 	 * size specified here is 0. This causes the user-level
245925cf1a30Sjl139090 	 * fcode interpreter to issue a request to the fcode
246025cf1a30Sjl139090 	 * driver to get the Oberon/cmu-ch fcode.
246125cf1a30Sjl139090 	 */
246225cf1a30Sjl139090 	fco_handle = opl_fc_ops_alloc_handle(parent, leaf_node,
246325cf1a30Sjl139090 	    NULL, 0, unit_address, probe_string);
246425cf1a30Sjl139090 
246525cf1a30Sjl139090 	error = fcode_interpreter(parent, &opl_fc_do_op, fco_handle);
246625cf1a30Sjl139090 
246725cf1a30Sjl139090 	if (error != 0) {
246825cf1a30Sjl139090 		cmn_err(CE_WARN, "IKP: Unable to probe PCI leaf (%d-%d-%d)",
246925cf1a30Sjl139090 		    probe->pr_board, probe->pr_channel, probe->pr_leaf);
247025cf1a30Sjl139090 
247125cf1a30Sjl139090 		opl_fc_ops_free_handle(fco_handle);
247225cf1a30Sjl139090 
247325cf1a30Sjl139090 		if (probe_string != NULL)
247425cf1a30Sjl139090 			kmem_free(probe_string, PROBE_STR_SIZE);
247525cf1a30Sjl139090 
247625cf1a30Sjl139090 		(void) opl_destroy_node(leaf_node);
247725cf1a30Sjl139090 	} else {
247825cf1a30Sjl139090 		*cfg_handle = fco_handle;
247925cf1a30Sjl139090 
248025cf1a30Sjl139090 		if (channel == OPL_CMU_CHANNEL)
248125cf1a30Sjl139090 			board_cfg->cfg_cmuch_probe_str = probe_string;
248225cf1a30Sjl139090 		else
248325cf1a30Sjl139090 			board_cfg->cfg_pcich_probe_str[channel][leaf]
248425cf1a30Sjl139090 			    = probe_string;
248525cf1a30Sjl139090 
248625cf1a30Sjl139090 		/*
248725cf1a30Sjl139090 		 * Compatible properties (if any) have been created,
248825cf1a30Sjl139090 		 * so bind driver.
248925cf1a30Sjl139090 		 */
24903fe80ca4SDan Cross 		ndi_devi_enter(parent);
249125cf1a30Sjl139090 		ASSERT(i_ddi_node_state(leaf_node) <= DS_LINKED);
249225cf1a30Sjl139090 
249325cf1a30Sjl139090 		mutex_enter(&DEVI(leaf_node)->devi_lock);
249425cf1a30Sjl139090 		DEVI(leaf_node)->devi_flags &= ~DEVI_NO_BIND;
249525cf1a30Sjl139090 		mutex_exit(&DEVI(leaf_node)->devi_lock);
249625cf1a30Sjl139090 
24973fe80ca4SDan Cross 		ndi_devi_exit(parent);
249825cf1a30Sjl139090 
2499e98fafb9Sjl139090 		if (ndi_devi_bind_driver(leaf_node, 0) != DDI_SUCCESS) {
2500e98fafb9Sjl139090 			cmn_err(CE_WARN, "IKP: Unable to bind PCI leaf "
2501e98fafb9Sjl139090 			    "(%d-%d-%d)", probe->pr_board, probe->pr_channel,
250225cf1a30Sjl139090 			    probe->pr_leaf);
250325cf1a30Sjl139090 		}
250425cf1a30Sjl139090 	}
250525cf1a30Sjl139090 
250625cf1a30Sjl139090 	if ((error != 0) && (channel == OPL_CMU_CHANNEL))
250725cf1a30Sjl139090 		return (-1);
250825cf1a30Sjl139090 
250925cf1a30Sjl139090 	return (0);
251025cf1a30Sjl139090 }
251125cf1a30Sjl139090 
251225cf1a30Sjl139090 static void
opl_init_leaves(int myboard)251325cf1a30Sjl139090 opl_init_leaves(int myboard)
251425cf1a30Sjl139090 {
251525cf1a30Sjl139090 	dev_info_t	*parent, *node;
251625cf1a30Sjl139090 	char		*name;
25173fe80ca4SDan Cross 	int		ret;
251825cf1a30Sjl139090 	int		len, portid, board, channel, leaf;
251925cf1a30Sjl139090 	opl_board_cfg_t	*cfg;
252025cf1a30Sjl139090 
252125cf1a30Sjl139090 	parent = ddi_root_node();
252225cf1a30Sjl139090 
252325cf1a30Sjl139090 	/*
252425cf1a30Sjl139090 	 * Hold parent node busy to walk its child list
252525cf1a30Sjl139090 	 */
25263fe80ca4SDan Cross 	ndi_devi_enter(parent);
252725cf1a30Sjl139090 
2528e98fafb9Sjl139090 	for (node = ddi_get_child(parent); (node != NULL); node =
2529e98fafb9Sjl139090 	    ddi_get_next_sibling(node)) {
253025cf1a30Sjl139090 
253125cf1a30Sjl139090 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
253225cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS) {
253325cf1a30Sjl139090 			/*
253425cf1a30Sjl139090 			 * The property does not exist for this node.
253525cf1a30Sjl139090 			 */
253625cf1a30Sjl139090 			continue;
253725cf1a30Sjl139090 		}
253825cf1a30Sjl139090 
253925cf1a30Sjl139090 		if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
254025cf1a30Sjl139090 
254125cf1a30Sjl139090 			ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
254225cf1a30Sjl139090 			if (ret == DDI_PROP_SUCCESS) {
254325cf1a30Sjl139090 
254425cf1a30Sjl139090 				ret = OPL_GET_PROP(int, node, "board#",
254525cf1a30Sjl139090 				    &board, -1);
254625cf1a30Sjl139090 				if ((ret != DDI_PROP_SUCCESS) ||
254768ac2337Sjl139090 				    (board != myboard)) {
254868ac2337Sjl139090 					kmem_free(name, len);
254925cf1a30Sjl139090 					continue;
255068ac2337Sjl139090 				}
255125cf1a30Sjl139090 
255225cf1a30Sjl139090 				cfg = &opl_boards[board];
255325cf1a30Sjl139090 				channel = OPL_PORTID_TO_CHANNEL(portid);
255425cf1a30Sjl139090 				if (channel == OPL_CMU_CHANNEL) {
255525cf1a30Sjl139090 
255625cf1a30Sjl139090 					if (cfg->cfg_cmuch_handle != NULL)
255725cf1a30Sjl139090 						cfg->cfg_cmuch_leaf = node;
255825cf1a30Sjl139090 
255925cf1a30Sjl139090 				} else {
256025cf1a30Sjl139090 
256125cf1a30Sjl139090 					leaf = OPL_PORTID_TO_LEAF(portid);
2562e98fafb9Sjl139090 					if (cfg->cfg_pcich_handle[
2563e98fafb9Sjl139090 					    channel][leaf] != NULL)
2564e98fafb9Sjl139090 						cfg->cfg_pcich_leaf[
2565e98fafb9Sjl139090 						    channel][leaf] = node;
256625cf1a30Sjl139090 				}
256725cf1a30Sjl139090 			}
256825cf1a30Sjl139090 		}
256925cf1a30Sjl139090 
257025cf1a30Sjl139090 		kmem_free(name, len);
257125cf1a30Sjl139090 		if (ret != DDI_PROP_SUCCESS)
257225cf1a30Sjl139090 			break;
257325cf1a30Sjl139090 	}
257425cf1a30Sjl139090 
25753fe80ca4SDan Cross 	ndi_devi_exit(parent);
257625cf1a30Sjl139090 }
257725cf1a30Sjl139090 
257825cf1a30Sjl139090 /*
257925cf1a30Sjl139090  * Create "pci" node and hierarchy for the Oberon channels and the
258025cf1a30Sjl139090  * CMU channel.
258125cf1a30Sjl139090  */
258225cf1a30Sjl139090 /*ARGSUSED*/
258325cf1a30Sjl139090 static int
opl_probe_io(opl_probe_t * probe)258425cf1a30Sjl139090 opl_probe_io(opl_probe_t *probe)
258525cf1a30Sjl139090 {
258625cf1a30Sjl139090 
258725cf1a30Sjl139090 	int		i, j;
258825cf1a30Sjl139090 	hwd_pci_ch_t	*channels;
258925cf1a30Sjl139090 
259025cf1a30Sjl139090 	if (HWD_STATUS_OK(probe->pr_sb->sb_cmu.cmu_ch.chan_status)) {
259125cf1a30Sjl139090 
259225cf1a30Sjl139090 		probe->pr_channel = HWD_CMU_CHANNEL;
259325cf1a30Sjl139090 		probe->pr_channel_status =
259425cf1a30Sjl139090 		    probe->pr_sb->sb_cmu.cmu_ch.chan_status;
259525cf1a30Sjl139090 		probe->pr_leaf = 0;
259625cf1a30Sjl139090 		probe->pr_leaf_status = probe->pr_channel_status;
259725cf1a30Sjl139090 
259825cf1a30Sjl139090 		if (opl_probe_leaf(probe) != 0)
259925cf1a30Sjl139090 			return (-1);
260025cf1a30Sjl139090 	}
260125cf1a30Sjl139090 
260225cf1a30Sjl139090 	channels = &probe->pr_sb->sb_pci_ch[0];
260325cf1a30Sjl139090 
260425cf1a30Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
260525cf1a30Sjl139090 
260625cf1a30Sjl139090 		if (!HWD_STATUS_OK(channels[i].pci_status))
260725cf1a30Sjl139090 			continue;
260825cf1a30Sjl139090 
260925cf1a30Sjl139090 		probe->pr_channel = i;
261025cf1a30Sjl139090 		probe->pr_channel_status = channels[i].pci_status;
261125cf1a30Sjl139090 
261225cf1a30Sjl139090 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
261325cf1a30Sjl139090 
261425cf1a30Sjl139090 			probe->pr_leaf = j;
261525cf1a30Sjl139090 			probe->pr_leaf_status =
261625cf1a30Sjl139090 			    channels[i].pci_leaf[j].leaf_status;
261725cf1a30Sjl139090 
261825cf1a30Sjl139090 			if (!HWD_STATUS_OK(probe->pr_leaf_status))
261925cf1a30Sjl139090 				continue;
262025cf1a30Sjl139090 
262125cf1a30Sjl139090 			(void) opl_probe_leaf(probe);
262225cf1a30Sjl139090 		}
262325cf1a30Sjl139090 	}
262425cf1a30Sjl139090 	opl_init_leaves(probe->pr_board);
262525cf1a30Sjl139090 	return (0);
262625cf1a30Sjl139090 }
262725cf1a30Sjl139090 
262825cf1a30Sjl139090 /*
262925cf1a30Sjl139090  * Perform the probe in the following order:
263025cf1a30Sjl139090  *
263125cf1a30Sjl139090  *	processors
263225cf1a30Sjl139090  *	memory
263325cf1a30Sjl139090  *	IO
263425cf1a30Sjl139090  *
263525cf1a30Sjl139090  * Each probe function returns 0 on sucess and a non-zero value on failure.
263625cf1a30Sjl139090  * What is a failure is determined by the implementor of the probe function.
263725cf1a30Sjl139090  * For example, while probing CPUs, any error encountered during probe
263825cf1a30Sjl139090  * is considered a failure and causes the whole probe operation to fail.
263925cf1a30Sjl139090  * However, for I/O, an error encountered while probing one device
264025cf1a30Sjl139090  * should not prevent other devices from being probed. It should not cause
264125cf1a30Sjl139090  * the whole probe operation to fail.
264225cf1a30Sjl139090  */
264325cf1a30Sjl139090 int
opl_probe_sb(int board,unsigned * cpu_impl)2644e98fafb9Sjl139090 opl_probe_sb(int board, unsigned *cpu_impl)
264525cf1a30Sjl139090 {
264625cf1a30Sjl139090 	opl_probe_t	*probe;
264725cf1a30Sjl139090 	int		ret;
264825cf1a30Sjl139090 
264925cf1a30Sjl139090 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
265025cf1a30Sjl139090 		return (-1);
265125cf1a30Sjl139090 
265225cf1a30Sjl139090 	ASSERT(opl_cfg_inited != 0);
265325cf1a30Sjl139090 
265425cf1a30Sjl139090 	/*
265525cf1a30Sjl139090 	 * If the previous probe failed and left a partially configured
265625cf1a30Sjl139090 	 * board, we need to unprobe the board and start with a clean slate.
265725cf1a30Sjl139090 	 */
265825cf1a30Sjl139090 	if ((opl_boards[board].cfg_hwd != NULL) &&
265925cf1a30Sjl139090 	    (opl_unprobe_sb(board) != 0))
266025cf1a30Sjl139090 		return (-1);
266125cf1a30Sjl139090 
266225cf1a30Sjl139090 	ret = 0;
266325cf1a30Sjl139090 
266425cf1a30Sjl139090 	probe = kmem_zalloc(sizeof (opl_probe_t), KM_SLEEP);
266525cf1a30Sjl139090 	probe->pr_board = board;
266625cf1a30Sjl139090 
266725cf1a30Sjl139090 	if ((opl_probe_init(probe) != 0) ||
266825cf1a30Sjl139090 
266925cf1a30Sjl139090 	    (opl_probe_cpu_chips(probe) != 0) ||
267025cf1a30Sjl139090 
267125cf1a30Sjl139090 	    (opl_probe_memory(probe) != 0) ||
267225cf1a30Sjl139090 
267325cf1a30Sjl139090 	    (opl_probe_io(probe) != 0)) {
267425cf1a30Sjl139090 
267525cf1a30Sjl139090 		/*
267625cf1a30Sjl139090 		 * Probe failed. Perform cleanup.
267725cf1a30Sjl139090 		 */
267825cf1a30Sjl139090 		(void) opl_unprobe_sb(board);
267925cf1a30Sjl139090 		ret = -1;
268025cf1a30Sjl139090 	}
268125cf1a30Sjl139090 
2682e98fafb9Sjl139090 	*cpu_impl = probe->pr_cpu_impl;
2683e98fafb9Sjl139090 
268425cf1a30Sjl139090 	kmem_free(probe, sizeof (opl_probe_t));
268525cf1a30Sjl139090 
268625cf1a30Sjl139090 	return (ret);
268725cf1a30Sjl139090 }
268825cf1a30Sjl139090 
268925cf1a30Sjl139090 /*
269025cf1a30Sjl139090  * This unprobing also includes CMU-CH.
269125cf1a30Sjl139090  */
269225cf1a30Sjl139090 /*ARGSUSED*/
269325cf1a30Sjl139090 static int
opl_unprobe_io(int board)269425cf1a30Sjl139090 opl_unprobe_io(int board)
269525cf1a30Sjl139090 {
269625cf1a30Sjl139090 	int		i, j, ret;
269725cf1a30Sjl139090 	opl_board_cfg_t	*board_cfg;
269825cf1a30Sjl139090 	dev_info_t	**node;
269925cf1a30Sjl139090 	fco_handle_t	*hand;
270025cf1a30Sjl139090 	char		**probe_str;
270125cf1a30Sjl139090 
270225cf1a30Sjl139090 	board_cfg = &opl_boards[board];
270325cf1a30Sjl139090 
270425cf1a30Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
270525cf1a30Sjl139090 
270625cf1a30Sjl139090 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
270725cf1a30Sjl139090 
270825cf1a30Sjl139090 			node = &board_cfg->cfg_pcich_leaf[i][j];
270925cf1a30Sjl139090 			hand = &board_cfg->cfg_pcich_handle[i][j];
271025cf1a30Sjl139090 			probe_str = &board_cfg->cfg_pcich_probe_str[i][j];
271125cf1a30Sjl139090 
271225cf1a30Sjl139090 			if (*node == NULL)
271325cf1a30Sjl139090 				continue;
271425cf1a30Sjl139090 
271525cf1a30Sjl139090 			if (*hand != NULL) {
271625cf1a30Sjl139090 				opl_fc_ops_free_handle(*hand);
271725cf1a30Sjl139090 				*hand = NULL;
271825cf1a30Sjl139090 			}
271925cf1a30Sjl139090 
272025cf1a30Sjl139090 			if (*probe_str != NULL) {
272125cf1a30Sjl139090 				kmem_free(*probe_str, PROBE_STR_SIZE);
272225cf1a30Sjl139090 				*probe_str = NULL;
272325cf1a30Sjl139090 			}
272425cf1a30Sjl139090 
272525cf1a30Sjl139090 			ret = opl_destroy_node(*node);
272625cf1a30Sjl139090 			if (ret != 0) {
272725cf1a30Sjl139090 
2728e98fafb9Sjl139090 				cmn_err(CE_WARN, "IKP: destroy pci (%d-%d-%d) "
2729e98fafb9Sjl139090 				    "failed", board, i, j);
273025cf1a30Sjl139090 				return (-1);
273125cf1a30Sjl139090 			}
273225cf1a30Sjl139090 
273325cf1a30Sjl139090 			*node = NULL;
273425cf1a30Sjl139090 
273525cf1a30Sjl139090 		}
273625cf1a30Sjl139090 	}
273725cf1a30Sjl139090 
273825cf1a30Sjl139090 	node = &board_cfg->cfg_cmuch_leaf;
273925cf1a30Sjl139090 	hand = &board_cfg->cfg_cmuch_handle;
274025cf1a30Sjl139090 	probe_str = &board_cfg->cfg_cmuch_probe_str;
274125cf1a30Sjl139090 
274225cf1a30Sjl139090 	if (*node == NULL)
274325cf1a30Sjl139090 		return (0);
274425cf1a30Sjl139090 
274525cf1a30Sjl139090 	if (*hand != NULL) {
274625cf1a30Sjl139090 		opl_fc_ops_free_handle(*hand);
274725cf1a30Sjl139090 		*hand = NULL;
274825cf1a30Sjl139090 	}
274925cf1a30Sjl139090 
275025cf1a30Sjl139090 	if (*probe_str != NULL) {
275125cf1a30Sjl139090 		kmem_free(*probe_str, PROBE_STR_SIZE);
275225cf1a30Sjl139090 		*probe_str = NULL;
275325cf1a30Sjl139090 	}
275425cf1a30Sjl139090 
275525cf1a30Sjl139090 	if (opl_destroy_node(*node) != 0) {
275625cf1a30Sjl139090 
2757e98fafb9Sjl139090 		cmn_err(CE_WARN, "IKP: destroy pci (%d-%d-%d) failed", board,
2758e98fafb9Sjl139090 		    OPL_CMU_CHANNEL, 0);
275925cf1a30Sjl139090 		return (-1);
276025cf1a30Sjl139090 	}
276125cf1a30Sjl139090 
276225cf1a30Sjl139090 	*node = NULL;
276325cf1a30Sjl139090 
276425cf1a30Sjl139090 	return (0);
276525cf1a30Sjl139090 }
276625cf1a30Sjl139090 
276725cf1a30Sjl139090 /*
276825cf1a30Sjl139090  * Destroy the "pseudo-mc" node for a board.
276925cf1a30Sjl139090  */
277025cf1a30Sjl139090 static int
opl_unprobe_memory(int board)277125cf1a30Sjl139090 opl_unprobe_memory(int board)
277225cf1a30Sjl139090 {
277325cf1a30Sjl139090 	opl_board_cfg_t	*board_cfg;
277425cf1a30Sjl139090 
277525cf1a30Sjl139090 	board_cfg = &opl_boards[board];
277625cf1a30Sjl139090 
277725cf1a30Sjl139090 	if (board_cfg->cfg_pseudo_mc == NULL)
277825cf1a30Sjl139090 		return (0);
277925cf1a30Sjl139090 
278025cf1a30Sjl139090 	if (opl_destroy_node(board_cfg->cfg_pseudo_mc) != 0) {
278125cf1a30Sjl139090 
278225cf1a30Sjl139090 		cmn_err(CE_WARN, "IKP: destroy pseudo-mc (%d) failed", board);
278325cf1a30Sjl139090 		return (-1);
278425cf1a30Sjl139090 	}
278525cf1a30Sjl139090 
278625cf1a30Sjl139090 	board_cfg->cfg_pseudo_mc = NULL;
278725cf1a30Sjl139090 
278825cf1a30Sjl139090 	return (0);
278925cf1a30Sjl139090 }
279025cf1a30Sjl139090 
279125cf1a30Sjl139090 /*
279225cf1a30Sjl139090  * Destroy the "cmp" nodes for a board. This also destroys the "core"
279325cf1a30Sjl139090  * and "cpu" nodes below the "cmp" nodes.
279425cf1a30Sjl139090  */
279525cf1a30Sjl139090 static int
opl_unprobe_processors(int board)279625cf1a30Sjl139090 opl_unprobe_processors(int board)
279725cf1a30Sjl139090 {
279825cf1a30Sjl139090 	int		i;
279925cf1a30Sjl139090 	dev_info_t	**cfg_cpu_chips;
280025cf1a30Sjl139090 
280125cf1a30Sjl139090 	cfg_cpu_chips = opl_boards[board].cfg_cpu_chips;
280225cf1a30Sjl139090 
280325cf1a30Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
280425cf1a30Sjl139090 
280525cf1a30Sjl139090 		if (cfg_cpu_chips[i] == NULL)
280625cf1a30Sjl139090 			continue;
280725cf1a30Sjl139090 
280825cf1a30Sjl139090 		if (opl_destroy_node(cfg_cpu_chips[i]) != 0) {
280925cf1a30Sjl139090 
2810e98fafb9Sjl139090 			cmn_err(CE_WARN, "IKP: destroy chip (%d-%d) failed",
2811e98fafb9Sjl139090 			    board, i);
281225cf1a30Sjl139090 			return (-1);
281325cf1a30Sjl139090 		}
281425cf1a30Sjl139090 
281525cf1a30Sjl139090 		cfg_cpu_chips[i] = NULL;
281625cf1a30Sjl139090 	}
281725cf1a30Sjl139090 
281825cf1a30Sjl139090 	return (0);
281925cf1a30Sjl139090 }
282025cf1a30Sjl139090 
282125cf1a30Sjl139090 /*
282225cf1a30Sjl139090  * Perform the unprobe in the following order:
282325cf1a30Sjl139090  *
282425cf1a30Sjl139090  *	IO
282525cf1a30Sjl139090  *	memory
282625cf1a30Sjl139090  *	processors
282725cf1a30Sjl139090  */
282825cf1a30Sjl139090 int
opl_unprobe_sb(int board)282925cf1a30Sjl139090 opl_unprobe_sb(int board)
283025cf1a30Sjl139090 {
283125cf1a30Sjl139090 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
283225cf1a30Sjl139090 		return (-1);
283325cf1a30Sjl139090 
283425cf1a30Sjl139090 	ASSERT(opl_cfg_inited != 0);
283525cf1a30Sjl139090 
283625cf1a30Sjl139090 	if ((opl_unprobe_io(board) != 0) ||
283725cf1a30Sjl139090 
283825cf1a30Sjl139090 	    (opl_unprobe_memory(board) != 0) ||
283925cf1a30Sjl139090 
284025cf1a30Sjl139090 	    (opl_unprobe_processors(board) != 0))
284125cf1a30Sjl139090 
284225cf1a30Sjl139090 		return (-1);
284325cf1a30Sjl139090 
284425cf1a30Sjl139090 	if (opl_boards[board].cfg_hwd != NULL) {
284525cf1a30Sjl139090 #ifdef UCTEST
284625cf1a30Sjl139090 		size_t			size = 0xA000;
284725cf1a30Sjl139090 #endif
284825cf1a30Sjl139090 		/* Release the memory for the HWD */
284925cf1a30Sjl139090 		void *hwdp = opl_boards[board].cfg_hwd;
285025cf1a30Sjl139090 		opl_boards[board].cfg_hwd = NULL;
285125cf1a30Sjl139090 #ifdef UCTEST
285225cf1a30Sjl139090 		hwdp = (void *)((char *)hwdp - 0x1000);
285325cf1a30Sjl139090 		hat_unload(kas.a_hat, hwdp, size, HAT_UNLOAD_UNLOCK);
285425cf1a30Sjl139090 		vmem_free(heap_arena, hwdp, size);
285525cf1a30Sjl139090 #else
285625cf1a30Sjl139090 		kmem_free(hwdp, HWD_DATA_SIZE);
285725cf1a30Sjl139090 #endif
285825cf1a30Sjl139090 	}
285925cf1a30Sjl139090 	return (0);
286025cf1a30Sjl139090 }
286125cf1a30Sjl139090 
286225cf1a30Sjl139090 /*
286325cf1a30Sjl139090  * For MAC patrol support, we need to update the PA-related properties
286425cf1a30Sjl139090  * when there is a copy-rename event.  This should be called after the
286525cf1a30Sjl139090  * physical copy and rename has been done by DR, and before the MAC
286625cf1a30Sjl139090  * patrol is restarted.
286725cf1a30Sjl139090  */
286825cf1a30Sjl139090 int
oplcfg_pa_swap(int from,int to)286925cf1a30Sjl139090 oplcfg_pa_swap(int from, int to)
287025cf1a30Sjl139090 {
287125cf1a30Sjl139090 	dev_info_t *from_node = opl_boards[from].cfg_pseudo_mc;
287225cf1a30Sjl139090 	dev_info_t *to_node = opl_boards[to].cfg_pseudo_mc;
287325cf1a30Sjl139090 	opl_range_t *rangef, *ranget;
287425cf1a30Sjl139090 	int elems;
287525cf1a30Sjl139090 	int ret;
287625cf1a30Sjl139090 
287725cf1a30Sjl139090 	if ((OPL_GET_PROP_ARRAY(int, from_node, "sb-mem-ranges", rangef,
287825cf1a30Sjl139090 	    elems) != DDI_SUCCESS) || (elems != 4)) {
287925cf1a30Sjl139090 		/* XXX -- bad news */
288025cf1a30Sjl139090 		return (-1);
288125cf1a30Sjl139090 	}
288225cf1a30Sjl139090 	if ((OPL_GET_PROP_ARRAY(int, to_node, "sb-mem-ranges", ranget,
288325cf1a30Sjl139090 	    elems) != DDI_SUCCESS) || (elems != 4)) {
288425cf1a30Sjl139090 		/* XXX -- bad news */
288525cf1a30Sjl139090 		return (-1);
288625cf1a30Sjl139090 	}
288725cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, from_node, "sb-mem-ranges", (int *)ranget,
288825cf1a30Sjl139090 	    4);
288925cf1a30Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, to_node, "sb-mem-ranges", (int *)rangef,
289025cf1a30Sjl139090 	    4);
289125cf1a30Sjl139090 
289225cf1a30Sjl139090 	OPL_FREE_PROP(ranget);
289325cf1a30Sjl139090 	OPL_FREE_PROP(rangef);
289425cf1a30Sjl139090 
289525cf1a30Sjl139090 	return (0);
289625cf1a30Sjl139090 }
2897