xref: /onnv-gate/usr/src/uts/sun4u/io/opl_cfg.c (revision 3965:b92c333acf86)
11772Sjl139090 /*
21772Sjl139090  * CDDL HEADER START
31772Sjl139090  *
41772Sjl139090  * The contents of this file are subject to the terms of the
51772Sjl139090  * Common Development and Distribution License (the "License").
61772Sjl139090  * You may not use this file except in compliance with the License.
71772Sjl139090  *
81772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91772Sjl139090  * or http://www.opensolaris.org/os/licensing.
101772Sjl139090  * See the License for the specific language governing permissions
111772Sjl139090  * and limitations under the License.
121772Sjl139090  *
131772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
141772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
161772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
171772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
181772Sjl139090  *
191772Sjl139090  * CDDL HEADER END
201772Sjl139090  */
211772Sjl139090 /*
223354Sjl139090  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
231772Sjl139090  * Use is subject to license terms.
241772Sjl139090  */
251772Sjl139090 
261772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
271772Sjl139090 
281772Sjl139090 #include <sys/conf.h>
291772Sjl139090 #include <sys/kmem.h>
301772Sjl139090 #include <sys/debug.h>
311772Sjl139090 #include <sys/modctl.h>
321772Sjl139090 #include <sys/autoconf.h>
331772Sjl139090 #include <sys/hwconf.h>
341772Sjl139090 #include <sys/ddi_impldefs.h>
351772Sjl139090 #include <sys/ddi.h>
361772Sjl139090 #include <sys/sunddi.h>
371772Sjl139090 #include <sys/sunndi.h>
381772Sjl139090 #include <sys/ndi_impldefs.h>
391772Sjl139090 #include <sys/machsystm.h>
401772Sjl139090 #include <sys/fcode.h>
411772Sjl139090 #include <sys/promif.h>
421772Sjl139090 #include <sys/promimpl.h>
431772Sjl139090 #include <sys/opl_cfg.h>
441772Sjl139090 #include <sys/scfd/scfostoescf.h>
451772Sjl139090 
461772Sjl139090 static unsigned int		opl_cfg_inited;
471772Sjl139090 static opl_board_cfg_t		opl_boards[HWD_SBS_PER_DOMAIN];
481772Sjl139090 
491772Sjl139090 /*
501772Sjl139090  * Module control operations
511772Sjl139090  */
521772Sjl139090 
531772Sjl139090 extern struct mod_ops mod_miscops;
541772Sjl139090 
551772Sjl139090 static struct modlmisc modlmisc = {
561772Sjl139090 	&mod_miscops,				/* Type of module */
571772Sjl139090 	"OPL opl_cfg %I%"
581772Sjl139090 };
591772Sjl139090 
601772Sjl139090 static struct modlinkage modlinkage = {
611772Sjl139090 	MODREV_1, (void *)&modlmisc, NULL
621772Sjl139090 };
631772Sjl139090 
641772Sjl139090 static int	opl_map_in(dev_info_t *, fco_handle_t, fc_ci_t *);
651772Sjl139090 static int	opl_map_out(dev_info_t *, fco_handle_t, fc_ci_t *);
661772Sjl139090 static int	opl_register_fetch(dev_info_t *, fco_handle_t, fc_ci_t *);
671772Sjl139090 static int	opl_register_store(dev_info_t *, fco_handle_t, fc_ci_t *);
681772Sjl139090 
691772Sjl139090 static int	opl_claim_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
701772Sjl139090 static int	opl_release_memory(dev_info_t *, fco_handle_t, fc_ci_t *);
711772Sjl139090 static int	opl_vtop(dev_info_t *, fco_handle_t, fc_ci_t *);
721772Sjl139090 
731772Sjl139090 static int	opl_config_child(dev_info_t *, fco_handle_t, fc_ci_t *);
741772Sjl139090 
751772Sjl139090 static int	opl_get_fcode_size(dev_info_t *, fco_handle_t, fc_ci_t *);
761772Sjl139090 static int	opl_get_fcode(dev_info_t *, fco_handle_t, fc_ci_t *);
771772Sjl139090 
781772Sjl139090 static int	opl_map_phys(dev_info_t *, struct regspec *,  caddr_t *,
791772Sjl139090 				ddi_device_acc_attr_t *, ddi_acc_handle_t *);
801772Sjl139090 static void	opl_unmap_phys(ddi_acc_handle_t *);
811772Sjl139090 static int	opl_get_hwd_va(dev_info_t *, fco_handle_t, fc_ci_t *);
821982Smv143129 static int	opl_master_interrupt(dev_info_t *, fco_handle_t, fc_ci_t *);
831772Sjl139090 
841772Sjl139090 extern int	prom_get_fcode_size(char *);
851772Sjl139090 extern int	prom_get_fcode(char *, char *);
861772Sjl139090 
871982Smv143129 static int	master_interrupt_init(uint32_t, uint32_t);
881982Smv143129 
891772Sjl139090 #define	PROBE_STR_SIZE	64
901772Sjl139090 #define	UNIT_ADDR_SIZE	64
911772Sjl139090 
921772Sjl139090 opl_fc_ops_t	opl_fc_ops[] = {
931772Sjl139090 
941772Sjl139090 	{	FC_MAP_IN,		opl_map_in},
951772Sjl139090 	{	FC_MAP_OUT,		opl_map_out},
961772Sjl139090 	{	"rx@",			opl_register_fetch},
971772Sjl139090 	{	FC_RL_FETCH,		opl_register_fetch},
981772Sjl139090 	{	FC_RW_FETCH,		opl_register_fetch},
991772Sjl139090 	{	FC_RB_FETCH,		opl_register_fetch},
1001772Sjl139090 	{	"rx!",			opl_register_store},
1011772Sjl139090 	{	FC_RL_STORE,		opl_register_store},
1021772Sjl139090 	{	FC_RW_STORE,		opl_register_store},
1031772Sjl139090 	{	FC_RB_STORE,		opl_register_store},
1041772Sjl139090 	{	"claim-memory",		opl_claim_memory},
1051772Sjl139090 	{	"release-memory",	opl_release_memory},
1061772Sjl139090 	{	"vtop",			opl_vtop},
1071772Sjl139090 	{	FC_CONFIG_CHILD,	opl_config_child},
1081772Sjl139090 	{	FC_GET_FCODE_SIZE,	opl_get_fcode_size},
1091772Sjl139090 	{	FC_GET_FCODE,		opl_get_fcode},
1101772Sjl139090 	{	"get-hwd-va",		opl_get_hwd_va},
1111982Smv143129 	{	"master-interrupt",	opl_master_interrupt},
1121772Sjl139090 	{	NULL,			NULL}
1131772Sjl139090 
1141772Sjl139090 };
1151772Sjl139090 
1161772Sjl139090 extern caddr_t	efcode_vaddr;
1171772Sjl139090 extern int	efcode_size;
1181772Sjl139090 
1191772Sjl139090 #ifdef DEBUG
1201772Sjl139090 #define	HWDDUMP_OFFSETS		1
1211772Sjl139090 #define	HWDDUMP_ALL_STATUS	2
1221772Sjl139090 #define	HWDDUMP_CHUNKS		3
1231772Sjl139090 #define	HWDDUMP_SBP		4
1241772Sjl139090 
1251772Sjl139090 int		hwddump_flags = HWDDUMP_SBP | HWDDUMP_CHUNKS;
1261772Sjl139090 #endif
1271772Sjl139090 
1281982Smv143129 static int	master_interrupt_inited = 0;
1291982Smv143129 
1301772Sjl139090 int
1311772Sjl139090 _init()
1321772Sjl139090 {
1331772Sjl139090 	int	err = 0;
1341772Sjl139090 
1351772Sjl139090 	/*
1361772Sjl139090 	 * Create a resource map for the contiguous memory allocated
1371772Sjl139090 	 * at start-of-day in startup.c
1381772Sjl139090 	 */
1391772Sjl139090 	err = ndi_ra_map_setup(ddi_root_node(), "opl-fcodemem");
1401772Sjl139090 	if (err == NDI_FAILURE) {
1411772Sjl139090 		cmn_err(CE_WARN, "Cannot setup resource map opl-fcodemem\n");
1421772Sjl139090 		return (1);
1431772Sjl139090 	}
1441772Sjl139090 
1451772Sjl139090 	/*
1461772Sjl139090 	 * Put the allocated memory into the pool.
1471772Sjl139090 	 */
1481772Sjl139090 	(void) ndi_ra_free(ddi_root_node(), (uint64_t)efcode_vaddr,
1491772Sjl139090 		(uint64_t)efcode_size, "opl-fcodemem", 0);
1501772Sjl139090 
1511772Sjl139090 	if ((err = mod_install(&modlinkage)) != 0) {
1521772Sjl139090 		cmn_err(CE_WARN, "opl_cfg failed to load, error=%d", err);
1531772Sjl139090 		(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
1541772Sjl139090 	}
1551772Sjl139090 
1561772Sjl139090 	return (err);
1571772Sjl139090 }
1581772Sjl139090 
1591772Sjl139090 int
1601772Sjl139090 _fini(void)
1611772Sjl139090 {
1621772Sjl139090 	int ret;
1631772Sjl139090 
1641772Sjl139090 	ret = (mod_remove(&modlinkage));
1651772Sjl139090 	if (ret != 0)
1661772Sjl139090 		return (ret);
1671772Sjl139090 
1681772Sjl139090 	(void) ndi_ra_map_destroy(ddi_root_node(), "opl-fcodemem");
1691772Sjl139090 
1701772Sjl139090 	return (ret);
1711772Sjl139090 }
1721772Sjl139090 
1731772Sjl139090 int
1741772Sjl139090 _info(modinfop)
1751772Sjl139090 struct modinfo *modinfop;
1761772Sjl139090 {
1771772Sjl139090 	return (mod_info(&modlinkage, modinfop));
1781772Sjl139090 }
1791772Sjl139090 
1801772Sjl139090 #ifdef DEBUG
1811772Sjl139090 static void
1821772Sjl139090 opl_dump_hwd(opl_probe_t *probe)
1831772Sjl139090 {
1841772Sjl139090 	hwd_header_t		*hdrp;
1851772Sjl139090 	hwd_sb_status_t		*statp;
1861772Sjl139090 	hwd_domain_info_t	*dinfop;
1871772Sjl139090 	hwd_sb_t		*sbp;
1881772Sjl139090 	hwd_cpu_chip_t		*chips;
1891772Sjl139090 	hwd_pci_ch_t		*channels;
1901772Sjl139090 	int			board, i, status;
1911772Sjl139090 
1921772Sjl139090 	board = probe->pr_board;
1931772Sjl139090 
1941772Sjl139090 	hdrp = probe->pr_hdr;
1951772Sjl139090 	statp = probe->pr_sb_status;
1961772Sjl139090 	dinfop = probe->pr_dinfo;
1971772Sjl139090 	sbp = probe->pr_sb;
1981772Sjl139090 
1991772Sjl139090 	printf("HWD: board %d\n", board);
2001772Sjl139090 	printf("HWD:magic = 0x%x\n", hdrp->hdr_magic);
2011772Sjl139090 	printf("HWD:version = 0x%x.%x\n", hdrp->hdr_version.major,
2021772Sjl139090 	    hdrp->hdr_version.minor);
2031772Sjl139090 
2041772Sjl139090 	if (hwddump_flags & HWDDUMP_OFFSETS) {
2051772Sjl139090 		printf("HWD:status offset = 0x%x\n",
2061772Sjl139090 		    hdrp->hdr_sb_status_offset);
2071772Sjl139090 		printf("HWD:domain offset = 0x%x\n",
2081772Sjl139090 		    hdrp->hdr_domain_info_offset);
2091772Sjl139090 		printf("HWD:board offset = 0x%x\n", hdrp->hdr_sb_info_offset);
2101772Sjl139090 	}
2111772Sjl139090 
2121772Sjl139090 	if (hwddump_flags & HWDDUMP_SBP)
2131772Sjl139090 		printf("HWD:sb_t ptr = 0x%p\n", (void *)probe->pr_sb);
2141772Sjl139090 
2151772Sjl139090 	if (hwddump_flags & HWDDUMP_ALL_STATUS) {
2161772Sjl139090 		int bd;
2171772Sjl139090 		printf("HWD:board status =");
2181772Sjl139090 		for (bd = 0; bd < HWD_SBS_PER_DOMAIN; bd++)
2191772Sjl139090 			printf("%x ", statp->sb_status[bd]);
2201772Sjl139090 		printf("\n");
2211772Sjl139090 	} else {
2221772Sjl139090 		printf("HWD:board status = %d\n", statp->sb_status[board]);
2231772Sjl139090 	}
2241772Sjl139090 
2251772Sjl139090 	printf("HWD:banner name = %s\n", dinfop->dinf_banner_name);
2261772Sjl139090 	printf("HWD:platform = %s\n", dinfop->dinf_platform_token);
2271772Sjl139090 
2281772Sjl139090 	printf("HWD:chip status:\n");
2291772Sjl139090 	chips = &sbp->sb_cmu.cmu_cpu_chips[0];
2301772Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
2311772Sjl139090 
2321772Sjl139090 		status = chips[i].chip_status;
2331772Sjl139090 		printf("chip[%d] = ", i);
2341772Sjl139090 		if (HWD_STATUS_NONE(status))
2351772Sjl139090 			printf("none");
2361772Sjl139090 		else if (HWD_STATUS_FAILED(status))
2371772Sjl139090 			printf("fail");
2381772Sjl139090 		else if (HWD_STATUS_OK(status))
2391772Sjl139090 			printf("ok");
2401772Sjl139090 		printf("\n");
2411772Sjl139090 	}
2421772Sjl139090 
2431772Sjl139090 	if (hwddump_flags & HWDDUMP_CHUNKS) {
2441772Sjl139090 		int chunk;
2451772Sjl139090 		hwd_memory_t *mem = &sbp->sb_cmu.cmu_memory;
2461772Sjl139090 		printf("HWD:chunks:\n");
2471772Sjl139090 		for (chunk = 0; chunk < HWD_MAX_MEM_CHUNKS; chunk++)
2481772Sjl139090 			printf("\t%d 0x%lx 0x%lx\n", chunk,
2491772Sjl139090 			    mem->mem_chunks[chunk].chnk_start_address,
2501772Sjl139090 			    mem->mem_chunks[chunk].chnk_size);
2511772Sjl139090 	}
2521772Sjl139090 
2531772Sjl139090 	printf("HWD:channel status:\n");
2541772Sjl139090 	channels = &sbp->sb_pci_ch[0];
2551772Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
2561772Sjl139090 
2571772Sjl139090 		status = channels[i].pci_status;
2581772Sjl139090 		printf("channels[%d] = ", i);
2591772Sjl139090 		if (HWD_STATUS_NONE(status))
2601772Sjl139090 			printf("none");
2611772Sjl139090 		else if (HWD_STATUS_FAILED(status))
2621772Sjl139090 			printf("fail");
2631772Sjl139090 		else if (HWD_STATUS_OK(status))
2641772Sjl139090 			printf("ok");
2651772Sjl139090 		printf("\n");
2661772Sjl139090 	}
2671772Sjl139090 	printf("channels[%d] = ", i);
2681772Sjl139090 	status = sbp->sb_cmu.cmu_ch.chan_status;
2691772Sjl139090 	if (HWD_STATUS_NONE(status))
2701772Sjl139090 		printf("none");
2711772Sjl139090 	else if (HWD_STATUS_FAILED(status))
2721772Sjl139090 		printf("fail");
2731772Sjl139090 	else if (HWD_STATUS_OK(status))
2741772Sjl139090 		printf("ok");
2751772Sjl139090 	printf("\n");
2761772Sjl139090 }
2771772Sjl139090 #endif /* DEBUG */
2781772Sjl139090 
2791772Sjl139090 #ifdef UCTEST
2801772Sjl139090 	/*
2811772Sjl139090 	 * For SesamI debugging, just map the SRAM directly to a kernel
2821772Sjl139090 	 * VA and read it out from there
2831772Sjl139090 	 */
2841772Sjl139090 
2851772Sjl139090 #include <sys/vmem.h>
2861772Sjl139090 #include <vm/seg_kmem.h>
2871772Sjl139090 
2881772Sjl139090 /*
2891772Sjl139090  * 0x4081F1323000LL is the HWD base address for LSB 0. But we need to map
2901772Sjl139090  * at page boundaries. So, we use a base address of 0x4081F1322000LL.
2911772Sjl139090  * Note that this has to match the HWD base pa set in .sesami-common-defs.
2921772Sjl139090  *
2931772Sjl139090  * The size specified for the HWD in the SCF spec is 36K. But since
2941772Sjl139090  * we adjusted the base address by 4K, we need to use 40K for the
2951772Sjl139090  * mapping size to cover the HWD. And 40K is also a multiple of the
2961772Sjl139090  * base page size.
2971772Sjl139090  */
2981772Sjl139090 #define	OPL_HWD_BASE(lsb)       \
2991772Sjl139090 (0x4081F1322000LL | (((uint64_t)(lsb)) << 40))
3001772Sjl139090 
3011772Sjl139090 	void    *opl_hwd_vaddr;
3021772Sjl139090 #endif /* UCTEST */
3031772Sjl139090 
3041772Sjl139090 /*
3051772Sjl139090  * Get the hardware descriptor from SCF.
3061772Sjl139090  */
3071772Sjl139090 
3081772Sjl139090 /*ARGSUSED*/
3091772Sjl139090 int
3101772Sjl139090 opl_read_hwd(int board, hwd_header_t **hdrp, hwd_sb_status_t **statp,
3111772Sjl139090 	hwd_domain_info_t **dinfop, hwd_sb_t **sbp)
3121772Sjl139090 {
3131772Sjl139090 	static int (*getinfop)(uint32_t, uint8_t, uint32_t, uint32_t *,
3141772Sjl139090 	    void *) = NULL;
3151772Sjl139090 	void *hwdp;
3161772Sjl139090 
3171772Sjl139090 	uint32_t key = KEY_ESCF;	/* required value */
3181772Sjl139090 	uint8_t  type = 0x40;		/* SUB_OS_RECEIVE_HWD */
3191772Sjl139090 	uint32_t transid = board;
3201772Sjl139090 	uint32_t datasize = HWD_DATA_SIZE;
3211772Sjl139090 
3221772Sjl139090 	hwd_header_t		*hd;
3231772Sjl139090 	hwd_sb_status_t		*st;
3241772Sjl139090 	hwd_domain_info_t	*di;
3251772Sjl139090 	hwd_sb_t		*sb;
3261772Sjl139090 
3271772Sjl139090 	int	ret;
3281772Sjl139090 
3291772Sjl139090 	if (opl_boards[board].cfg_hwd == NULL) {
3301772Sjl139090 #ifdef UCTEST
3311772Sjl139090 		/*
3321772Sjl139090 		 * Just map the HWD in SRAM to a kernel VA
3331772Sjl139090 		 */
3341772Sjl139090 
3351772Sjl139090 		size_t			size;
3361772Sjl139090 		pfn_t			pfn;
3371772Sjl139090 
3381772Sjl139090 		size = 0xA000;
3391772Sjl139090 
3401772Sjl139090 		opl_hwd_vaddr = vmem_alloc(heap_arena, size, VM_SLEEP);
3411772Sjl139090 		if (opl_hwd_vaddr == NULL) {
3421772Sjl139090 			cmn_err(CE_NOTE, "No space for HWD");
3431772Sjl139090 			return (-1);
3441772Sjl139090 		}
3451772Sjl139090 
3461772Sjl139090 		pfn = btop(OPL_HWD_BASE(board));
3471772Sjl139090 		hat_devload(kas.a_hat, opl_hwd_vaddr, size, pfn, PROT_READ,
3481772Sjl139090 		    HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK);
3491772Sjl139090 
3501772Sjl139090 		hwdp = (void *)((char *)opl_hwd_vaddr + 0x1000);
3511772Sjl139090 		opl_boards[board].cfg_hwd = hwdp;
3521772Sjl139090 		ret = 0;
3531772Sjl139090 #else
3541772Sjl139090 
3551772Sjl139090 		/* find the scf_service_getinfo() function */
3561772Sjl139090 		if (getinfop == NULL)
3571772Sjl139090 			getinfop = (int (*)(uint32_t, uint8_t, uint32_t,
3581772Sjl139090 			    uint32_t *,
3591772Sjl139090 			    void *))modgetsymvalue("scf_service_getinfo", 0);
3601772Sjl139090 
3611772Sjl139090 		if (getinfop == NULL)
3621772Sjl139090 			return (-1);
3631772Sjl139090 
3641772Sjl139090 		/* allocate memory to receive the data */
3651772Sjl139090 		hwdp = kmem_alloc(HWD_DATA_SIZE, KM_SLEEP);
3661772Sjl139090 
3671772Sjl139090 		/* get the HWD */
3681772Sjl139090 		ret = (*getinfop)(key, type, transid, &datasize, hwdp);
3691772Sjl139090 		if (ret == 0)
3701772Sjl139090 			opl_boards[board].cfg_hwd = hwdp;
3711772Sjl139090 		else
3721772Sjl139090 			kmem_free(hwdp, HWD_DATA_SIZE);
3731772Sjl139090 #endif
3741772Sjl139090 	} else {
3751772Sjl139090 		hwdp = opl_boards[board].cfg_hwd;
3761772Sjl139090 		ret = 0;
3771772Sjl139090 	}
3781772Sjl139090 
3791772Sjl139090 	/* copy the data to the destination */
3801772Sjl139090 	if (ret == 0) {
3811772Sjl139090 		hd = (hwd_header_t *)hwdp;
3821772Sjl139090 		st = (hwd_sb_status_t *)
3831772Sjl139090 		    ((char *)hwdp + hd->hdr_sb_status_offset);
3841772Sjl139090 		di = (hwd_domain_info_t *)
3851772Sjl139090 		    ((char *)hwdp + hd->hdr_domain_info_offset);
3861772Sjl139090 		sb = (hwd_sb_t *)
3871772Sjl139090 		    ((char *)hwdp + hd->hdr_sb_info_offset);
3881772Sjl139090 		if (hdrp != NULL)
3891772Sjl139090 			*hdrp = hd;
3901772Sjl139090 		if (statp != NULL)
3911772Sjl139090 			*statp = st;
3921772Sjl139090 		if (dinfop != NULL)
3931772Sjl139090 			*dinfop = di;
3941772Sjl139090 		if (sbp != NULL)
3951772Sjl139090 			*sbp = sb;
3961772Sjl139090 	}
3971772Sjl139090 
3981772Sjl139090 	return (ret);
3991772Sjl139090 }
4001772Sjl139090 
4011772Sjl139090 /*
4021772Sjl139090  * The opl_probe_t probe structure is used to pass all sorts of parameters
4031772Sjl139090  * to callback functions during probing. It also contains a snapshot of
4041772Sjl139090  * the hardware descriptor that is taken at the beginning of a probe.
4051772Sjl139090  */
4061772Sjl139090 static int
4071772Sjl139090 opl_probe_init(opl_probe_t *probe)
4081772Sjl139090 {
4091772Sjl139090 	hwd_header_t		**hdrp;
4101772Sjl139090 	hwd_sb_status_t		**statp;
4111772Sjl139090 	hwd_domain_info_t	**dinfop;
4121772Sjl139090 	hwd_sb_t		**sbp;
4131772Sjl139090 	int			board, ret;
4141772Sjl139090 
4151772Sjl139090 	board = probe->pr_board;
4161772Sjl139090 
4171772Sjl139090 	hdrp = &probe->pr_hdr;
4181772Sjl139090 	statp = &probe->pr_sb_status;
4191772Sjl139090 	dinfop = &probe->pr_dinfo;
4201772Sjl139090 	sbp = &probe->pr_sb;
4211772Sjl139090 
4221772Sjl139090 	/*
4231772Sjl139090 	 * Read the hardware descriptor.
4241772Sjl139090 	 */
4251772Sjl139090 	ret = opl_read_hwd(board, hdrp, statp, dinfop, sbp);
4261772Sjl139090 	if (ret != 0) {
4271772Sjl139090 
4281772Sjl139090 		cmn_err(CE_WARN, "IKP: failed to read HWD header");
4291772Sjl139090 		return (-1);
4301772Sjl139090 	}
4311772Sjl139090 
4321772Sjl139090 #ifdef DEBUG
4331772Sjl139090 	opl_dump_hwd(probe);
4341772Sjl139090 #endif
4351772Sjl139090 	return (0);
4361772Sjl139090 }
4371772Sjl139090 
4381772Sjl139090 /*
4391772Sjl139090  * This function is used to obtain pointers to relevant device nodes
4401772Sjl139090  * which are created by Solaris at boot time.
4411772Sjl139090  *
4421772Sjl139090  * This function walks the child nodes of a given node, extracts
4431772Sjl139090  * the "name" property, if it exists, and passes the node to a
4441772Sjl139090  * callback init function. The callback determines if this node is
4451772Sjl139090  * interesting or not. If it is, then a pointer to the node is
4461772Sjl139090  * stored away by the callback for use during unprobe.
4471772Sjl139090  *
4481772Sjl139090  * The DDI get property function allocates storage for the name
4491772Sjl139090  * property. That needs to be freed within this function.
4501772Sjl139090  */
4511772Sjl139090 static int
4521772Sjl139090 opl_init_nodes(dev_info_t *parent, opl_init_func_t init)
4531772Sjl139090 {
4541772Sjl139090 	dev_info_t	*node;
4551772Sjl139090 	char		*name;
4561772Sjl139090 	int 		circ, ret;
4571772Sjl139090 	int		len;
4581772Sjl139090 
4591772Sjl139090 	ASSERT(parent != NULL);
4601772Sjl139090 
4611772Sjl139090 	/*
4621772Sjl139090 	 * Hold parent node busy to walk its child list
4631772Sjl139090 	 */
4641772Sjl139090 	ndi_devi_enter(parent, &circ);
4651772Sjl139090 	node = ddi_get_child(parent);
4661772Sjl139090 
4671772Sjl139090 	while (node != NULL) {
4681772Sjl139090 
4691772Sjl139090 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
4701772Sjl139090 		if (ret != DDI_PROP_SUCCESS) {
4711772Sjl139090 			/*
4721772Sjl139090 			 * The property does not exist for this node.
4731772Sjl139090 			 */
4741772Sjl139090 			node = ddi_get_next_sibling(node);
4751772Sjl139090 			continue;
4761772Sjl139090 		}
4771772Sjl139090 
4781772Sjl139090 		ret = init(node, name, len);
4791772Sjl139090 		kmem_free(name, len);
4801772Sjl139090 		if (ret != 0) {
4811772Sjl139090 
4821772Sjl139090 			ndi_devi_exit(parent, circ);
4831772Sjl139090 			return (-1);
4841772Sjl139090 		}
4851772Sjl139090 
4861772Sjl139090 		node = ddi_get_next_sibling(node);
4871772Sjl139090 	}
4881772Sjl139090 
4891772Sjl139090 	ndi_devi_exit(parent, circ);
4901772Sjl139090 
4911772Sjl139090 	return (0);
4921772Sjl139090 }
4931772Sjl139090 
4941772Sjl139090 /*
4951772Sjl139090  * This init function finds all the interesting nodes under the
4961772Sjl139090  * root node and stores pointers to them. The following nodes
4971772Sjl139090  * are considered interesting by this implementation:
4981772Sjl139090  *
4991772Sjl139090  *	"cmp"
5001772Sjl139090  *		These are nodes that represent processor chips.
5011772Sjl139090  *
5021772Sjl139090  *	"pci"
5031772Sjl139090  *		These are nodes that represent PCI leaves.
5041772Sjl139090  *
5051772Sjl139090  *	"pseudo-mc"
5061772Sjl139090  *		These are nodes that contain memory information.
5071772Sjl139090  */
5081772Sjl139090 static int
5091772Sjl139090 opl_init_root_nodes(dev_info_t *node, char *name, int len)
5101772Sjl139090 {
5111772Sjl139090 	int		portid, board, chip, channel, leaf;
5121772Sjl139090 	int		ret;
5131772Sjl139090 
5141772Sjl139090 	if (strncmp(name, OPL_CPU_CHIP_NODE, len) == 0) {
5151772Sjl139090 
5161772Sjl139090 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
5171772Sjl139090 		if (ret != DDI_PROP_SUCCESS)
5181772Sjl139090 			return (-1);
5191772Sjl139090 
5201772Sjl139090 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
5211772Sjl139090 		if (ret != DDI_PROP_SUCCESS)
5221772Sjl139090 			return (-1);
5231772Sjl139090 
5241772Sjl139090 		chip = OPL_CPU_CHIP(portid);
5251772Sjl139090 		opl_boards[board].cfg_cpu_chips[chip] = node;
5261772Sjl139090 
5271772Sjl139090 	} else if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
5281772Sjl139090 
5291772Sjl139090 		ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
5301772Sjl139090 		if (ret != DDI_PROP_SUCCESS)
5311772Sjl139090 			return (-1);
5321772Sjl139090 
5331772Sjl139090 		board = OPL_IO_PORTID_TO_LSB(portid);
5341772Sjl139090 		channel = OPL_PORTID_TO_CHANNEL(portid);
5351772Sjl139090 
5361772Sjl139090 		if (channel == OPL_CMU_CHANNEL) {
5371772Sjl139090 
5381772Sjl139090 			opl_boards[board].cfg_cmuch_leaf = node;
5391772Sjl139090 
5401772Sjl139090 		} else {
5411772Sjl139090 
5421772Sjl139090 			leaf = OPL_PORTID_TO_LEAF(portid);
5431772Sjl139090 			opl_boards[board].cfg_pcich_leaf[channel][leaf] = node;
5441772Sjl139090 		}
5451772Sjl139090 	} else if (strncmp(name, OPL_PSEUDO_MC_NODE, len) == 0) {
5461772Sjl139090 
5471772Sjl139090 		ret = OPL_GET_PROP(int, node, "board#", &board, -1);
5481772Sjl139090 		if (ret != DDI_PROP_SUCCESS)
5491772Sjl139090 			return (-1);
5501772Sjl139090 
5511772Sjl139090 		ASSERT((board >= 0) && (board < HWD_SBS_PER_DOMAIN));
5521772Sjl139090 
5531772Sjl139090 		opl_boards[board].cfg_pseudo_mc = node;
5541772Sjl139090 	}
5551772Sjl139090 
5561772Sjl139090 	return (0);
5571772Sjl139090 }
5581772Sjl139090 
5591772Sjl139090 /*
5601772Sjl139090  * This function initializes the OPL IKP feature. Currently, all it does
5611772Sjl139090  * is find the interesting nodes that Solaris has created at boot time
5621772Sjl139090  * for boards present at boot time and store pointers to them. This
5631772Sjl139090  * is useful if those boards are unprobed by DR.
5641772Sjl139090  */
5651772Sjl139090 int
5661772Sjl139090 opl_init_cfg()
5671772Sjl139090 {
5681772Sjl139090 	dev_info_t	*root;
5691772Sjl139090 
5701772Sjl139090 	if (opl_cfg_inited == 0) {
5711772Sjl139090 
5721772Sjl139090 		root = ddi_root_node();
5731772Sjl139090 		if ((opl_init_nodes(root, opl_init_root_nodes) != 0)) {
5741772Sjl139090 			cmn_err(CE_WARN, "IKP: init failed");
5751772Sjl139090 			return (1);
5761772Sjl139090 		}
5771772Sjl139090 
5781772Sjl139090 		opl_cfg_inited = 1;
5791772Sjl139090 	}
5801772Sjl139090 
5811772Sjl139090 	return (0);
5821772Sjl139090 }
5831772Sjl139090 
5841772Sjl139090 /*
5851772Sjl139090  * When DR is initialized, we walk the device tree and acquire a hold on
5861772Sjl139090  * all the nodes that are interesting to IKP. This is so that the corresponding
5871772Sjl139090  * branches cannot be deleted.
5881772Sjl139090  *
5891772Sjl139090  * The following function informs the walk about which nodes are interesting
5901772Sjl139090  * so that it can hold the corresponding branches.
5911772Sjl139090  */
5921772Sjl139090 static int
5931772Sjl139090 opl_hold_node(char *name)
5941772Sjl139090 {
5951772Sjl139090 	/*
5961772Sjl139090 	 * We only need to hold/release the following nodes which
5971772Sjl139090 	 * represent separate branches that must be managed.
5981772Sjl139090 	 */
5991772Sjl139090 	return ((strcmp(name, OPL_CPU_CHIP_NODE) == 0) ||
6001772Sjl139090 		(strcmp(name, OPL_PSEUDO_MC_NODE) == 0) ||
6011772Sjl139090 		(strcmp(name, OPL_PCI_LEAF_NODE) == 0));
6021772Sjl139090 }
6031772Sjl139090 
6041772Sjl139090 static int
6051772Sjl139090 opl_hold_rele_devtree(dev_info_t *rdip, void *arg)
6061772Sjl139090 {
6071772Sjl139090 
6081772Sjl139090 	int	*holdp = (int *)arg;
6091772Sjl139090 	char	*name = ddi_node_name(rdip);
6101772Sjl139090 
6111772Sjl139090 	/*
6121772Sjl139090 	 * We only need to hold/release the following nodes which
6131772Sjl139090 	 * represent separate branches that must be managed.
6141772Sjl139090 	 */
6151772Sjl139090 	if (opl_hold_node(name) == 0) {
6161772Sjl139090 		/* Not of interest to us */
6171772Sjl139090 		return (DDI_WALK_PRUNECHILD);
6181772Sjl139090 	}
6191772Sjl139090 	if (*holdp) {
6201772Sjl139090 		ASSERT(!e_ddi_branch_held(rdip));
6211772Sjl139090 		e_ddi_branch_hold(rdip);
6221772Sjl139090 	} else {
6231772Sjl139090 		ASSERT(e_ddi_branch_held(rdip));
6241772Sjl139090 		e_ddi_branch_rele(rdip);
6251772Sjl139090 	}
6261772Sjl139090 
6271772Sjl139090 	return (DDI_WALK_PRUNECHILD);
6281772Sjl139090 }
6291772Sjl139090 
6301772Sjl139090 void
6311772Sjl139090 opl_hold_devtree()
6321772Sjl139090 {
6331772Sjl139090 	dev_info_t *dip;
6341772Sjl139090 	int circ;
6351772Sjl139090 	int hold = 1;
6361772Sjl139090 
6371772Sjl139090 	dip = ddi_root_node();
6381772Sjl139090 	ndi_devi_enter(dip, &circ);
6391772Sjl139090 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
6401772Sjl139090 	ndi_devi_exit(dip, circ);
6411772Sjl139090 }
6421772Sjl139090 
6431772Sjl139090 void
6441772Sjl139090 opl_release_devtree()
6451772Sjl139090 {
6461772Sjl139090 	dev_info_t *dip;
6471772Sjl139090 	int circ;
6481772Sjl139090 	int hold = 0;
6491772Sjl139090 
6501772Sjl139090 	dip = ddi_root_node();
6511772Sjl139090 	ndi_devi_enter(dip, &circ);
6521772Sjl139090 	ddi_walk_devs(ddi_get_child(dip), opl_hold_rele_devtree, &hold);
6531772Sjl139090 	ndi_devi_exit(dip, circ);
6541772Sjl139090 }
6551772Sjl139090 
6561772Sjl139090 /*
6571772Sjl139090  * This is a helper function that allows opl_create_node() to return a
6581772Sjl139090  * pointer to a newly created node to its caller.
6591772Sjl139090  */
6601772Sjl139090 /*ARGSUSED*/
6611772Sjl139090 static void
6621772Sjl139090 opl_set_node(dev_info_t *node, void *arg, uint_t flags)
6631772Sjl139090 {
6641772Sjl139090 	opl_probe_t	*probe;
6651772Sjl139090 
6661772Sjl139090 	probe = arg;
6671772Sjl139090 	probe->pr_node = node;
6681772Sjl139090 }
6691772Sjl139090 
6701772Sjl139090 /*
6711772Sjl139090  * Function to create a node in the device tree under a specified parent.
6721772Sjl139090  *
6731772Sjl139090  * e_ddi_branch_create() allows the creation of a whole branch with a
6741772Sjl139090  * single call of the function. However, we only use it to create one node
6751772Sjl139090  * at a time in the case of non-I/O device nodes. In other words, we
6761772Sjl139090  * create branches by repeatedly using this function. This makes the
6771772Sjl139090  * code more readable.
6781772Sjl139090  *
6791772Sjl139090  * The branch descriptor passed to e_ddi_branch_create() takes two
6801772Sjl139090  * callbacks. The create() callback is used to set the properties of a
6811772Sjl139090  * newly created node. The other callback is used to return a pointer
6821772Sjl139090  * to the newly created node. The create() callback is passed by the
6831772Sjl139090  * caller of this function based on the kind of node he wishes to
6841772Sjl139090  * create.
6851772Sjl139090  *
6861772Sjl139090  * e_ddi_branch_create() returns with the newly created node held. We
6871772Sjl139090  * only need to hold the top nodes of the branches we create. We release
6881772Sjl139090  * the hold for the others. E.g., the "cmp" node needs to be held. Since
6891772Sjl139090  * we hold the "cmp" node, there is no need to hold the "core" and "cpu"
6901772Sjl139090  * nodes below it.
6911772Sjl139090  */
6921772Sjl139090 static dev_info_t *
6931772Sjl139090 opl_create_node(opl_probe_t *probe)
6941772Sjl139090 {
6951772Sjl139090 	devi_branch_t	branch;
6961772Sjl139090 
6971772Sjl139090 	probe->pr_node = NULL;
6981772Sjl139090 
6991772Sjl139090 	branch.arg = probe;
7001772Sjl139090 	branch.type = DEVI_BRANCH_SID;
7011772Sjl139090 	branch.create.sid_branch_create = probe->pr_create;
7021772Sjl139090 	branch.devi_branch_callback = opl_set_node;
7031772Sjl139090 
7041772Sjl139090 	if (e_ddi_branch_create(probe->pr_parent, &branch, NULL, 0) != 0)
7051772Sjl139090 		return (NULL);
7061772Sjl139090 
7071772Sjl139090 	ASSERT(probe->pr_node != NULL);
7081772Sjl139090 
7091772Sjl139090 	if (probe->pr_hold == 0)
7101772Sjl139090 		e_ddi_branch_rele(probe->pr_node);
7111772Sjl139090 
7121772Sjl139090 	return (probe->pr_node);
7131772Sjl139090 }
7141772Sjl139090 
7151772Sjl139090 /*
7161772Sjl139090  * Function to tear down a whole branch rooted at the specified node.
7171772Sjl139090  *
7181772Sjl139090  * Although we create each node of a branch individually, we destroy
7191772Sjl139090  * a whole branch in one call. This is more efficient.
7201772Sjl139090  */
7211772Sjl139090 static int
7221772Sjl139090 opl_destroy_node(dev_info_t *node)
7231772Sjl139090 {
7243354Sjl139090 	if (e_ddi_branch_destroy(node, NULL, 0) != 0) {
7253354Sjl139090 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7263354Sjl139090 		(void) ddi_pathname(node, path);
7273354Sjl139090 		cmn_err(CE_WARN, "OPL node removal failed: %s (%p)",
7283354Sjl139090 			    path, (void *)node);
7293354Sjl139090 		kmem_free(path, MAXPATHLEN);
7301772Sjl139090 		return (-1);
7313354Sjl139090 	}
7321772Sjl139090 
7331772Sjl139090 	return (0);
7341772Sjl139090 }
7351772Sjl139090 
7361772Sjl139090 /*
7371772Sjl139090  * Set the properties for a "cpu" node.
7381772Sjl139090  */
7391772Sjl139090 /*ARGSUSED*/
7401772Sjl139090 static int
7411772Sjl139090 opl_create_cpu(dev_info_t *node, void *arg, uint_t flags)
7421772Sjl139090 {
7431772Sjl139090 	opl_probe_t	*probe;
7441772Sjl139090 	hwd_cpu_chip_t	*chip;
7451772Sjl139090 	hwd_core_t	*core;
7461772Sjl139090 	hwd_cpu_t	*cpu;
7471772Sjl139090 	int		ret;
7481772Sjl139090 
7491772Sjl139090 	probe = arg;
7501772Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
7511772Sjl139090 	core = &chip->chip_cores[probe->pr_core];
7521772Sjl139090 	cpu = &core->core_cpus[probe->pr_cpu];
7531772Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_NODE);
7541772Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CPU_NODE);
7551772Sjl139090 
7561772Sjl139090 	OPL_UPDATE_PROP(int, node, "cpuid", cpu->cpu_cpuid);
7571772Sjl139090 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_cpu);
7581772Sjl139090 
7591772Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
7601772Sjl139090 
7611772Sjl139090 	return (DDI_WALK_TERMINATE);
7621772Sjl139090 }
7631772Sjl139090 
7641772Sjl139090 /*
7651772Sjl139090  * Create "cpu" nodes as child nodes of a given "core" node.
7661772Sjl139090  */
7671772Sjl139090 static int
7681772Sjl139090 opl_probe_cpus(opl_probe_t *probe)
7691772Sjl139090 {
7701772Sjl139090 	int		i;
7711772Sjl139090 	hwd_cpu_chip_t	*chip;
7721772Sjl139090 	hwd_core_t	*core;
7731772Sjl139090 	hwd_cpu_t	*cpus;
7741772Sjl139090 
7751772Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
7761772Sjl139090 	core = &chip->chip_cores[probe->pr_core];
7771772Sjl139090 	cpus = &core->core_cpus[0];
7781772Sjl139090 
7791772Sjl139090 	for (i = 0; i < HWD_CPUS_PER_CORE; i++) {
7801772Sjl139090 
7811772Sjl139090 		/*
7821772Sjl139090 		 * Olympus-C has 2 cpus per core.
7831772Sjl139090 		 * Jupiter has 4 cpus per core.
7841772Sjl139090 		 * For the Olympus-C based platform, we expect the cpu_status
7851772Sjl139090 		 * of the non-existent cpus to be set to missing.
7861772Sjl139090 		 */
7871772Sjl139090 		if (!HWD_STATUS_OK(cpus[i].cpu_status))
7881772Sjl139090 			continue;
7891772Sjl139090 
7901772Sjl139090 		probe->pr_create = opl_create_cpu;
7911772Sjl139090 		probe->pr_cpu = i;
7921772Sjl139090 		if (opl_create_node(probe) == NULL) {
7931772Sjl139090 
7941772Sjl139090 			cmn_err(CE_WARN, "IKP: create cpu (%d-%d-%d-%d) failed",
7951772Sjl139090 				probe->pr_board, probe->pr_cpu_chip,
7961772Sjl139090 				probe->pr_core, probe->pr_cpu);
7971772Sjl139090 			return (-1);
7981772Sjl139090 		}
7991772Sjl139090 	}
8001772Sjl139090 
8011772Sjl139090 	return (0);
8021772Sjl139090 }
8031772Sjl139090 
8041772Sjl139090 /*
8051772Sjl139090  * Set the properties for a "core" node.
8061772Sjl139090  */
8071772Sjl139090 /*ARGSUSED*/
8081772Sjl139090 static int
8091772Sjl139090 opl_create_core(dev_info_t *node, void *arg, uint_t flags)
8101772Sjl139090 {
8111772Sjl139090 	opl_probe_t	*probe;
8121772Sjl139090 	hwd_cpu_chip_t	*chip;
8131772Sjl139090 	hwd_core_t	*core;
8141772Sjl139090 	int		sharing[2];
8151772Sjl139090 	int		ret;
8161772Sjl139090 
8171772Sjl139090 	probe = arg;
8181772Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
8191772Sjl139090 	core = &chip->chip_cores[probe->pr_core];
8201772Sjl139090 
8211772Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CORE_NODE);
8221772Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", OPL_CORE_NODE);
8231772Sjl139090 	OPL_UPDATE_PROP(string, node, "compatible", chip->chip_compatible);
8241772Sjl139090 
8251772Sjl139090 	OPL_UPDATE_PROP(int, node, "reg", probe->pr_core);
8261772Sjl139090 	OPL_UPDATE_PROP(int, node, "manufacturer#", core->core_manufacturer);
8271772Sjl139090 	OPL_UPDATE_PROP(int, node, "implementation#",
8281772Sjl139090 	    core->core_implementation);
8291772Sjl139090 	OPL_UPDATE_PROP(int, node, "mask#", core->core_mask);
8301772Sjl139090 
8313659Sjimand 	OPL_UPDATE_PROP(int, node, "sparc-version", 9);
8321772Sjl139090 	OPL_UPDATE_PROP(int, node, "clock-frequency", core->core_frequency);
8331772Sjl139090 
8341772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-size", core->core_l1_icache_size);
8351772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-line-size",
8361772Sjl139090 	    core->core_l1_icache_line_size);
8371772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-icache-associativity",
8381772Sjl139090 	    core->core_l1_icache_associativity);
8391772Sjl139090 	OPL_UPDATE_PROP(int, node, "#itlb-entries",
8401772Sjl139090 	    core->core_num_itlb_entries);
8411772Sjl139090 
8421772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-size", core->core_l1_dcache_size);
8431772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-line-size",
8441772Sjl139090 	    core->core_l1_dcache_line_size);
8451772Sjl139090 	OPL_UPDATE_PROP(int, node, "l1-dcache-associativity",
8461772Sjl139090 	    core->core_l1_dcache_associativity);
8471772Sjl139090 	OPL_UPDATE_PROP(int, node, "#dtlb-entries",
8481772Sjl139090 	    core->core_num_dtlb_entries);
8491772Sjl139090 
8501772Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-size", core->core_l2_cache_size);
8511772Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-line-size",
8521772Sjl139090 	    core->core_l2_cache_line_size);
8531772Sjl139090 	OPL_UPDATE_PROP(int, node, "l2-cache-associativity",
8541772Sjl139090 	    core->core_l2_cache_associativity);
8551772Sjl139090 	sharing[0] = 0;
8561772Sjl139090 	sharing[1] = core->core_l2_cache_sharing;
8571772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "l2-cache-sharing", sharing, 2);
8581772Sjl139090 
8591772Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
8601772Sjl139090 
8611772Sjl139090 	return (DDI_WALK_TERMINATE);
8621772Sjl139090 }
8631772Sjl139090 
8641772Sjl139090 /*
8651772Sjl139090  * Create "core" nodes as child nodes of a given "cmp" node.
8661772Sjl139090  *
8671772Sjl139090  * Create the branch below each "core" node".
8681772Sjl139090  */
8691772Sjl139090 static int
8701772Sjl139090 opl_probe_cores(opl_probe_t *probe)
8711772Sjl139090 {
8721772Sjl139090 	int		i;
8731772Sjl139090 	hwd_cpu_chip_t	*chip;
8741772Sjl139090 	hwd_core_t	*cores;
8751772Sjl139090 	dev_info_t	*parent, *node;
8761772Sjl139090 
8771772Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
8781772Sjl139090 	cores = &chip->chip_cores[0];
8791772Sjl139090 	parent = probe->pr_parent;
8801772Sjl139090 
8811772Sjl139090 	for (i = 0; i < HWD_CORES_PER_CPU_CHIP; i++) {
8821772Sjl139090 
8831772Sjl139090 		if (!HWD_STATUS_OK(cores[i].core_status))
8841772Sjl139090 			continue;
8851772Sjl139090 
8861772Sjl139090 		probe->pr_parent = parent;
8871772Sjl139090 		probe->pr_create = opl_create_core;
8881772Sjl139090 		probe->pr_core = i;
8891772Sjl139090 		node = opl_create_node(probe);
8901772Sjl139090 		if (node == NULL) {
8911772Sjl139090 
8921772Sjl139090 			cmn_err(CE_WARN, "IKP: create core (%d-%d-%d) failed",
8931772Sjl139090 				probe->pr_board, probe->pr_cpu_chip,
8941772Sjl139090 				probe->pr_core);
8951772Sjl139090 			return (-1);
8961772Sjl139090 		}
8971772Sjl139090 
8981772Sjl139090 		/*
8991772Sjl139090 		 * Create "cpu" nodes below "core".
9001772Sjl139090 		 */
9011772Sjl139090 		probe->pr_parent = node;
9021772Sjl139090 		if (opl_probe_cpus(probe) != 0)
9031772Sjl139090 			return (-1);
9041772Sjl139090 	}
9051772Sjl139090 
9061772Sjl139090 	return (0);
9071772Sjl139090 }
9081772Sjl139090 
9091772Sjl139090 /*
9101772Sjl139090  * Set the properties for a "cmp" node.
9111772Sjl139090  */
9121772Sjl139090 /*ARGSUSED*/
9131772Sjl139090 static int
9141772Sjl139090 opl_create_cpu_chip(dev_info_t *node, void *arg, uint_t flags)
9151772Sjl139090 {
9161772Sjl139090 	opl_probe_t	*probe;
9171772Sjl139090 	hwd_cpu_chip_t	*chip;
9181772Sjl139090 	opl_range_t	range;
9191772Sjl139090 	uint64_t	dummy_addr;
9201772Sjl139090 	int		ret;
9211772Sjl139090 
9221772Sjl139090 	probe = arg;
9231772Sjl139090 	chip = &probe->pr_sb->sb_cmu.cmu_cpu_chips[probe->pr_cpu_chip];
9241772Sjl139090 
9251772Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_CPU_CHIP_NODE);
9261772Sjl139090 
9271772Sjl139090 	OPL_UPDATE_PROP(int, node, "portid", chip->chip_portid);
9281772Sjl139090 	OPL_UPDATE_PROP(int, node, "board#", probe->pr_board);
9291772Sjl139090 
9301772Sjl139090 	dummy_addr = OPL_PROC_AS(probe->pr_board, probe->pr_cpu_chip);
9311772Sjl139090 	range.rg_addr_hi = OPL_HI(dummy_addr);
9321772Sjl139090 	range.rg_addr_lo = OPL_LO(dummy_addr);
9331772Sjl139090 	range.rg_size_hi = 0;
9341772Sjl139090 	range.rg_size_lo = 0;
9351772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
9361772Sjl139090 
9371772Sjl139090 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
9381772Sjl139090 	OPL_UPDATE_PROP(int, node, "#size-cells", 0);
9391772Sjl139090 
9401772Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
9411772Sjl139090 
9421772Sjl139090 	return (DDI_WALK_TERMINATE);
9431772Sjl139090 }
9441772Sjl139090 
9451772Sjl139090 /*
9461772Sjl139090  * Create "cmp" nodes as child nodes of the root node.
9471772Sjl139090  *
9481772Sjl139090  * Create the branch below each "cmp" node.
9491772Sjl139090  */
9501772Sjl139090 static int
9511772Sjl139090 opl_probe_cpu_chips(opl_probe_t *probe)
9521772Sjl139090 {
9531772Sjl139090 	int		i;
9541772Sjl139090 	dev_info_t	**cfg_cpu_chips;
9551772Sjl139090 	hwd_cpu_chip_t	*chips;
9561772Sjl139090 	dev_info_t	*node;
9571772Sjl139090 
9581772Sjl139090 	cfg_cpu_chips = opl_boards[probe->pr_board].cfg_cpu_chips;
9591772Sjl139090 	chips = &probe->pr_sb->sb_cmu.cmu_cpu_chips[0];
9601772Sjl139090 
9611772Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
9621772Sjl139090 
9631772Sjl139090 		ASSERT(cfg_cpu_chips[i] == NULL);
9641772Sjl139090 
9651772Sjl139090 		if (!HWD_STATUS_OK(chips[i].chip_status))
9661772Sjl139090 			continue;
9671772Sjl139090 
9681772Sjl139090 		probe->pr_parent = ddi_root_node();
9691772Sjl139090 		probe->pr_create = opl_create_cpu_chip;
9701772Sjl139090 		probe->pr_cpu_chip = i;
9711772Sjl139090 		probe->pr_hold = 1;
9721772Sjl139090 		node = opl_create_node(probe);
9731772Sjl139090 		if (node == NULL) {
9741772Sjl139090 
9751772Sjl139090 			cmn_err(CE_WARN, "IKP: create chip (%d-%d) failed",
9761772Sjl139090 				probe->pr_board, probe->pr_cpu_chip);
9771772Sjl139090 			return (-1);
9781772Sjl139090 		}
9791772Sjl139090 
9801772Sjl139090 		cfg_cpu_chips[i] = node;
9811772Sjl139090 
9821772Sjl139090 		/*
9831772Sjl139090 		 * Create "core" nodes below "cmp".
9841772Sjl139090 		 * We hold the "cmp" node. So, there is no need to hold
9851772Sjl139090 		 * the "core" and "cpu" nodes below it.
9861772Sjl139090 		 */
9871772Sjl139090 		probe->pr_parent = node;
9881772Sjl139090 		probe->pr_hold = 0;
9891772Sjl139090 		if (opl_probe_cores(probe) != 0)
9901772Sjl139090 			return (-1);
9911772Sjl139090 	}
9921772Sjl139090 
9931772Sjl139090 	return (0);
9941772Sjl139090 }
9951772Sjl139090 
9961772Sjl139090 /*
9971772Sjl139090  * Set the properties for a "pseudo-mc" node.
9981772Sjl139090  */
9991772Sjl139090 /*ARGSUSED*/
10001772Sjl139090 static int
10011772Sjl139090 opl_create_pseudo_mc(dev_info_t *node, void *arg, uint_t flags)
10021772Sjl139090 {
10031772Sjl139090 	opl_probe_t	*probe;
10041772Sjl139090 	int		board, portid;
10051772Sjl139090 	hwd_bank_t	*bank;
10061772Sjl139090 	hwd_memory_t	*mem;
10071772Sjl139090 	opl_range_t	range;
10081772Sjl139090 	opl_mc_addr_t	mc[HWD_BANKS_PER_CMU];
10091772Sjl139090 	int		status[2][7];
10101772Sjl139090 	int		i, j;
10111772Sjl139090 	int		ret;
10121772Sjl139090 
10131772Sjl139090 	probe = arg;
10141772Sjl139090 	board = probe->pr_board;
10151772Sjl139090 
10161772Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_PSEUDO_MC_NODE);
10171772Sjl139090 	OPL_UPDATE_PROP(string, node, "device_type", "memory-controller");
10181772Sjl139090 	OPL_UPDATE_PROP(string, node, "compatible", "FJSV,oplmc");
10191772Sjl139090 
10201772Sjl139090 	portid = OPL_LSB_TO_PSEUDOMC_PORTID(board);
10211772Sjl139090 	OPL_UPDATE_PROP(int, node, "portid", portid);
10221772Sjl139090 
10231772Sjl139090 	range.rg_addr_hi = OPL_HI(OPL_MC_AS(board));
10241772Sjl139090 	range.rg_addr_lo = 0x200;
10251772Sjl139090 	range.rg_size_hi = 0;
10261772Sjl139090 	range.rg_size_lo = 0;
10271772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "reg", (int *)&range, 4);
10281772Sjl139090 
10291772Sjl139090 	OPL_UPDATE_PROP(int, node, "board#", board);
10301772Sjl139090 	OPL_UPDATE_PROP(int, node, "physical-board#",
10311772Sjl139090 	    probe->pr_sb->sb_psb_number);
10321772Sjl139090 
10331772Sjl139090 	OPL_UPDATE_PROP(int, node, "#address-cells", 1);
10341772Sjl139090 	OPL_UPDATE_PROP(int, node, "#size-cells", 2);
10351772Sjl139090 
10361772Sjl139090 	mem = &probe->pr_sb->sb_cmu.cmu_memory;
10371772Sjl139090 
10381772Sjl139090 	range.rg_addr_hi = OPL_HI(mem->mem_start_address);
10391772Sjl139090 	range.rg_addr_lo = OPL_LO(mem->mem_start_address);
10401772Sjl139090 	range.rg_size_hi = OPL_HI(mem->mem_size);
10411772Sjl139090 	range.rg_size_lo = OPL_LO(mem->mem_size);
10421772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, node, "sb-mem-ranges", (int *)&range, 4);
10431772Sjl139090 
10441772Sjl139090 	bank = probe->pr_sb->sb_cmu.cmu_memory.mem_banks;
10451772Sjl139090 	for (i = 0, j = 0; i < HWD_BANKS_PER_CMU; i++) {
10461772Sjl139090 
10471772Sjl139090 		if (!HWD_STATUS_OK(bank[i].bank_status))
10481772Sjl139090 			continue;
10491772Sjl139090 
10501772Sjl139090 		mc[j].mc_bank = i;
10511772Sjl139090 		mc[j].mc_hi = OPL_HI(bank[i].bank_register_address);
10521772Sjl139090 		mc[j].mc_lo = OPL_LO(bank[i].bank_register_address);
10531772Sjl139090 		j++;
10541772Sjl139090 	}
10553354Sjl139090 
10563354Sjl139090 	if (j > 0) {
10573354Sjl139090 		OPL_UPDATE_PROP_ARRAY(int, node, "mc-addr", (int *)mc, j*3);
10583354Sjl139090 	} else {
10593354Sjl139090 		/*
10603354Sjl139090 		 * If there is no memory, we need the mc-addr property, but
10613354Sjl139090 		 * it is length 0.  The only way to do this using ndi seems
10623354Sjl139090 		 * to be by creating a boolean property.
10633354Sjl139090 		 */
10643354Sjl139090 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node, "mc-addr");
10653354Sjl139090 		OPL_UPDATE_PROP_ERR(ret, "mc-addr");
10663354Sjl139090 	}
10671772Sjl139090 
10681772Sjl139090 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs0-mc-pa-trans-table",
10691772Sjl139090 	    mem->mem_cs[0].cs_pa_mac_table, 64);
10701772Sjl139090 	OPL_UPDATE_PROP_ARRAY(byte, node, "cs1-mc-pa-trans-table",
10711772Sjl139090 	    mem->mem_cs[1].cs_pa_mac_table, 64);
10721772Sjl139090 
10731772Sjl139090 #define	CS_PER_MEM 2
10741772Sjl139090 
10751772Sjl139090 	for (i = 0, j = 0; i < CS_PER_MEM; i++) {
10761772Sjl139090 		if (HWD_STATUS_OK(mem->mem_cs[i].cs_status) ||
10771772Sjl139090 			HWD_STATUS_FAILED(mem->mem_cs[i].cs_status)) {
10781772Sjl139090 			status[j][0] = i;
10791772Sjl139090 			if (HWD_STATUS_OK(mem->mem_cs[i].cs_status))
10801772Sjl139090 				status[j][1] = 0;
10811772Sjl139090 			else
10821772Sjl139090 				status[j][1] = 1;
10831772Sjl139090 			status[j][2] =
10841772Sjl139090 			    OPL_HI(mem->mem_cs[i].cs_available_capacity);
10851772Sjl139090 			status[j][3] =
10861772Sjl139090 			    OPL_LO(mem->mem_cs[i].cs_available_capacity);
10871772Sjl139090 			status[j][4] = OPL_HI(mem->mem_cs[i].cs_dimm_capacity);
10881772Sjl139090 			status[j][5] = OPL_LO(mem->mem_cs[i].cs_dimm_capacity);
10891772Sjl139090 			status[j][6] = mem->mem_cs[i].cs_number_of_dimms;
10901772Sjl139090 			j++;
10911772Sjl139090 		}
10921772Sjl139090 	}
10933354Sjl139090 
10943354Sjl139090 	if (j > 0) {
10953354Sjl139090 		OPL_UPDATE_PROP_ARRAY(int, node, "cs-status", (int *)status,
10963354Sjl139090 		    j*7);
10973354Sjl139090 	} else {
10983354Sjl139090 		/*
10993354Sjl139090 		 * If there is no memory, we need the cs-status property, but
11003354Sjl139090 		 * it is length 0.  The only way to do this using ndi seems
11013354Sjl139090 		 * to be by creating a boolean property.
11023354Sjl139090 		 */
11033354Sjl139090 		ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, node,
11043354Sjl139090 		    "cs-status");
11053354Sjl139090 		OPL_UPDATE_PROP_ERR(ret, "cs-status");
11063354Sjl139090 	}
11071772Sjl139090 
11081772Sjl139090 	return (DDI_WALK_TERMINATE);
11091772Sjl139090 }
11101772Sjl139090 
11111772Sjl139090 /*
11121772Sjl139090  * Create "pseudo-mc" nodes
11131772Sjl139090  */
11141772Sjl139090 static int
11151772Sjl139090 opl_probe_memory(opl_probe_t *probe)
11161772Sjl139090 {
11171772Sjl139090 	int		board;
11181772Sjl139090 	opl_board_cfg_t	*board_cfg;
11191772Sjl139090 	dev_info_t	*node;
11201772Sjl139090 
11211772Sjl139090 	board = probe->pr_board;
11221772Sjl139090 	board_cfg = &opl_boards[board];
11231772Sjl139090 
11241772Sjl139090 	ASSERT(board_cfg->cfg_pseudo_mc == NULL);
11251772Sjl139090 
11261772Sjl139090 	probe->pr_parent = ddi_root_node();
11271772Sjl139090 	probe->pr_create = opl_create_pseudo_mc;
11281772Sjl139090 	probe->pr_hold = 1;
11291772Sjl139090 	node = opl_create_node(probe);
11301772Sjl139090 	if (node == NULL) {
11311772Sjl139090 
11321772Sjl139090 		cmn_err(CE_WARN, "IKP: create pseudo-mc (%d) failed", board);
11331772Sjl139090 		return (-1);
11341772Sjl139090 	}
11351772Sjl139090 
11361772Sjl139090 	board_cfg->cfg_pseudo_mc = node;
11371772Sjl139090 
11381772Sjl139090 	return (0);
11391772Sjl139090 }
11401772Sjl139090 
11411772Sjl139090 /*
11421772Sjl139090  * Allocate the fcode ops handle.
11431772Sjl139090  */
11441772Sjl139090 /*ARGSUSED*/
11451772Sjl139090 static
11461772Sjl139090 fco_handle_t
11471772Sjl139090 opl_fc_ops_alloc_handle(dev_info_t *parent, dev_info_t *child,
11481772Sjl139090 			void *fcode, size_t fcode_size, char *unit_address,
11491772Sjl139090 			char *my_args)
11501772Sjl139090 {
11511772Sjl139090 	fco_handle_t	rp;
11521772Sjl139090 	phandle_t	h;
11531772Sjl139090 	char		*buf;
11541772Sjl139090 
11551772Sjl139090 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
11561772Sjl139090 	rp->next_handle = fc_ops_alloc_handle(parent, child, fcode, fcode_size,
11571772Sjl139090 	    unit_address, NULL);
11581772Sjl139090 	rp->ap = parent;
11591772Sjl139090 	rp->child = child;
11601772Sjl139090 	rp->fcode = fcode;
11611772Sjl139090 	rp->fcode_size = fcode_size;
11621772Sjl139090 	rp->my_args = my_args;
11631772Sjl139090 
11641772Sjl139090 	if (unit_address) {
11651772Sjl139090 		buf = kmem_zalloc(UNIT_ADDR_SIZE, KM_SLEEP);
11661772Sjl139090 		(void) strcpy(buf, unit_address);
11671772Sjl139090 		rp->unit_address = buf;
11681772Sjl139090 	}
11691772Sjl139090 
11701772Sjl139090 	/*
11711772Sjl139090 	 * Add the child's nodeid to our table...
11721772Sjl139090 	 */
11731772Sjl139090 	h = ddi_get_nodeid(rp->child);
11741772Sjl139090 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child, h);
11751772Sjl139090 
11761772Sjl139090 	return (rp);
11771772Sjl139090 }
11781772Sjl139090 
11791772Sjl139090 
11801772Sjl139090 static void
11811772Sjl139090 opl_fc_ops_free_handle(fco_handle_t rp)
11821772Sjl139090 {
11831772Sjl139090 	struct fc_resource	*resp, *nresp;
11841772Sjl139090 
11851772Sjl139090 	ASSERT(rp);
11861772Sjl139090 
11871772Sjl139090 	if (rp->next_handle)
11881772Sjl139090 		fc_ops_free_handle(rp->next_handle);
11891772Sjl139090 	if (rp->unit_address)
11901772Sjl139090 		kmem_free(rp->unit_address, UNIT_ADDR_SIZE);
11911772Sjl139090 
11921772Sjl139090 	/*
11931772Sjl139090 	 * Release all the resources from the resource list
11941772Sjl139090 	 */
11951772Sjl139090 	for (resp = rp->head; resp != NULL; resp = nresp) {
11961772Sjl139090 		nresp = resp->next;
11971772Sjl139090 		switch (resp->type) {
11981772Sjl139090 
11991772Sjl139090 		case RT_MAP:
12003354Sjl139090 			/*
12013354Sjl139090 			 * If this is still mapped, we'd better unmap it now,
12023354Sjl139090 			 * or all our structures that are tracking it will
12033354Sjl139090 			 * be leaked.
12043354Sjl139090 			 */
12053354Sjl139090 			if (resp->fc_map_handle != NULL)
12063354Sjl139090 				opl_unmap_phys(&resp->fc_map_handle);
12071772Sjl139090 			break;
12081772Sjl139090 
12091772Sjl139090 		case RT_DMA:
12101772Sjl139090 			/*
12111772Sjl139090 			 * DMA has to be freed up at exit time.
12121772Sjl139090 			 */
12131772Sjl139090 			cmn_err(CE_CONT,
12141772Sjl139090 			    "opl_fc_ops_free_handle: Unexpected DMA seen!");
12151772Sjl139090 			break;
12161772Sjl139090 
12171772Sjl139090 		case RT_CONTIGIOUS:
12181772Sjl139090 			FC_DEBUG2(1, CE_CONT, "opl_fc_ops_free: "
12191772Sjl139090 			    "Free claim-memory resource 0x%lx size 0x%x\n",
12201772Sjl139090 			    resp->fc_contig_virt, resp->fc_contig_len);
12211772Sjl139090 
12221772Sjl139090 			(void) ndi_ra_free(ddi_root_node(),
12231772Sjl139090 			    (uint64_t)resp->fc_contig_virt,
12241772Sjl139090 			    resp->fc_contig_len, "opl-fcodemem",
12251772Sjl139090 			    NDI_RA_PASS);
12261772Sjl139090 
12271772Sjl139090 			break;
12281772Sjl139090 
12291772Sjl139090 		default:
12301772Sjl139090 			cmn_err(CE_CONT, "opl_fc_ops_free: "
12311772Sjl139090 			    "unknown resource type %d", resp->type);
12321772Sjl139090 			break;
12331772Sjl139090 		}
12341772Sjl139090 		fc_rem_resource(rp, resp);
12351772Sjl139090 		kmem_free(resp, sizeof (struct fc_resource));
12361772Sjl139090 	}
12371772Sjl139090 
12381772Sjl139090 	kmem_free(rp, sizeof (struct fc_resource_list));
12391772Sjl139090 }
12401772Sjl139090 
12411772Sjl139090 int
12421772Sjl139090 opl_fc_do_op(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
12431772Sjl139090 {
12441772Sjl139090 	opl_fc_ops_t	*op;
12451772Sjl139090 	char		*service = fc_cell2ptr(cp->svc_name);
12461772Sjl139090 
12471772Sjl139090 	ASSERT(rp);
12481772Sjl139090 
12491772Sjl139090 	FC_DEBUG1(1, CE_CONT, "opl_fc_do_op: <%s>\n", service);
12501772Sjl139090 
12511772Sjl139090 	/*
12521772Sjl139090 	 * First try the generic fc_ops.
12531772Sjl139090 	 */
12541772Sjl139090 	if (fc_ops(ap, rp->next_handle, cp) == 0)
12551772Sjl139090 		return (0);
12561772Sjl139090 
12571772Sjl139090 	/*
12581772Sjl139090 	 * Now try the Jupiter-specific ops.
12591772Sjl139090 	 */
12601772Sjl139090 	for (op = opl_fc_ops; op->fc_service != NULL; ++op)
12611772Sjl139090 		if (strcmp(op->fc_service, service) == 0)
12621772Sjl139090 			return (op->fc_op(ap, rp, cp));
12631772Sjl139090 
12641772Sjl139090 	FC_DEBUG1(9, CE_CONT, "opl_fc_do_op: <%s> not serviced\n", service);
12651772Sjl139090 
12661772Sjl139090 	return (-1);
12671772Sjl139090 }
12681772Sjl139090 
12691772Sjl139090 /*
12701772Sjl139090  * map-in  (phys.lo phys.hi size -- virt)
12711772Sjl139090  */
12721772Sjl139090 static int
12731772Sjl139090 opl_map_in(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
12741772Sjl139090 {
12751772Sjl139090 	size_t			len;
12761772Sjl139090 	int			error;
12771772Sjl139090 	caddr_t			virt;
12781772Sjl139090 	struct fc_resource	*resp;
12791772Sjl139090 	struct regspec		rspec;
12801772Sjl139090 	ddi_device_acc_attr_t	acc;
12811772Sjl139090 	ddi_acc_handle_t	h;
12821772Sjl139090 
12831772Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
12841772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
12851772Sjl139090 
12861772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
12871772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
12881772Sjl139090 
12891772Sjl139090 	rspec.regspec_size = len = fc_cell2size(fc_arg(cp, 0));
12901772Sjl139090 	rspec.regspec_bustype = fc_cell2uint(fc_arg(cp, 1));
12911772Sjl139090 	rspec.regspec_addr = fc_cell2uint(fc_arg(cp, 2));
12921772Sjl139090 
12931772Sjl139090 	acc.devacc_attr_version = DDI_DEVICE_ATTR_V0;
12941772Sjl139090 	acc.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
12951772Sjl139090 	acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
12961772Sjl139090 
12971772Sjl139090 	FC_DEBUG3(1, CE_CONT, "opl_map_in: attempting map in "
12981772Sjl139090 	    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
12991772Sjl139090 	    rspec.regspec_addr, rspec.regspec_size);
13001772Sjl139090 
13011772Sjl139090 	error = opl_map_phys(rp->child, &rspec, &virt, &acc, &h);
13021772Sjl139090 
13031772Sjl139090 	if (error)  {
13041772Sjl139090 		FC_DEBUG3(1, CE_CONT, "opl_map_in: map in failed - "
13051772Sjl139090 		    "address 0x%08x.%08x length %x\n", rspec.regspec_bustype,
13061772Sjl139090 		    rspec.regspec_addr, rspec.regspec_size);
13071772Sjl139090 
13081772Sjl139090 		return (fc_priv_error(cp, "opl map-in failed"));
13091772Sjl139090 	}
13101772Sjl139090 
13111772Sjl139090 	FC_DEBUG1(3, CE_CONT, "opl_map_in: returning virt %p\n", virt);
13121772Sjl139090 
13131772Sjl139090 	cp->nresults = fc_int2cell(1);
13141772Sjl139090 	fc_result(cp, 0) = fc_ptr2cell(virt);
13151772Sjl139090 
13161772Sjl139090 	/*
13171772Sjl139090 	 * Log this resource ...
13181772Sjl139090 	 */
13191772Sjl139090 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
13201772Sjl139090 	resp->type = RT_MAP;
13211772Sjl139090 	resp->fc_map_virt = virt;
13221772Sjl139090 	resp->fc_map_len = len;
13231772Sjl139090 	resp->fc_map_handle = h;
13241772Sjl139090 	fc_add_resource(rp, resp);
13251772Sjl139090 
13261772Sjl139090 	return (fc_success_op(ap, rp, cp));
13271772Sjl139090 }
13281772Sjl139090 
13291772Sjl139090 /*
13301772Sjl139090  * map-out (virt size -- )
13311772Sjl139090  */
13321772Sjl139090 static int
13331772Sjl139090 opl_map_out(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
13341772Sjl139090 {
13351772Sjl139090 	caddr_t			virt;
13361772Sjl139090 	size_t			len;
13371772Sjl139090 	struct fc_resource	*resp;
13381772Sjl139090 
13391772Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
13401772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
13411772Sjl139090 
13421772Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 1));
13431772Sjl139090 
13441772Sjl139090 	len = fc_cell2size(fc_arg(cp, 0));
13451772Sjl139090 
13461772Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_map_out: attempting map out %p %x\n",
13471772Sjl139090 	    virt, len);
13481772Sjl139090 
13491772Sjl139090 	/*
13501772Sjl139090 	 * Find if this request matches a mapping resource we set up.
13511772Sjl139090 	 */
13521772Sjl139090 	fc_lock_resource_list(rp);
13531772Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
13541772Sjl139090 		if (resp->type != RT_MAP)
13551772Sjl139090 			continue;
13561772Sjl139090 		if (resp->fc_map_virt != virt)
13571772Sjl139090 			continue;
13581772Sjl139090 		if (resp->fc_map_len == len)
13591772Sjl139090 			break;
13601772Sjl139090 	}
13611772Sjl139090 	fc_unlock_resource_list(rp);
13621772Sjl139090 
13631772Sjl139090 	if (resp == NULL)
13641772Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
13651772Sjl139090 		    "known mapping"));
13661772Sjl139090 
13671772Sjl139090 	opl_unmap_phys(&resp->fc_map_handle);
13681772Sjl139090 
13691772Sjl139090 	/*
13701772Sjl139090 	 * remove the resource from the list and release it.
13711772Sjl139090 	 */
13721772Sjl139090 	fc_rem_resource(rp, resp);
13731772Sjl139090 	kmem_free(resp, sizeof (struct fc_resource));
13741772Sjl139090 
13751772Sjl139090 	cp->nresults = fc_int2cell(0);
13761772Sjl139090 	return (fc_success_op(ap, rp, cp));
13771772Sjl139090 }
13781772Sjl139090 
13791772Sjl139090 static int
13801772Sjl139090 opl_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
13811772Sjl139090 {
13821772Sjl139090 	size_t			len;
13831772Sjl139090 	caddr_t			virt;
13841772Sjl139090 	int			error = 0;
13851772Sjl139090 	uint64_t		v;
13861772Sjl139090 	uint64_t		x;
13871772Sjl139090 	uint32_t		l;
13881772Sjl139090 	uint16_t		w;
13891772Sjl139090 	uint8_t			b;
13901772Sjl139090 	char			*service = fc_cell2ptr(cp->svc_name);
13911772Sjl139090 	struct fc_resource	*resp;
13921772Sjl139090 
13931772Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
13941772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
13951772Sjl139090 
13961772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
13971772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
13981772Sjl139090 
13991772Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
14001772Sjl139090 
14011772Sjl139090 	/*
14021772Sjl139090 	 * Determine the access width .. we can switch on the 2nd
14031772Sjl139090 	 * character of the name which is "rx@", "rl@", "rb@" or "rw@"
14041772Sjl139090 	 */
14051772Sjl139090 	switch (*(service + 1)) {
14061772Sjl139090 	case 'x':	len = sizeof (x); break;
14071772Sjl139090 	case 'l':	len = sizeof (l); break;
14081772Sjl139090 	case 'w':	len = sizeof (w); break;
14091772Sjl139090 	case 'b':	len = sizeof (b); break;
14101772Sjl139090 	}
14111772Sjl139090 
14121772Sjl139090 	/*
14131772Sjl139090 	 * Check the alignment ...
14141772Sjl139090 	 */
14151772Sjl139090 	if (((intptr_t)virt & (len - 1)) != 0)
14161772Sjl139090 		return (fc_priv_error(cp, "unaligned access"));
14171772Sjl139090 
14181772Sjl139090 	/*
14191772Sjl139090 	 * Find if this virt is 'within' a request we know about
14201772Sjl139090 	 */
14211772Sjl139090 	fc_lock_resource_list(rp);
14221772Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
14231772Sjl139090 		if (resp->type == RT_MAP) {
14241772Sjl139090 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
14251772Sjl139090 			    ((virt + len) <=
14261772Sjl139090 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
14271772Sjl139090 				break;
14281772Sjl139090 		} else if (resp->type == RT_CONTIGIOUS) {
14291772Sjl139090 		    if ((virt >= (caddr_t)resp->fc_contig_virt) && ((virt + len)
14301772Sjl139090 			<= ((caddr_t)resp->fc_contig_virt +
14311772Sjl139090 			    resp->fc_contig_len)))
14321772Sjl139090 				break;
14331772Sjl139090 		}
14341772Sjl139090 	}
14351772Sjl139090 	fc_unlock_resource_list(rp);
14361772Sjl139090 
14371772Sjl139090 	if (resp == NULL) {
14381772Sjl139090 		return (fc_priv_error(cp, "request not within "
14391772Sjl139090 		    "known mappings"));
14401772Sjl139090 	}
14411772Sjl139090 
14421772Sjl139090 	switch (len) {
14431772Sjl139090 	case sizeof (x):
14441772Sjl139090 		if (resp->type == RT_MAP)
14451772Sjl139090 			error = ddi_peek64(rp->child,
14461772Sjl139090 			(int64_t *)virt, (int64_t *)&x);
14471772Sjl139090 		else /* RT_CONTIGIOUS */
14481772Sjl139090 			x = *(int64_t *)virt;
14491772Sjl139090 		v = x;
14501772Sjl139090 		break;
14511772Sjl139090 	case sizeof (l):
14521772Sjl139090 		if (resp->type == RT_MAP)
14531772Sjl139090 			error = ddi_peek32(rp->child,
14541772Sjl139090 			(int32_t *)virt, (int32_t *)&l);
14551772Sjl139090 		else /* RT_CONTIGIOUS */
14561772Sjl139090 			l = *(int32_t *)virt;
14571772Sjl139090 		v = l;
14581772Sjl139090 		break;
14591772Sjl139090 	case sizeof (w):
14601772Sjl139090 		if (resp->type == RT_MAP)
14611772Sjl139090 			error = ddi_peek16(rp->child,
14621772Sjl139090 			(int16_t *)virt, (int16_t *)&w);
14631772Sjl139090 		else /* RT_CONTIGIOUS */
14641772Sjl139090 			w = *(int16_t *)virt;
14651772Sjl139090 		v = w;
14661772Sjl139090 		break;
14671772Sjl139090 	case sizeof (b):
14681772Sjl139090 		if (resp->type == RT_MAP)
14691772Sjl139090 			error = ddi_peek8(rp->child,
14701772Sjl139090 			(int8_t *)virt, (int8_t *)&b);
14711772Sjl139090 		else /* RT_CONTIGIOUS */
14721772Sjl139090 			b = *(int8_t *)virt;
14731772Sjl139090 		v = b;
14741772Sjl139090 		break;
14751772Sjl139090 	}
14761772Sjl139090 
14771772Sjl139090 	if (error == DDI_FAILURE) {
14781772Sjl139090 		FC_DEBUG2(1, CE_CONT, "opl_register_fetch: access error "
14791772Sjl139090 		    "accessing virt %p len %d\n", virt, len);
14801772Sjl139090 		return (fc_priv_error(cp, "access error"));
14811772Sjl139090 	}
14821772Sjl139090 
14831772Sjl139090 	FC_DEBUG3(1, CE_CONT, "register_fetch (%s) %llx %llx\n",
14841772Sjl139090 	    service, virt, v);
14851772Sjl139090 
14861772Sjl139090 	cp->nresults = fc_int2cell(1);
14871772Sjl139090 	switch (len) {
14881772Sjl139090 	case sizeof (x): fc_result(cp, 0) = x; break;
14891772Sjl139090 	case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break;
14901772Sjl139090 	case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break;
14911772Sjl139090 	case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break;
14921772Sjl139090 	}
14931772Sjl139090 	return (fc_success_op(ap, rp, cp));
14941772Sjl139090 }
14951772Sjl139090 
14961772Sjl139090 static int
14971772Sjl139090 opl_register_store(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
14981772Sjl139090 {
14991772Sjl139090 	size_t			len;
15001772Sjl139090 	caddr_t			virt;
15011772Sjl139090 	uint64_t		v;
15021772Sjl139090 	uint64_t		x;
15031772Sjl139090 	uint32_t		l;
15041772Sjl139090 	uint16_t		w;
15051772Sjl139090 	uint8_t			b;
15061772Sjl139090 	char			*service = fc_cell2ptr(cp->svc_name);
15071772Sjl139090 	struct fc_resource	*resp;
15081772Sjl139090 	int			error = 0;
15091772Sjl139090 
15101772Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
15111772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
15121772Sjl139090 
15131772Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
15141772Sjl139090 
15151772Sjl139090 	/*
15161772Sjl139090 	 * Determine the access width .. we can switch on the 2nd
15171772Sjl139090 	 * character of the name which is "rx!", "rl!", "rb!" or "rw!"
15181772Sjl139090 	 */
15191772Sjl139090 	switch (*(service + 1)) {
15201772Sjl139090 	case 'x':
15211772Sjl139090 		len = sizeof (x);
15221772Sjl139090 		x = fc_arg(cp, 1);
15231772Sjl139090 		v = x;
15241772Sjl139090 		break;
15251772Sjl139090 	case 'l':
15261772Sjl139090 		len = sizeof (l);
15271772Sjl139090 		l = fc_cell2uint32_t(fc_arg(cp, 1));
15281772Sjl139090 		v = l;
15291772Sjl139090 		break;
15301772Sjl139090 	case 'w':
15311772Sjl139090 		len = sizeof (w);
15321772Sjl139090 		w = fc_cell2uint16_t(fc_arg(cp, 1));
15331772Sjl139090 		v = w;
15341772Sjl139090 		break;
15351772Sjl139090 	case 'b':
15361772Sjl139090 		len = sizeof (b);
15371772Sjl139090 		b = fc_cell2uint8_t(fc_arg(cp, 1));
15381772Sjl139090 		v = b;
15391772Sjl139090 		break;
15401772Sjl139090 	}
15411772Sjl139090 
15421772Sjl139090 	FC_DEBUG3(1, CE_CONT, "register_store (%s) %llx %llx\n",
15431772Sjl139090 	    service, virt, v);
15441772Sjl139090 
15451772Sjl139090 	/*
15461772Sjl139090 	 * Check the alignment ...
15471772Sjl139090 	 */
15481772Sjl139090 	if (((intptr_t)virt & (len - 1)) != 0)
15491772Sjl139090 		return (fc_priv_error(cp, "unaligned access"));
15501772Sjl139090 
15511772Sjl139090 	/*
15521772Sjl139090 	 * Find if this virt is 'within' a request we know about
15531772Sjl139090 	 */
15541772Sjl139090 	fc_lock_resource_list(rp);
15551772Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
15561772Sjl139090 		if (resp->type == RT_MAP) {
15571772Sjl139090 			if ((virt >= (caddr_t)resp->fc_map_virt) &&
15581772Sjl139090 			    ((virt + len) <=
15591772Sjl139090 			    ((caddr_t)resp->fc_map_virt + resp->fc_map_len)))
15601772Sjl139090 				break;
15611772Sjl139090 		} else if (resp->type == RT_CONTIGIOUS) {
15621772Sjl139090 		    if ((virt >= (caddr_t)resp->fc_contig_virt) && ((virt + len)
15631772Sjl139090 			<= ((caddr_t)resp->fc_contig_virt +
15641772Sjl139090 			    resp->fc_contig_len)))
15651772Sjl139090 				break;
15661772Sjl139090 		}
15671772Sjl139090 	}
15681772Sjl139090 	fc_unlock_resource_list(rp);
15691772Sjl139090 
15701772Sjl139090 	if (resp == NULL)
15711772Sjl139090 		return (fc_priv_error(cp, "request not within"
15721772Sjl139090 		    "known mappings"));
15731772Sjl139090 
15741772Sjl139090 	switch (len) {
15751772Sjl139090 	case sizeof (x):
15761772Sjl139090 		if (resp->type == RT_MAP)
15771772Sjl139090 			error = ddi_poke64(rp->child, (int64_t *)virt, x);
15781772Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
15791772Sjl139090 			*(uint64_t *)virt = x;
15801772Sjl139090 		break;
15811772Sjl139090 	case sizeof (l):
15821772Sjl139090 		if (resp->type == RT_MAP)
15831772Sjl139090 			error = ddi_poke32(rp->child, (int32_t *)virt, l);
15841772Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
15851772Sjl139090 			*(uint32_t *)virt = l;
15861772Sjl139090 		break;
15871772Sjl139090 	case sizeof (w):
15881772Sjl139090 		if (resp->type == RT_MAP)
15891772Sjl139090 			error = ddi_poke16(rp->child, (int16_t *)virt, w);
15901772Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
15911772Sjl139090 			*(uint16_t *)virt = w;
15921772Sjl139090 		break;
15931772Sjl139090 	case sizeof (b):
15941772Sjl139090 		if (resp->type == RT_MAP)
15951772Sjl139090 			error = ddi_poke8(rp->child, (int8_t *)virt, b);
15961772Sjl139090 		else if (resp->type == RT_CONTIGIOUS)
15971772Sjl139090 			*(uint8_t *)virt = b;
15981772Sjl139090 		break;
15991772Sjl139090 	}
16001772Sjl139090 
16011772Sjl139090 	if (error == DDI_FAILURE) {
16021772Sjl139090 		FC_DEBUG2(1, CE_CONT, "opl_register_store: access error "
16031772Sjl139090 		    "accessing virt %p len %d\n", virt, len);
16041772Sjl139090 		return (fc_priv_error(cp, "access error"));
16051772Sjl139090 	}
16061772Sjl139090 
16071772Sjl139090 	cp->nresults = fc_int2cell(0);
16081772Sjl139090 	return (fc_success_op(ap, rp, cp));
16091772Sjl139090 }
16101772Sjl139090 
16111772Sjl139090 /*
16121772Sjl139090  * opl_claim_memory
16131772Sjl139090  *
16141772Sjl139090  * claim-memory (align size vhint -- vaddr)
16151772Sjl139090  */
16161772Sjl139090 static int
16171772Sjl139090 opl_claim_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
16181772Sjl139090 {
16191772Sjl139090 	int			align, size, vhint;
16201772Sjl139090 	uint64_t		answer, alen;
16211772Sjl139090 	ndi_ra_request_t	request;
16221772Sjl139090 	struct fc_resource	*resp;
16231772Sjl139090 
16241772Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
16251772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
16261772Sjl139090 
16271772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
16281772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
16291772Sjl139090 
16301772Sjl139090 	vhint = fc_cell2int(fc_arg(cp, 2));
16311772Sjl139090 	size  = fc_cell2int(fc_arg(cp, 1));
16321772Sjl139090 	align = fc_cell2int(fc_arg(cp, 0));
16331772Sjl139090 
16341772Sjl139090 	FC_DEBUG3(1, CE_CONT, "opl_claim_memory: align=0x%x size=0x%x "
16351772Sjl139090 	    "vhint=0x%x\n", align, size, vhint);
16361772Sjl139090 
16371772Sjl139090 	if (size == 0) {
16381772Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
16391772Sjl139090 		    "contiguous memory of size zero\n");
16401772Sjl139090 		return (fc_priv_error(cp, "allocation error"));
16411772Sjl139090 	}
16421772Sjl139090 
16431772Sjl139090 	if (vhint) {
16441772Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - vhint is not zero "
16451772Sjl139090 		    "vhint=0x%x - Ignoring Argument\n", vhint);
16461772Sjl139090 	}
16471772Sjl139090 
16481772Sjl139090 	bzero((caddr_t)&request, sizeof (ndi_ra_request_t));
16491772Sjl139090 	request.ra_flags	= NDI_RA_ALLOC_BOUNDED;
16501772Sjl139090 	request.ra_boundbase	= 0;
16511772Sjl139090 	request.ra_boundlen	= 0xffffffff;
16521772Sjl139090 	request.ra_len		= size;
16531772Sjl139090 	request.ra_align_mask	= align - 1;
16541772Sjl139090 
16551772Sjl139090 	if (ndi_ra_alloc(ddi_root_node(), &request, &answer, &alen,
16561772Sjl139090 	    "opl-fcodemem", NDI_RA_PASS) != NDI_SUCCESS) {
16571772Sjl139090 		cmn_err(CE_WARN, "opl_claim_memory - unable to allocate "
16581772Sjl139090 		    "contiguous memory\n");
16591772Sjl139090 		return (fc_priv_error(cp, "allocation error"));
16601772Sjl139090 	}
16611772Sjl139090 
16621772Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_claim_memory: address allocated=0x%lx "
16631772Sjl139090 	    "size=0x%x\n", answer, alen);
16641772Sjl139090 
16651772Sjl139090 	cp->nresults = fc_int2cell(1);
16661772Sjl139090 	fc_result(cp, 0) = answer;
16671772Sjl139090 
16681772Sjl139090 	/*
16691772Sjl139090 	 * Log this resource ...
16701772Sjl139090 	 */
16711772Sjl139090 	resp = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
16721772Sjl139090 	resp->type = RT_CONTIGIOUS;
16731772Sjl139090 	resp->fc_contig_virt = (void *)answer;
16741772Sjl139090 	resp->fc_contig_len = size;
16751772Sjl139090 	fc_add_resource(rp, resp);
16761772Sjl139090 
16771772Sjl139090 	return (fc_success_op(ap, rp, cp));
16781772Sjl139090 }
16791772Sjl139090 
16801772Sjl139090 /*
16811772Sjl139090  * opl_release_memory
16821772Sjl139090  *
16831772Sjl139090  * release-memory (size vaddr -- )
16841772Sjl139090  */
16851772Sjl139090 static int
16861772Sjl139090 opl_release_memory(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
16871772Sjl139090 {
16881772Sjl139090 	int32_t			vaddr, size;
16891772Sjl139090 	struct fc_resource	*resp;
16901772Sjl139090 
16911772Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
16921772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
16931772Sjl139090 
16941772Sjl139090 	if (fc_cell2int(cp->nresults) != 0)
16951772Sjl139090 		return (fc_syntax_error(cp, "nresults must be 0"));
16961772Sjl139090 
16971772Sjl139090 	vaddr = fc_cell2int(fc_arg(cp, 1));
16981772Sjl139090 	size  = fc_cell2int(fc_arg(cp, 0));
16991772Sjl139090 
17001772Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_release_memory: vaddr=0x%x size=0x%x\n",
17011772Sjl139090 	    vaddr, size);
17021772Sjl139090 
17031772Sjl139090 	/*
17041772Sjl139090 	 * Find if this request matches a mapping resource we set up.
17051772Sjl139090 	 */
17061772Sjl139090 	fc_lock_resource_list(rp);
17071772Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
17081772Sjl139090 		if (resp->type != RT_CONTIGIOUS)
17091772Sjl139090 			continue;
17101772Sjl139090 		if (resp->fc_contig_virt != (void *)(uintptr_t)vaddr)
17111772Sjl139090 			continue;
17121772Sjl139090 		if (resp->fc_contig_len == size)
17131772Sjl139090 			break;
17141772Sjl139090 	}
17151772Sjl139090 	fc_unlock_resource_list(rp);
17161772Sjl139090 
17171772Sjl139090 	if (resp == NULL)
17181772Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
17191772Sjl139090 		    "known mapping"));
17201772Sjl139090 
17211772Sjl139090 	(void) ndi_ra_free(ddi_root_node(), vaddr, size,
17221772Sjl139090 	    "opl-fcodemem", NDI_RA_PASS);
17231772Sjl139090 
17241772Sjl139090 	/*
17251772Sjl139090 	 * remove the resource from the list and release it.
17261772Sjl139090 	 */
17271772Sjl139090 	fc_rem_resource(rp, resp);
17281772Sjl139090 	kmem_free(resp, sizeof (struct fc_resource));
17291772Sjl139090 
17301772Sjl139090 	cp->nresults = fc_int2cell(0);
17311772Sjl139090 
17321772Sjl139090 	return (fc_success_op(ap, rp, cp));
17331772Sjl139090 }
17341772Sjl139090 
17351772Sjl139090 /*
17361772Sjl139090  * opl_vtop
17371772Sjl139090  *
17381772Sjl139090  * vtop (vaddr -- paddr.lo paddr.hi)
17391772Sjl139090  */
17401772Sjl139090 static int
17411772Sjl139090 opl_vtop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
17421772Sjl139090 {
17431772Sjl139090 	int			vaddr;
17441772Sjl139090 	uint64_t		paddr;
17451772Sjl139090 	struct fc_resource	*resp;
17461772Sjl139090 
17471772Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
17481772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
17491772Sjl139090 
17501772Sjl139090 	if (fc_cell2int(cp->nresults) >= 3)
17511772Sjl139090 		return (fc_syntax_error(cp, "nresults must be less than 2"));
17521772Sjl139090 
17531772Sjl139090 	vaddr = fc_cell2int(fc_arg(cp, 0));
17541772Sjl139090 
17551772Sjl139090 	/*
17561772Sjl139090 	 * Find if this request matches a mapping resource we set up.
17571772Sjl139090 	 */
17581772Sjl139090 	fc_lock_resource_list(rp);
17591772Sjl139090 	for (resp = rp->head; resp != NULL; resp = resp->next) {
17601772Sjl139090 		if (resp->type != RT_CONTIGIOUS)
17611772Sjl139090 			continue;
1762*3965Sbm42561 		if (((uint64_t)resp->fc_contig_virt <= vaddr) &&
1763*3965Sbm42561 		    (vaddr < (uint64_t)resp->fc_contig_virt +
1764*3965Sbm42561 		    resp->fc_contig_len))
17651772Sjl139090 			break;
17661772Sjl139090 	}
17671772Sjl139090 	fc_unlock_resource_list(rp);
17681772Sjl139090 
17691772Sjl139090 	if (resp == NULL)
17701772Sjl139090 		return (fc_priv_error(cp, "request doesn't match a "
17711772Sjl139090 		    "known mapping"));
17721772Sjl139090 
17731772Sjl139090 	paddr = va_to_pa((void *)(uintptr_t)vaddr);
17741772Sjl139090 
17751772Sjl139090 	FC_DEBUG2(1, CE_CONT, "opl_vtop: vaddr=0x%x paddr=0x%x\n",
17761772Sjl139090 	    vaddr, paddr);
17771772Sjl139090 
17781772Sjl139090 	cp->nresults = fc_int2cell(2);
17791772Sjl139090 
17801772Sjl139090 	fc_result(cp, 0) = paddr;
17811772Sjl139090 	fc_result(cp, 1) = 0;
17821772Sjl139090 
17831772Sjl139090 	return (fc_success_op(ap, rp, cp));
17841772Sjl139090 }
17851772Sjl139090 
17861772Sjl139090 static int
17871772Sjl139090 opl_config_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
17881772Sjl139090 {
17891772Sjl139090 	fc_phandle_t h;
17901772Sjl139090 
17911772Sjl139090 	if (fc_cell2int(cp->nargs) != 0)
17921772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 0"));
17931772Sjl139090 
17941772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
17951772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
17961772Sjl139090 
17971772Sjl139090 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), rp->child);
17981772Sjl139090 
17991772Sjl139090 	cp->nresults = fc_int2cell(1);
18001772Sjl139090 	fc_result(cp, 0) = fc_phandle2cell(h);
18011772Sjl139090 
18021772Sjl139090 	return (fc_success_op(ap, rp, cp));
18031772Sjl139090 }
18041772Sjl139090 
18051772Sjl139090 static int
18061772Sjl139090 opl_get_fcode(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
18071772Sjl139090 {
18081772Sjl139090 	caddr_t		dropin_name_virt, fcode_virt;
18091772Sjl139090 	char		*dropin_name, *fcode;
18101772Sjl139090 	int		fcode_len, status;
18111772Sjl139090 
18121772Sjl139090 	if (fc_cell2int(cp->nargs) != 3)
18131772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 3"));
18141772Sjl139090 
18151772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
18161772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
18171772Sjl139090 
18181772Sjl139090 	dropin_name_virt = fc_cell2ptr(fc_arg(cp, 0));
18191772Sjl139090 
18201772Sjl139090 	fcode_virt = fc_cell2ptr(fc_arg(cp, 1));
18211772Sjl139090 
18221772Sjl139090 	fcode_len = fc_cell2int(fc_arg(cp, 2));
18231772Sjl139090 
18241772Sjl139090 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
18251772Sjl139090 
18261772Sjl139090 	FC_DEBUG2(1, CE_CONT, "get_fcode: %x %d\n", fcode_virt, fcode_len);
18271772Sjl139090 
18281772Sjl139090 	if (copyinstr(fc_cell2ptr(dropin_name_virt), dropin_name,
18291772Sjl139090 	    FC_SVC_NAME_LEN - 1, NULL))  {
18301772Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode: "
18311772Sjl139090 		    "fault copying in drop in name %p\n", dropin_name_virt);
18321772Sjl139090 		status = 0;
18331772Sjl139090 	} else {
18341772Sjl139090 		FC_DEBUG1(1, CE_CONT, "get_fcode: %s\n", dropin_name);
18351772Sjl139090 
18361772Sjl139090 		fcode = kmem_zalloc(fcode_len, KM_SLEEP);
18371772Sjl139090 
18381772Sjl139090 		if ((status = prom_get_fcode(dropin_name, fcode)) != 0) {
18391772Sjl139090 
18401772Sjl139090 			if (copyout((void *)fcode, (void *)fcode_virt,
18411772Sjl139090 			    fcode_len)) {
18421772Sjl139090 				cmn_err(CE_WARN, " opl_get_fcode: Unable "
18431772Sjl139090 				    "to copy out fcode image");
18441772Sjl139090 				status = 0;
18451772Sjl139090 			}
18461772Sjl139090 		}
18471772Sjl139090 
18481772Sjl139090 		kmem_free(fcode, fcode_len);
18491772Sjl139090 	}
18501772Sjl139090 
18511772Sjl139090 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
18521772Sjl139090 
18531772Sjl139090 	cp->nresults = fc_int2cell(1);
18541772Sjl139090 	fc_result(cp, 0) = status;
18551772Sjl139090 
18561772Sjl139090 	return (fc_success_op(ap, rp, cp));
18571772Sjl139090 }
18581772Sjl139090 
18591772Sjl139090 static int
18601772Sjl139090 opl_get_fcode_size(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
18611772Sjl139090 {
18621772Sjl139090 	caddr_t		virt;
18631772Sjl139090 	char		*dropin_name;
18641772Sjl139090 	int		len;
18651772Sjl139090 
18661772Sjl139090 	if (fc_cell2int(cp->nargs) != 1)
18671772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 1"));
18681772Sjl139090 
18691772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
18701772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
18711772Sjl139090 
18721772Sjl139090 	virt = fc_cell2ptr(fc_arg(cp, 0));
18731772Sjl139090 
18741772Sjl139090 	dropin_name = kmem_zalloc(FC_SVC_NAME_LEN, KM_SLEEP);
18751772Sjl139090 
18761772Sjl139090 	FC_DEBUG0(1, CE_CONT, "opl_get_fcode_size:\n");
18771772Sjl139090 
18781772Sjl139090 	if (copyinstr(fc_cell2ptr(virt), dropin_name,
18791772Sjl139090 	    FC_SVC_NAME_LEN - 1, NULL))  {
18801772Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: "
18811772Sjl139090 		    "fault copying in drop in name %p\n", virt);
18821772Sjl139090 		len = 0;
18831772Sjl139090 	} else {
18841772Sjl139090 		FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: %s\n", dropin_name);
18851772Sjl139090 
18861772Sjl139090 		len = prom_get_fcode_size(dropin_name);
18871772Sjl139090 	}
18881772Sjl139090 
18891772Sjl139090 	kmem_free(dropin_name, FC_SVC_NAME_LEN);
18901772Sjl139090 
18911772Sjl139090 	FC_DEBUG1(1, CE_CONT, "opl_get_fcode_size: fcode_len = %d\n", len);
18921772Sjl139090 
18931772Sjl139090 	cp->nresults = fc_int2cell(1);
18941772Sjl139090 	fc_result(cp, 0) = len;
18951772Sjl139090 
18961772Sjl139090 	return (fc_success_op(ap, rp, cp));
18971772Sjl139090 }
18981772Sjl139090 
18991772Sjl139090 static int
19001772Sjl139090 opl_map_phys(dev_info_t *dip, struct regspec *phys_spec,
19011772Sjl139090     caddr_t *addrp, ddi_device_acc_attr_t *accattrp,
19021772Sjl139090     ddi_acc_handle_t *handlep)
19031772Sjl139090 {
19041772Sjl139090 	ddi_map_req_t 	mapreq;
19051772Sjl139090 	ddi_acc_hdl_t	*acc_handlep;
19061772Sjl139090 	int		result;
19071772Sjl139090 	struct regspec	*rspecp;
19081772Sjl139090 
19091772Sjl139090 	*handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL);
19101772Sjl139090 	acc_handlep = impl_acc_hdl_get(*handlep);
19111772Sjl139090 	acc_handlep->ah_vers = VERS_ACCHDL;
19121772Sjl139090 	acc_handlep->ah_dip = dip;
19131772Sjl139090 	acc_handlep->ah_rnumber = 0;
19141772Sjl139090 	acc_handlep->ah_offset = 0;
19151772Sjl139090 	acc_handlep->ah_len = 0;
19161772Sjl139090 	acc_handlep->ah_acc = *accattrp;
19171772Sjl139090 	rspecp = kmem_zalloc(sizeof (struct regspec), KM_SLEEP);
19181772Sjl139090 	*rspecp = *phys_spec;
19191772Sjl139090 	/*
19201772Sjl139090 	 * cache a copy of the reg spec
19211772Sjl139090 	 */
19221772Sjl139090 	acc_handlep->ah_bus_private = rspecp;
19231772Sjl139090 
19241772Sjl139090 	mapreq.map_op = DDI_MO_MAP_LOCKED;
19251772Sjl139090 	mapreq.map_type = DDI_MT_REGSPEC;
19261772Sjl139090 	mapreq.map_obj.rp = (struct regspec *)phys_spec;
19271772Sjl139090 	mapreq.map_prot = PROT_READ | PROT_WRITE;
19281772Sjl139090 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
19291772Sjl139090 	mapreq.map_handlep = acc_handlep;
19301772Sjl139090 	mapreq.map_vers = DDI_MAP_VERSION;
19311772Sjl139090 
19321772Sjl139090 	result = ddi_map(dip, &mapreq, 0, 0, addrp);
19331772Sjl139090 
19341772Sjl139090 	if (result != DDI_SUCCESS) {
19351772Sjl139090 		impl_acc_hdl_free(*handlep);
19363354Sjl139090 		kmem_free(rspecp, sizeof (struct regspec));
19371772Sjl139090 		*handlep = (ddi_acc_handle_t)NULL;
19381772Sjl139090 	} else {
19391772Sjl139090 		acc_handlep->ah_addr = *addrp;
19401772Sjl139090 	}
19411772Sjl139090 
19421772Sjl139090 	return (result);
19431772Sjl139090 }
19441772Sjl139090 
19451772Sjl139090 static void
19461772Sjl139090 opl_unmap_phys(ddi_acc_handle_t *handlep)
19471772Sjl139090 {
19481772Sjl139090 	ddi_map_req_t	mapreq;
19491772Sjl139090 	ddi_acc_hdl_t	*acc_handlep;
19501772Sjl139090 	struct regspec	*rspecp;
19511772Sjl139090 
19521772Sjl139090 	acc_handlep = impl_acc_hdl_get(*handlep);
19531772Sjl139090 	ASSERT(acc_handlep);
19541772Sjl139090 	rspecp = acc_handlep->ah_bus_private;
19551772Sjl139090 
19561772Sjl139090 	mapreq.map_op = DDI_MO_UNMAP;
19571772Sjl139090 	mapreq.map_type = DDI_MT_REGSPEC;
19581772Sjl139090 	mapreq.map_obj.rp = (struct regspec *)rspecp;
19591772Sjl139090 	mapreq.map_prot = PROT_READ | PROT_WRITE;
19601772Sjl139090 	mapreq.map_flags = DDI_MF_KERNEL_MAPPING;
19611772Sjl139090 	mapreq.map_handlep = acc_handlep;
19621772Sjl139090 	mapreq.map_vers = DDI_MAP_VERSION;
19631772Sjl139090 
19641772Sjl139090 	(void) ddi_map(acc_handlep->ah_dip, &mapreq, acc_handlep->ah_offset,
19651772Sjl139090 	    acc_handlep->ah_len, &acc_handlep->ah_addr);
19661772Sjl139090 
19671772Sjl139090 	impl_acc_hdl_free(*handlep);
19681772Sjl139090 	/*
19691772Sjl139090 	 * Free the cached copy
19701772Sjl139090 	 */
19711772Sjl139090 	kmem_free(rspecp, sizeof (struct regspec));
19721772Sjl139090 	*handlep = (ddi_acc_handle_t)NULL;
19731772Sjl139090 }
19741772Sjl139090 
19751772Sjl139090 static int
19761772Sjl139090 opl_get_hwd_va(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
19771772Sjl139090 {
19781772Sjl139090 	uint32_t	portid;
19791772Sjl139090 	void		*hwd_virt;
19801772Sjl139090 	hwd_header_t	*hwd_h = NULL;
19811772Sjl139090 	hwd_sb_t	*hwd_sb = NULL;
19821772Sjl139090 	int		lsb, ch, leaf;
19831772Sjl139090 	int		status = 1;
19841772Sjl139090 
19851772Sjl139090 	/* Check the argument */
19861772Sjl139090 	if (fc_cell2int(cp->nargs) != 2)
19871772Sjl139090 		return (fc_syntax_error(cp, "nargs must be 2"));
19881772Sjl139090 
19891772Sjl139090 	if (fc_cell2int(cp->nresults) < 1)
19901772Sjl139090 		return (fc_syntax_error(cp, "nresults must be >= 1"));
19911772Sjl139090 
19921772Sjl139090 	/* Get the parameters */
19931772Sjl139090 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
19941772Sjl139090 	hwd_virt = (void *)fc_cell2ptr(fc_arg(cp, 1));
19951772Sjl139090 
19961772Sjl139090 	/* Get the ID numbers */
19971772Sjl139090 	lsb  = OPL_IO_PORTID_TO_LSB(portid);
19981772Sjl139090 	ch   = OPL_PORTID_TO_CHANNEL(portid);
19991772Sjl139090 	leaf = OPL_PORTID_TO_LEAF(portid);
20001772Sjl139090 	ASSERT(OPL_IO_PORTID(lsb, ch, leaf) == portid);
20011772Sjl139090 
20021772Sjl139090 	/* Set the pointer of hwd. */
20031772Sjl139090 	if ((hwd_h = (hwd_header_t *)opl_boards[lsb].cfg_hwd) == NULL) {
20041772Sjl139090 		return (fc_priv_error(cp, "null hwd header"));
20051772Sjl139090 	}
20061772Sjl139090 	/* Set the pointer of hwd sb. */
20071772Sjl139090 	if ((hwd_sb = (hwd_sb_t *)((char *)hwd_h + hwd_h->hdr_sb_info_offset))
20081772Sjl139090 	    == NULL) {
20091772Sjl139090 		return (fc_priv_error(cp, "null hwd sb"));
20101772Sjl139090 	}
20111772Sjl139090 
20121772Sjl139090 	if (ch == OPL_CMU_CHANNEL) {
20131772Sjl139090 		/* Copyout CMU-CH HW Descriptor */
20141772Sjl139090 		if (copyout((void *)&hwd_sb->sb_cmu.cmu_ch,
20151772Sjl139090 		    (void *)hwd_virt, sizeof (hwd_cmu_chan_t))) {
20161772Sjl139090 			cmn_err(CE_WARN, "opl_get_hwd_va: "
20171772Sjl139090 			"Unable to copy out cmuch descriptor for %x",
20181772Sjl139090 			    portid);
20191772Sjl139090 			status = 0;
20201772Sjl139090 		}
20211772Sjl139090 	} else {
20221772Sjl139090 		/* Copyout PCI-CH HW Descriptor */
20231772Sjl139090 		if (copyout((void *)&hwd_sb->sb_pci_ch[ch].pci_leaf[leaf],
20241772Sjl139090 		    (void *)hwd_virt, sizeof (hwd_leaf_t))) {
20251772Sjl139090 			cmn_err(CE_WARN, "opl_get_hwd_va: "
20261772Sjl139090 			"Unable to copy out pcich descriptor for %x",
20271772Sjl139090 			    portid);
20281772Sjl139090 			status = 0;
20291772Sjl139090 		}
20301772Sjl139090 	}
20311772Sjl139090 
20321772Sjl139090 	cp->nresults = fc_int2cell(1);
20331772Sjl139090 	fc_result(cp, 0) = status;
20341772Sjl139090 
20351772Sjl139090 	return (fc_success_op(ap, rp, cp));
20361772Sjl139090 }
20371772Sjl139090 
20381772Sjl139090 /*
20391982Smv143129  * After Solaris boots, a user can enter OBP using L1A, etc. While in OBP,
20401982Smv143129  * interrupts may be received from PCI devices. These interrupts
20411982Smv143129  * cannot be handled meaningfully since the system is in OBP. These
20421982Smv143129  * interrupts need to be cleared on the CPU side so that the CPU may
20431982Smv143129  * continue with whatever it is doing. Devices that have raised the
20441982Smv143129  * interrupts are expected to reraise the interrupts after sometime
20451982Smv143129  * as they have not been handled. At that time, Solaris will have a
20461982Smv143129  * chance to properly service the interrupts.
20471982Smv143129  *
20481982Smv143129  * The location of the interrupt registers depends on what is present
20491982Smv143129  * at a port. OPL currently supports the Oberon and the CMU channel.
20501982Smv143129  * The following handler handles both kinds of ports and computes
20511982Smv143129  * interrupt register addresses from the specifications and Jupiter Bus
20521982Smv143129  * device bindings.
20531982Smv143129  *
20541982Smv143129  * Fcode drivers install their interrupt handler via a "master-interrupt"
20551982Smv143129  * service. For boot time devices, this takes place within OBP. In the case
20561982Smv143129  * of DR, OPL uses IKP. The Fcode drivers that run within the efcode framework
20571982Smv143129  * attempt to install their handler via the "master-interrupt" service.
20581982Smv143129  * However, we cannot meaningfully install the Fcode driver's handler.
20591982Smv143129  * Instead, we install our own handler in OBP which does the same thing.
20601982Smv143129  *
20611982Smv143129  * Note that the only handling done for interrupts here is to clear it
20621982Smv143129  * on the CPU side. If any device in the future requires more special
20631982Smv143129  * handling, we would have to put in some kind of framework for adding
20641982Smv143129  * device-specific handlers. This is *highly* unlikely, but possible.
20651982Smv143129  *
20661982Smv143129  * Finally, OBP provides a hook called "unix-interrupt-handler" to install
20671982Smv143129  * a Solaris-defined master-interrupt handler for a port. The default
20681982Smv143129  * definition for this method does nothing. Solaris may override this
20691982Smv143129  * with its own definition. This is the way the following handler gets
20701982Smv143129  * control from OBP when interrupts happen at a port after L1A, etc.
20711982Smv143129  */
20721982Smv143129 
20731982Smv143129 static char define_master_interrupt_handler[] =
20741982Smv143129 
20751982Smv143129 /*
20761982Smv143129  * This method translates an Oberon port id to the base (physical) address
20771982Smv143129  * of the interrupt clear registers for that port id.
20781982Smv143129  */
20791982Smv143129 
20801982Smv143129 ": pcich-mid>clear-int-pa   ( mid -- pa ) "
20811982Smv143129 "   dup 1 >> 7 and          ( mid ch# ) "
20821982Smv143129 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
20831982Smv143129 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
20841982Smv143129 "   swap d# 40 << or        ( mid ch# pa ) "
20851982Smv143129 "   swap d# 37 << or        ( mid pa ) "
20861982Smv143129 "   swap 1 and if h# 70.0000 else h# 60.0000 then "
20871982Smv143129 "   or h# 1400 or           ( pa ) "
20881982Smv143129 "; "
20891982Smv143129 
20901982Smv143129 /*
20911982Smv143129  * This method translates a CMU channel port id to the base (physical) address
20921982Smv143129  * of the interrupt clear registers for that port id. There are two classes of
20931982Smv143129  * interrupts that need to be handled for a CMU channel:
20941982Smv143129  *	- obio interrupts
20951982Smv143129  *	- pci interrupts
20961982Smv143129  * So, there are two addresses that need to be computed.
20971982Smv143129  */
20981982Smv143129 
20991982Smv143129 ": cmuch-mid>clear-int-pa   ( mid -- obio-pa pci-pa ) "
21001982Smv143129 "   dup 1 >> 7 and          ( mid ch# ) "
21011982Smv143129 "   over 4 >> h# 1f and     ( mid ch# lsb# ) "
21021982Smv143129 "   1 d# 46 <<              ( mid ch# lsb# pa ) "
21031982Smv143129 "   swap d# 40 << or        ( mid ch# pa ) "
21041982Smv143129 "   swap d# 37 << or        ( mid pa ) "
21051982Smv143129 "   nip dup h# 1800 +       ( pa obio-pa ) "
21061982Smv143129 "   swap h# 1400 +          ( obio-pa pci-pa ) "
21071982Smv143129 "; "
21081982Smv143129 
21091982Smv143129 /*
21101982Smv143129  * This method checks if a given I/O port ID is valid or not.
21111982Smv143129  * For a given LSB,
21121982Smv143129  *	Oberon ports range from 0 - 3
21131982Smv143129  *	CMU ch ports range from 4 - 4
21141982Smv143129  *
21151982Smv143129  * Also, the Oberon supports leaves 0 and 1.
21161982Smv143129  * The CMU ch supports only one leaf, leaf 0.
21171982Smv143129  */
21181982Smv143129 
21191982Smv143129 ": valid-io-mid? ( mid -- flag ) "
21201982Smv143129 "   dup 1 >> 7 and                     ( mid ch# ) "
21211982Smv143129 "   dup 4 > if 2drop false exit then   ( mid ch# ) "
21221982Smv143129 "   4 = swap 1 and 1 = and not "
21231982Smv143129 "; "
21241982Smv143129 
21251982Smv143129 /*
21261982Smv143129  * This method checks if a given port id is a CMU ch.
21271982Smv143129  */
21281982Smv143129 
21291982Smv143129 ": cmuch? ( mid -- flag ) 1 >> 7 and 4 = ; "
21301982Smv143129 
21311982Smv143129 /*
21321982Smv143129  * Given the base address of the array of interrupt clear registers for
21331982Smv143129  * a port id, this method iterates over the given interrupt number bitmap
21341982Smv143129  * and resets the interrupt on the CPU side for every interrupt number
21351982Smv143129  * in the bitmap. Note that physical addresses are used to perform the
21361982Smv143129  * writes, not virtual addresses. This allows the handler to work without
21371982Smv143129  * any involvement from Solaris.
21381982Smv143129  */
21391982Smv143129 
21401982Smv143129 ": clear-ints ( pa bitmap count -- ) "
21411982Smv143129 "   0 do                            ( pa bitmap ) "
21421982Smv143129 "      dup 0= if 2drop unloop exit then "
21431982Smv143129 "      tuck                         ( bitmap pa bitmap ) "
21441982Smv143129 "      1 and if                     ( bitmap pa ) "
21451982Smv143129 "	 dup i 8 * + 0 swap         ( bitmap pa 0 pa' ) "
21461982Smv143129 "	 h# 15 spacex!              ( bitmap pa ) "
21471982Smv143129 "      then                         ( bitmap pa ) "
21481982Smv143129 "      swap 1 >>                    ( pa bitmap ) "
21491982Smv143129 "   loop "
21501982Smv143129 "; "
21511982Smv143129 
21521982Smv143129 /*
21531982Smv143129  * This method replaces the master-interrupt handler in OBP. Once
21541982Smv143129  * this method is plumbed into OBP, OBP transfers control to this
21551982Smv143129  * handler while returning to Solaris from OBP after L1A. This method's
21561982Smv143129  * task is to simply reset received interrupts on the CPU side.
21571982Smv143129  * When the devices reassert the interrupts later, Solaris will
21581982Smv143129  * be able to see them and handle them.
21591982Smv143129  *
21601982Smv143129  * For each port ID that has interrupts, this method is called
21611982Smv143129  * once by OBP. The input arguments are:
21621982Smv143129  *	mid	portid
21631982Smv143129  *	bitmap	bitmap of interrupts that have happened
21641982Smv143129  *
21651982Smv143129  * This method returns true, if it is able to handle the interrupts.
21661982Smv143129  * OBP does nothing further.
21671982Smv143129  *
21681982Smv143129  * This method returns false, if it encountered a problem. Currently,
21691982Smv143129  * the only problem could be an invalid port id. OBP needs to do
21701982Smv143129  * its own processing in that case. If this method returns false,
21711982Smv143129  * it preserves the mid and bitmap arguments for OBP.
21721982Smv143129  */
21731982Smv143129 
21741982Smv143129 ": unix-resend-mondos ( mid bitmap -- [ mid bitmap false ] | true ) "
21751982Smv143129 
21761982Smv143129 /*
21771982Smv143129  * Uncomment the following line if you want to display the input arguments.
21781982Smv143129  * This is meant for debugging.
21791982Smv143129  * "   .\" Bitmap=\" dup u. .\" MID=\" over u. cr "
21801982Smv143129  */
21811982Smv143129 
21821982Smv143129 /*
21831982Smv143129  * If the port id is not valid (according to the Oberon and CMU ch
21841982Smv143129  * specifications, then return false to OBP to continue further
21851982Smv143129  * processing.
21861982Smv143129  */
21871982Smv143129 
21881982Smv143129 "   over valid-io-mid? not if       ( mid bitmap ) "
21891982Smv143129 "      false exit "
21901982Smv143129 "   then "
21911982Smv143129 
21921982Smv143129 /*
21931982Smv143129  * If the port is a CMU ch, then the 64-bit bitmap represents
21941982Smv143129  * 2 32-bit bitmaps:
21951982Smv143129  *	- obio interrupt bitmap (20 bits)
21961982Smv143129  *	- pci interrupt bitmap (32 bits)
21971982Smv143129  *
21981982Smv143129  * - Split the bitmap into two
21991982Smv143129  * - Compute the base addresses of the interrupt clear registers
22001982Smv143129  *   for both pci interrupts and obio interrupts
22011982Smv143129  * - Clear obio interrupts
22021982Smv143129  * - Clear pci interrupts
22031982Smv143129  */
22041982Smv143129 
22051982Smv143129 "   over cmuch? if                  ( mid bitmap ) "
22061982Smv143129 "      xlsplit                      ( mid pci-bit obio-bit ) "
22071982Smv143129 "      rot cmuch-mid>clear-int-pa   ( pci-bit obio-bit obio-pa pci-pa ) "
22081982Smv143129 "      >r                           ( pci-bit obio-bit obio-pa ) ( r: pci-pa ) "
22091982Smv143129 "      swap d# 20 clear-ints        ( pci-bit ) ( r: pci-pa ) "
22101982Smv143129 "      r> swap d# 32 clear-ints     (  ) ( r: ) "
22111982Smv143129 
22121982Smv143129 /*
22131982Smv143129  * If the port is an Oberon, then the 64-bit bitmap is used fully.
22141982Smv143129  *
22151982Smv143129  * - Compute the base address of the interrupt clear registers
22161982Smv143129  * - Clear interrupts
22171982Smv143129  */
22181982Smv143129 
22191982Smv143129 "   else                            ( mid bitmap ) "
22201982Smv143129 "      swap pcich-mid>clear-int-pa  ( bitmap pa ) "
22211982Smv143129 "      swap d# 64 clear-ints        (  ) "
22221982Smv143129 "   then "
22231982Smv143129 
22241982Smv143129 /*
22251982Smv143129  * Always return true from here.
22261982Smv143129  */
22271982Smv143129 
22281982Smv143129 "   true                            ( true ) "
22291982Smv143129 "; "
22301982Smv143129 ;
22311982Smv143129 
22321982Smv143129 static char	install_master_interrupt_handler[] =
22331982Smv143129 	"' unix-resend-mondos to unix-interrupt-handler";
22341982Smv143129 static char	handler[] = "unix-interrupt-handler";
22351982Smv143129 static char	handler_defined[] = "p\" %s\" find nip swap l! ";
22361982Smv143129 
22371982Smv143129 /*ARGSUSED*/
22381982Smv143129 static int
22391982Smv143129 master_interrupt_init(uint32_t portid, uint32_t xt)
22401982Smv143129 {
22411982Smv143129 	uint_t	defined;
22421982Smv143129 	char	buf[sizeof (handler) + sizeof (handler_defined)];
22431982Smv143129 
22441982Smv143129 	if (master_interrupt_inited)
22451982Smv143129 		return (1);
22461982Smv143129 
22471982Smv143129 	/*
22481982Smv143129 	 * Check if the defer word "unix-interrupt-handler" is defined.
22491982Smv143129 	 * This must be defined for OPL systems. So, this is only a
22501982Smv143129 	 * sanity check.
22511982Smv143129 	 */
22521982Smv143129 	(void) sprintf(buf, handler_defined, handler);
22531982Smv143129 	prom_interpret(buf, (uintptr_t)&defined, 0, 0, 0, 0);
22541982Smv143129 	if (!defined) {
22551982Smv143129 		cmn_err(CE_WARN, "master_interrupt_init: "
22561982Smv143129 		    "%s is not defined\n", handler);
22571982Smv143129 		return (0);
22581982Smv143129 	}
22591982Smv143129 
22601982Smv143129 	/*
22611982Smv143129 	 * Install the generic master-interrupt handler. Note that
22621982Smv143129 	 * this is only done one time on the first DR operation.
22631982Smv143129 	 * This is because, for OPL, one, single generic handler
22641982Smv143129 	 * handles all ports (Oberon and CMU channel) and all
22651982Smv143129 	 * interrupt sources within each port.
22661982Smv143129 	 *
22671982Smv143129 	 * The current support is only for the Oberon and CMU-channel.
22681982Smv143129 	 * If any others need to be supported, the handler has to be
22691982Smv143129 	 * modified accordingly.
22701982Smv143129 	 */
22711982Smv143129 
22721982Smv143129 	/*
22731982Smv143129 	 * Define the OPL master interrupt handler
22741982Smv143129 	 */
22751982Smv143129 	prom_interpret(define_master_interrupt_handler, 0, 0, 0, 0, 0);
22761982Smv143129 
22771982Smv143129 	/*
22781982Smv143129 	 * Take over the master interrupt handler from OBP.
22791982Smv143129 	 */
22801982Smv143129 	prom_interpret(install_master_interrupt_handler, 0, 0, 0, 0, 0);
22811982Smv143129 
22821982Smv143129 	master_interrupt_inited = 1;
22831982Smv143129 
22841982Smv143129 	/*
22851982Smv143129 	 * prom_interpret() does not return a status. So, we assume
22861982Smv143129 	 * that the calls succeeded. In reality, the calls may fail
22871982Smv143129 	 * if there is a syntax error, etc in the strings.
22881982Smv143129 	 */
22891982Smv143129 
22901982Smv143129 	return (1);
22911982Smv143129 }
22921982Smv143129 
22931982Smv143129 /*
22941982Smv143129  * Install the master-interrupt handler for a device.
22951982Smv143129  */
22961982Smv143129 static int
22971982Smv143129 opl_master_interrupt(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
22981982Smv143129 {
22991982Smv143129 	uint32_t	portid, xt;
23001982Smv143129 	int		board, channel, leaf;
23011982Smv143129 	int		status;
23021982Smv143129 
23031982Smv143129 	/* Check the argument */
23041982Smv143129 	if (fc_cell2int(cp->nargs) != 2)
23051982Smv143129 		return (fc_syntax_error(cp, "nargs must be 2"));
23061982Smv143129 
23071982Smv143129 	if (fc_cell2int(cp->nresults) < 1)
23081982Smv143129 		return (fc_syntax_error(cp, "nresults must be >= 1"));
23091982Smv143129 
23101982Smv143129 	/* Get the parameters */
23111982Smv143129 	portid = fc_cell2uint32_t(fc_arg(cp, 0));
23121982Smv143129 	xt = fc_cell2uint32_t(fc_arg(cp, 1));
23131982Smv143129 
23141982Smv143129 	board = OPL_IO_PORTID_TO_LSB(portid);
23151982Smv143129 	channel = OPL_PORTID_TO_CHANNEL(portid);
23161982Smv143129 	leaf = OPL_PORTID_TO_LEAF(portid);
23171982Smv143129 
23181982Smv143129 	if ((board >= HWD_SBS_PER_DOMAIN) || !OPL_VALID_CHANNEL(channel) ||
23191982Smv143129 	    (OPL_OBERON_CHANNEL(channel) && !OPL_VALID_LEAF(leaf)) ||
23201982Smv143129 	    ((channel == OPL_CMU_CHANNEL) && (leaf != 0))) {
23211982Smv143129 		FC_DEBUG1(1, CE_CONT, "opl_master_interrupt: invalid port %x\n",
23221982Smv143129 		    portid);
23231982Smv143129 		status = 0;
23241982Smv143129 	} else {
23251982Smv143129 		status = master_interrupt_init(portid, xt);
23261982Smv143129 	}
23271982Smv143129 
23281982Smv143129 	cp->nresults = fc_int2cell(1);
23291982Smv143129 	fc_result(cp, 0) = status;
23301982Smv143129 
23311982Smv143129 	return (fc_success_op(ap, rp, cp));
23321982Smv143129 }
23331982Smv143129 
23341982Smv143129 /*
23351772Sjl139090  * Set the properties for a leaf node (Oberon leaf or CMU channel leaf).
23361772Sjl139090  */
23371772Sjl139090 /*ARGSUSED*/
23381772Sjl139090 static int
23391772Sjl139090 opl_create_leaf(dev_info_t *node, void *arg, uint_t flags)
23401772Sjl139090 {
23411772Sjl139090 	int ret;
23421772Sjl139090 
23431772Sjl139090 	OPL_UPDATE_PROP(string, node, "name", OPL_PCI_LEAF_NODE);
23441772Sjl139090 
23451772Sjl139090 	OPL_UPDATE_PROP(string, node, "status", "okay");
23461772Sjl139090 
23471772Sjl139090 	return (DDI_WALK_TERMINATE);
23481772Sjl139090 }
23491772Sjl139090 
23501772Sjl139090 static char *
23511772Sjl139090 opl_get_probe_string(opl_probe_t *probe, int channel, int leaf)
23521772Sjl139090 {
23531772Sjl139090 	char 		*probe_string;
23541772Sjl139090 	int		portid;
23551772Sjl139090 
23561772Sjl139090 	probe_string = kmem_zalloc(PROBE_STR_SIZE, KM_SLEEP);
23571772Sjl139090 
23581772Sjl139090 	if (channel == OPL_CMU_CHANNEL)
23591772Sjl139090 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
23601772Sjl139090 	else
23611772Sjl139090 		portid = probe->
23621772Sjl139090 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
23631772Sjl139090 
23641772Sjl139090 	(void) sprintf(probe_string, "%x", portid);
23651772Sjl139090 
23661772Sjl139090 	return (probe_string);
23671772Sjl139090 }
23681772Sjl139090 
23691772Sjl139090 static int
23701772Sjl139090 opl_probe_leaf(opl_probe_t *probe)
23711772Sjl139090 {
23721772Sjl139090 	int		channel, leaf, portid, error, circ;
23731772Sjl139090 	int		board;
23741772Sjl139090 	fco_handle_t	fco_handle, *cfg_handle;
23751772Sjl139090 	dev_info_t	*parent, *leaf_node;
23761772Sjl139090 	char		unit_address[UNIT_ADDR_SIZE];
23771772Sjl139090 	char		*probe_string;
23781772Sjl139090 	opl_board_cfg_t	*board_cfg;
23791772Sjl139090 
23801772Sjl139090 	board = probe->pr_board;
23811772Sjl139090 	channel = probe->pr_channel;
23821772Sjl139090 	leaf = probe->pr_leaf;
23831772Sjl139090 	parent = ddi_root_node();
23841772Sjl139090 	board_cfg = &opl_boards[board];
23851772Sjl139090 
23861772Sjl139090 	ASSERT(OPL_VALID_CHANNEL(channel));
23871772Sjl139090 	ASSERT(OPL_VALID_LEAF(leaf));
23881772Sjl139090 
23891772Sjl139090 	if (channel == OPL_CMU_CHANNEL) {
23901772Sjl139090 		portid = probe->pr_sb->sb_cmu.cmu_ch.chan_portid;
23911772Sjl139090 		cfg_handle = &board_cfg->cfg_cmuch_handle;
23921772Sjl139090 	} else {
23931772Sjl139090 		portid = probe->
23941772Sjl139090 		    pr_sb->sb_pci_ch[channel].pci_leaf[leaf].leaf_port_id;
23951772Sjl139090 		cfg_handle = &board_cfg->cfg_pcich_handle[channel][leaf];
23961772Sjl139090 	}
23971772Sjl139090 
23981772Sjl139090 	/*
23991772Sjl139090 	 * Prevent any changes to leaf_node until we have bound
24001772Sjl139090 	 * it to the correct driver.
24011772Sjl139090 	 */
24021772Sjl139090 	ndi_devi_enter(parent, &circ);
24031772Sjl139090 
24041772Sjl139090 	/*
24051772Sjl139090 	 * Ideally, fcode would be run from the "sid_branch_create"
24061772Sjl139090 	 * callback (that is the primary purpose of that callback).
24071772Sjl139090 	 * However, the fcode interpreter was written with the
24081772Sjl139090 	 * assumption that the "new_child" was linked into the
24091772Sjl139090 	 * device tree. The callback is invoked with the devinfo node
24101772Sjl139090 	 * in the DS_PROTO state. More investigation is needed before
24111772Sjl139090 	 * we can invoke the interpreter from the callback. For now,
24121772Sjl139090 	 * we create the "new_child" in the BOUND state, invoke the
24131772Sjl139090 	 * fcode interpreter and then rebind the dip to use any
24141772Sjl139090 	 * compatible properties created by fcode.
24151772Sjl139090 	 */
24161772Sjl139090 
24171772Sjl139090 	probe->pr_parent = parent;
24181772Sjl139090 	probe->pr_create = opl_create_leaf;
24191772Sjl139090 	probe->pr_hold = 1;
24201772Sjl139090 
24211772Sjl139090 	leaf_node = opl_create_node(probe);
24221772Sjl139090 	if (leaf_node == NULL) {
24231772Sjl139090 
24241772Sjl139090 		cmn_err(CE_WARN, "IKP: create leaf (%d-%d-%d) failed",
24251772Sjl139090 			probe->pr_board, probe->pr_channel, probe->pr_leaf);
24261772Sjl139090 		ndi_devi_exit(parent, circ);
24271772Sjl139090 		return (-1);
24281772Sjl139090 	}
24291772Sjl139090 
24301772Sjl139090 	/*
24311772Sjl139090 	 * The platform DR interfaces created the dip in
24321772Sjl139090 	 * bound state. Bring devinfo node down to linked
24331772Sjl139090 	 * state and hold it there until compatible
24341772Sjl139090 	 * properties are created.
24351772Sjl139090 	 */
24361772Sjl139090 	e_ddi_branch_rele(leaf_node);
24371772Sjl139090 	(void) i_ndi_unconfig_node(leaf_node, DS_LINKED, 0);
24381772Sjl139090 	ASSERT(i_ddi_node_state(leaf_node) == DS_LINKED);
24391772Sjl139090 	e_ddi_branch_hold(leaf_node);
24401772Sjl139090 
24411772Sjl139090 	mutex_enter(&DEVI(leaf_node)->devi_lock);
24421772Sjl139090 	DEVI(leaf_node)->devi_flags |= DEVI_NO_BIND;
24431772Sjl139090 	mutex_exit(&DEVI(leaf_node)->devi_lock);
24441772Sjl139090 
24451772Sjl139090 	/*
24461772Sjl139090 	 * Drop the busy-hold on parent before calling
24471772Sjl139090 	 * fcode_interpreter to prevent potential deadlocks
24481772Sjl139090 	 */
24491772Sjl139090 	ndi_devi_exit(parent, circ);
24501772Sjl139090 
24511772Sjl139090 	(void) sprintf(unit_address, "%x", portid);
24521772Sjl139090 
24531772Sjl139090 	/*
24541772Sjl139090 	 * Get the probe string
24551772Sjl139090 	 */
24561772Sjl139090 	probe_string = opl_get_probe_string(probe, channel, leaf);
24571772Sjl139090 
24581772Sjl139090 	/*
24591772Sjl139090 	 * The fcode pointer specified here is NULL and the fcode
24601772Sjl139090 	 * size specified here is 0. This causes the user-level
24611772Sjl139090 	 * fcode interpreter to issue a request to the fcode
24621772Sjl139090 	 * driver to get the Oberon/cmu-ch fcode.
24631772Sjl139090 	 */
24641772Sjl139090 	fco_handle = opl_fc_ops_alloc_handle(parent, leaf_node,
24651772Sjl139090 	    NULL, 0, unit_address, probe_string);
24661772Sjl139090 
24671772Sjl139090 	error = fcode_interpreter(parent, &opl_fc_do_op, fco_handle);
24681772Sjl139090 
24691772Sjl139090 	if (error != 0) {
24701772Sjl139090 		cmn_err(CE_WARN, "IKP: Unable to probe PCI leaf (%d-%d-%d)",
24711772Sjl139090 			probe->pr_board, probe->pr_channel, probe->pr_leaf);
24721772Sjl139090 
24731772Sjl139090 		opl_fc_ops_free_handle(fco_handle);
24741772Sjl139090 
24751772Sjl139090 		if (probe_string != NULL)
24761772Sjl139090 			kmem_free(probe_string, PROBE_STR_SIZE);
24771772Sjl139090 
24781772Sjl139090 		(void) opl_destroy_node(leaf_node);
24791772Sjl139090 	} else {
24801772Sjl139090 		*cfg_handle = fco_handle;
24811772Sjl139090 
24821772Sjl139090 		if (channel == OPL_CMU_CHANNEL)
24831772Sjl139090 			board_cfg->cfg_cmuch_probe_str = probe_string;
24841772Sjl139090 		else
24851772Sjl139090 			board_cfg->cfg_pcich_probe_str[channel][leaf]
24861772Sjl139090 			    = probe_string;
24871772Sjl139090 
24881772Sjl139090 		/*
24891772Sjl139090 		 * Compatible properties (if any) have been created,
24901772Sjl139090 		 * so bind driver.
24911772Sjl139090 		 */
24921772Sjl139090 		ndi_devi_enter(parent, &circ);
24931772Sjl139090 		ASSERT(i_ddi_node_state(leaf_node) <= DS_LINKED);
24941772Sjl139090 
24951772Sjl139090 		mutex_enter(&DEVI(leaf_node)->devi_lock);
24961772Sjl139090 		DEVI(leaf_node)->devi_flags &= ~DEVI_NO_BIND;
24971772Sjl139090 		mutex_exit(&DEVI(leaf_node)->devi_lock);
24981772Sjl139090 
24991772Sjl139090 		ndi_devi_exit(parent, circ);
25001772Sjl139090 
25011772Sjl139090 		if (ndi_devi_bind_driver(leaf_node, 0) !=
25021772Sjl139090 			DDI_SUCCESS) {
25031772Sjl139090 			cmn_err(CE_WARN,
25041772Sjl139090 				"IKP: Unable to bind PCI leaf (%d-%d-%d)",
25051772Sjl139090 				probe->pr_board, probe->pr_channel,
25061772Sjl139090 				probe->pr_leaf);
25071772Sjl139090 		}
25081772Sjl139090 	}
25091772Sjl139090 
25101772Sjl139090 	if ((error != 0) && (channel == OPL_CMU_CHANNEL))
25111772Sjl139090 		return (-1);
25121772Sjl139090 
25131772Sjl139090 	return (0);
25141772Sjl139090 }
25151772Sjl139090 
25161772Sjl139090 static void
25171772Sjl139090 opl_init_leaves(int myboard)
25181772Sjl139090 {
25191772Sjl139090 	dev_info_t	*parent, *node;
25201772Sjl139090 	char		*name;
25211772Sjl139090 	int 		circ, ret;
25221772Sjl139090 	int		len, portid, board, channel, leaf;
25231772Sjl139090 	opl_board_cfg_t	*cfg;
25241772Sjl139090 
25251772Sjl139090 	parent = ddi_root_node();
25261772Sjl139090 
25271772Sjl139090 	/*
25281772Sjl139090 	 * Hold parent node busy to walk its child list
25291772Sjl139090 	 */
25301772Sjl139090 	ndi_devi_enter(parent, &circ);
25311772Sjl139090 
25321772Sjl139090 	for (node = ddi_get_child(parent);
25331772Sjl139090 		(node != NULL);
25341772Sjl139090 		node = ddi_get_next_sibling(node)) {
25351772Sjl139090 
25361772Sjl139090 		ret = OPL_GET_PROP(string, node, "name", &name, &len);
25371772Sjl139090 		if (ret != DDI_PROP_SUCCESS) {
25381772Sjl139090 			/*
25391772Sjl139090 			 * The property does not exist for this node.
25401772Sjl139090 			 */
25411772Sjl139090 			continue;
25421772Sjl139090 		}
25431772Sjl139090 
25441772Sjl139090 		if (strncmp(name, OPL_PCI_LEAF_NODE, len) == 0) {
25451772Sjl139090 
25461772Sjl139090 			ret = OPL_GET_PROP(int, node, "portid", &portid, -1);
25471772Sjl139090 			if (ret == DDI_PROP_SUCCESS) {
25481772Sjl139090 
25491772Sjl139090 				ret = OPL_GET_PROP(int, node, "board#",
25501772Sjl139090 				    &board, -1);
25511772Sjl139090 				if ((ret != DDI_PROP_SUCCESS) ||
25523354Sjl139090 				    (board != myboard)) {
25533354Sjl139090 					kmem_free(name, len);
25541772Sjl139090 					continue;
25553354Sjl139090 				}
25561772Sjl139090 
25571772Sjl139090 				cfg = &opl_boards[board];
25581772Sjl139090 				channel = OPL_PORTID_TO_CHANNEL(portid);
25591772Sjl139090 				if (channel == OPL_CMU_CHANNEL) {
25601772Sjl139090 
25611772Sjl139090 					if (cfg->cfg_cmuch_handle != NULL)
25621772Sjl139090 						cfg->cfg_cmuch_leaf = node;
25631772Sjl139090 
25641772Sjl139090 				} else {
25651772Sjl139090 
25661772Sjl139090 					leaf = OPL_PORTID_TO_LEAF(portid);
25671772Sjl139090 					if (cfg->cfg_pcich_handle
25681772Sjl139090 						[channel][leaf] != NULL)
25691772Sjl139090 						cfg->cfg_pcich_leaf
25701772Sjl139090 							[channel][leaf] = node;
25711772Sjl139090 				}
25721772Sjl139090 			}
25731772Sjl139090 		}
25741772Sjl139090 
25751772Sjl139090 		kmem_free(name, len);
25761772Sjl139090 		if (ret != DDI_PROP_SUCCESS)
25771772Sjl139090 			break;
25781772Sjl139090 	}
25791772Sjl139090 
25801772Sjl139090 	ndi_devi_exit(parent, circ);
25811772Sjl139090 }
25821772Sjl139090 
25831772Sjl139090 /*
25841772Sjl139090  * Create "pci" node and hierarchy for the Oberon channels and the
25851772Sjl139090  * CMU channel.
25861772Sjl139090  */
25871772Sjl139090 /*ARGSUSED*/
25881772Sjl139090 static int
25891772Sjl139090 opl_probe_io(opl_probe_t *probe)
25901772Sjl139090 {
25911772Sjl139090 
25921772Sjl139090 	int		i, j;
25931772Sjl139090 	hwd_pci_ch_t	*channels;
25941772Sjl139090 
25951772Sjl139090 	if (HWD_STATUS_OK(probe->pr_sb->sb_cmu.cmu_ch.chan_status)) {
25961772Sjl139090 
25971772Sjl139090 		probe->pr_channel = HWD_CMU_CHANNEL;
25981772Sjl139090 		probe->pr_channel_status =
25991772Sjl139090 		    probe->pr_sb->sb_cmu.cmu_ch.chan_status;
26001772Sjl139090 		probe->pr_leaf = 0;
26011772Sjl139090 		probe->pr_leaf_status = probe->pr_channel_status;
26021772Sjl139090 
26031772Sjl139090 		if (opl_probe_leaf(probe) != 0)
26041772Sjl139090 			return (-1);
26051772Sjl139090 	}
26061772Sjl139090 
26071772Sjl139090 	channels = &probe->pr_sb->sb_pci_ch[0];
26081772Sjl139090 
26091772Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
26101772Sjl139090 
26111772Sjl139090 		if (!HWD_STATUS_OK(channels[i].pci_status))
26121772Sjl139090 			continue;
26131772Sjl139090 
26141772Sjl139090 		probe->pr_channel = i;
26151772Sjl139090 		probe->pr_channel_status = channels[i].pci_status;
26161772Sjl139090 
26171772Sjl139090 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
26181772Sjl139090 
26191772Sjl139090 			probe->pr_leaf = j;
26201772Sjl139090 			probe->pr_leaf_status =
26211772Sjl139090 				channels[i].pci_leaf[j].leaf_status;
26221772Sjl139090 
26231772Sjl139090 			if (!HWD_STATUS_OK(probe->pr_leaf_status))
26241772Sjl139090 				continue;
26251772Sjl139090 
26261772Sjl139090 			(void) opl_probe_leaf(probe);
26271772Sjl139090 		}
26281772Sjl139090 	}
26291772Sjl139090 	opl_init_leaves(probe->pr_board);
26301772Sjl139090 	return (0);
26311772Sjl139090 }
26321772Sjl139090 
26331772Sjl139090 /*
26341772Sjl139090  * Perform the probe in the following order:
26351772Sjl139090  *
26361772Sjl139090  *	processors
26371772Sjl139090  *	memory
26381772Sjl139090  *	IO
26391772Sjl139090  *
26401772Sjl139090  * Each probe function returns 0 on sucess and a non-zero value on failure.
26411772Sjl139090  * What is a failure is determined by the implementor of the probe function.
26421772Sjl139090  * For example, while probing CPUs, any error encountered during probe
26431772Sjl139090  * is considered a failure and causes the whole probe operation to fail.
26441772Sjl139090  * However, for I/O, an error encountered while probing one device
26451772Sjl139090  * should not prevent other devices from being probed. It should not cause
26461772Sjl139090  * the whole probe operation to fail.
26471772Sjl139090  */
26481772Sjl139090 int
26491772Sjl139090 opl_probe_sb(int board)
26501772Sjl139090 {
26511772Sjl139090 	opl_probe_t	*probe;
26521772Sjl139090 	int		ret;
26531772Sjl139090 
26541772Sjl139090 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
26551772Sjl139090 		return (-1);
26561772Sjl139090 
26571772Sjl139090 	ASSERT(opl_cfg_inited != 0);
26581772Sjl139090 
26591772Sjl139090 	/*
26601772Sjl139090 	 * If the previous probe failed and left a partially configured
26611772Sjl139090 	 * board, we need to unprobe the board and start with a clean slate.
26621772Sjl139090 	 */
26631772Sjl139090 	if ((opl_boards[board].cfg_hwd != NULL) &&
26641772Sjl139090 	    (opl_unprobe_sb(board) != 0))
26651772Sjl139090 		return (-1);
26661772Sjl139090 
26671772Sjl139090 	ret = 0;
26681772Sjl139090 
26691772Sjl139090 	probe = kmem_zalloc(sizeof (opl_probe_t), KM_SLEEP);
26701772Sjl139090 	probe->pr_board = board;
26711772Sjl139090 
26721772Sjl139090 	if ((opl_probe_init(probe) != 0) ||
26731772Sjl139090 
26741772Sjl139090 	    (opl_probe_cpu_chips(probe) != 0) ||
26751772Sjl139090 
26761772Sjl139090 	    (opl_probe_memory(probe) != 0) ||
26771772Sjl139090 
26781772Sjl139090 	    (opl_probe_io(probe) != 0)) {
26791772Sjl139090 
26801772Sjl139090 		/*
26811772Sjl139090 		 * Probe failed. Perform cleanup.
26821772Sjl139090 		 */
26831772Sjl139090 		(void) opl_unprobe_sb(board);
26841772Sjl139090 		ret = -1;
26851772Sjl139090 	}
26861772Sjl139090 
26871772Sjl139090 	kmem_free(probe, sizeof (opl_probe_t));
26881772Sjl139090 
26891772Sjl139090 	return (ret);
26901772Sjl139090 }
26911772Sjl139090 
26921772Sjl139090 /*
26931772Sjl139090  * This unprobing also includes CMU-CH.
26941772Sjl139090  */
26951772Sjl139090 /*ARGSUSED*/
26961772Sjl139090 static int
26971772Sjl139090 opl_unprobe_io(int board)
26981772Sjl139090 {
26991772Sjl139090 	int		i, j, ret;
27001772Sjl139090 	opl_board_cfg_t	*board_cfg;
27011772Sjl139090 	dev_info_t	**node;
27021772Sjl139090 	fco_handle_t	*hand;
27031772Sjl139090 	char		**probe_str;
27041772Sjl139090 
27051772Sjl139090 	board_cfg = &opl_boards[board];
27061772Sjl139090 
27071772Sjl139090 	for (i = 0; i < HWD_PCI_CHANNELS_PER_SB; i++) {
27081772Sjl139090 
27091772Sjl139090 		for (j = 0; j < HWD_LEAVES_PER_PCI_CHANNEL; j++) {
27101772Sjl139090 
27111772Sjl139090 			node = &board_cfg->cfg_pcich_leaf[i][j];
27121772Sjl139090 			hand = &board_cfg->cfg_pcich_handle[i][j];
27131772Sjl139090 			probe_str = &board_cfg->cfg_pcich_probe_str[i][j];
27141772Sjl139090 
27151772Sjl139090 			if (*node == NULL)
27161772Sjl139090 				continue;
27171772Sjl139090 
27181772Sjl139090 			if (*hand != NULL) {
27191772Sjl139090 				opl_fc_ops_free_handle(*hand);
27201772Sjl139090 				*hand = NULL;
27211772Sjl139090 			}
27221772Sjl139090 
27231772Sjl139090 			if (*probe_str != NULL) {
27241772Sjl139090 				kmem_free(*probe_str, PROBE_STR_SIZE);
27251772Sjl139090 				*probe_str = NULL;
27261772Sjl139090 			}
27271772Sjl139090 
27281772Sjl139090 			ret = opl_destroy_node(*node);
27291772Sjl139090 			if (ret != 0) {
27301772Sjl139090 
27311772Sjl139090 				cmn_err(CE_WARN,
27321772Sjl139090 					"IKP: destroy pci (%d-%d-%d) failed",
27331772Sjl139090 					board, i, j);
27341772Sjl139090 				return (-1);
27351772Sjl139090 			}
27361772Sjl139090 
27371772Sjl139090 			*node = NULL;
27381772Sjl139090 
27391772Sjl139090 		}
27401772Sjl139090 	}
27411772Sjl139090 
27421772Sjl139090 	node = &board_cfg->cfg_cmuch_leaf;
27431772Sjl139090 	hand = &board_cfg->cfg_cmuch_handle;
27441772Sjl139090 	probe_str = &board_cfg->cfg_cmuch_probe_str;
27451772Sjl139090 
27461772Sjl139090 	if (*node == NULL)
27471772Sjl139090 		return (0);
27481772Sjl139090 
27491772Sjl139090 	if (*hand != NULL) {
27501772Sjl139090 		opl_fc_ops_free_handle(*hand);
27511772Sjl139090 		*hand = NULL;
27521772Sjl139090 	}
27531772Sjl139090 
27541772Sjl139090 	if (*probe_str != NULL) {
27551772Sjl139090 		kmem_free(*probe_str, PROBE_STR_SIZE);
27561772Sjl139090 		*probe_str = NULL;
27571772Sjl139090 	}
27581772Sjl139090 
27591772Sjl139090 	if (opl_destroy_node(*node) != 0) {
27601772Sjl139090 
27611772Sjl139090 		cmn_err(CE_WARN, "IKP: destroy pci (%d-%d-%d) failed",
27621772Sjl139090 			board, OPL_CMU_CHANNEL, 0);
27631772Sjl139090 		return (-1);
27641772Sjl139090 	}
27651772Sjl139090 
27661772Sjl139090 	*node = NULL;
27671772Sjl139090 
27681772Sjl139090 	return (0);
27691772Sjl139090 }
27701772Sjl139090 
27711772Sjl139090 /*
27721772Sjl139090  * Destroy the "pseudo-mc" node for a board.
27731772Sjl139090  */
27741772Sjl139090 static int
27751772Sjl139090 opl_unprobe_memory(int board)
27761772Sjl139090 {
27771772Sjl139090 	opl_board_cfg_t	*board_cfg;
27781772Sjl139090 
27791772Sjl139090 	board_cfg = &opl_boards[board];
27801772Sjl139090 
27811772Sjl139090 	if (board_cfg->cfg_pseudo_mc == NULL)
27821772Sjl139090 		return (0);
27831772Sjl139090 
27841772Sjl139090 	if (opl_destroy_node(board_cfg->cfg_pseudo_mc) != 0) {
27851772Sjl139090 
27861772Sjl139090 		cmn_err(CE_WARN, "IKP: destroy pseudo-mc (%d) failed", board);
27871772Sjl139090 		return (-1);
27881772Sjl139090 	}
27891772Sjl139090 
27901772Sjl139090 	board_cfg->cfg_pseudo_mc = NULL;
27911772Sjl139090 
27921772Sjl139090 	return (0);
27931772Sjl139090 }
27941772Sjl139090 
27951772Sjl139090 /*
27961772Sjl139090  * Destroy the "cmp" nodes for a board. This also destroys the "core"
27971772Sjl139090  * and "cpu" nodes below the "cmp" nodes.
27981772Sjl139090  */
27991772Sjl139090 static int
28001772Sjl139090 opl_unprobe_processors(int board)
28011772Sjl139090 {
28021772Sjl139090 	int		i;
28031772Sjl139090 	dev_info_t	**cfg_cpu_chips;
28041772Sjl139090 
28051772Sjl139090 	cfg_cpu_chips = opl_boards[board].cfg_cpu_chips;
28061772Sjl139090 
28071772Sjl139090 	for (i = 0; i < HWD_CPU_CHIPS_PER_CMU; i++) {
28081772Sjl139090 
28091772Sjl139090 		if (cfg_cpu_chips[i] == NULL)
28101772Sjl139090 			continue;
28111772Sjl139090 
28121772Sjl139090 		if (opl_destroy_node(cfg_cpu_chips[i]) != 0) {
28131772Sjl139090 
28141772Sjl139090 			cmn_err(CE_WARN,
28151772Sjl139090 				"IKP: destroy chip (%d-%d) failed", board, i);
28161772Sjl139090 			return (-1);
28171772Sjl139090 		}
28181772Sjl139090 
28191772Sjl139090 		cfg_cpu_chips[i] = NULL;
28201772Sjl139090 	}
28211772Sjl139090 
28221772Sjl139090 	return (0);
28231772Sjl139090 }
28241772Sjl139090 
28251772Sjl139090 /*
28261772Sjl139090  * Perform the unprobe in the following order:
28271772Sjl139090  *
28281772Sjl139090  *	IO
28291772Sjl139090  *	memory
28301772Sjl139090  *	processors
28311772Sjl139090  */
28321772Sjl139090 int
28331772Sjl139090 opl_unprobe_sb(int board)
28341772Sjl139090 {
28351772Sjl139090 	if ((board < 0) || (board >= HWD_SBS_PER_DOMAIN))
28361772Sjl139090 		return (-1);
28371772Sjl139090 
28381772Sjl139090 	ASSERT(opl_cfg_inited != 0);
28391772Sjl139090 
28401772Sjl139090 	if ((opl_unprobe_io(board) != 0) ||
28411772Sjl139090 
28421772Sjl139090 	    (opl_unprobe_memory(board) != 0) ||
28431772Sjl139090 
28441772Sjl139090 	    (opl_unprobe_processors(board) != 0))
28451772Sjl139090 
28461772Sjl139090 		return (-1);
28471772Sjl139090 
28481772Sjl139090 	if (opl_boards[board].cfg_hwd != NULL) {
28491772Sjl139090 #ifdef UCTEST
28501772Sjl139090 		size_t			size = 0xA000;
28511772Sjl139090 #endif
28521772Sjl139090 		/* Release the memory for the HWD */
28531772Sjl139090 		void *hwdp = opl_boards[board].cfg_hwd;
28541772Sjl139090 		opl_boards[board].cfg_hwd = NULL;
28551772Sjl139090 #ifdef UCTEST
28561772Sjl139090 		hwdp = (void *)((char *)hwdp - 0x1000);
28571772Sjl139090 		hat_unload(kas.a_hat, hwdp, size, HAT_UNLOAD_UNLOCK);
28581772Sjl139090 		vmem_free(heap_arena, hwdp, size);
28591772Sjl139090 #else
28601772Sjl139090 		kmem_free(hwdp, HWD_DATA_SIZE);
28611772Sjl139090 #endif
28621772Sjl139090 	}
28631772Sjl139090 	return (0);
28641772Sjl139090 }
28651772Sjl139090 
28661772Sjl139090 /*
28671772Sjl139090  * For MAC patrol support, we need to update the PA-related properties
28681772Sjl139090  * when there is a copy-rename event.  This should be called after the
28691772Sjl139090  * physical copy and rename has been done by DR, and before the MAC
28701772Sjl139090  * patrol is restarted.
28711772Sjl139090  */
28721772Sjl139090 int
28731772Sjl139090 oplcfg_pa_swap(int from, int to)
28741772Sjl139090 {
28751772Sjl139090 	dev_info_t *from_node = opl_boards[from].cfg_pseudo_mc;
28761772Sjl139090 	dev_info_t *to_node = opl_boards[to].cfg_pseudo_mc;
28771772Sjl139090 	opl_range_t *rangef, *ranget;
28781772Sjl139090 	int elems;
28791772Sjl139090 	int ret;
28801772Sjl139090 
28811772Sjl139090 	if ((OPL_GET_PROP_ARRAY(int, from_node, "sb-mem-ranges", rangef,
28821772Sjl139090 	    elems) != DDI_SUCCESS) || (elems != 4)) {
28831772Sjl139090 		/* XXX -- bad news */
28841772Sjl139090 		return (-1);
28851772Sjl139090 	}
28861772Sjl139090 	if ((OPL_GET_PROP_ARRAY(int, to_node, "sb-mem-ranges", ranget,
28871772Sjl139090 	    elems) != DDI_SUCCESS) || (elems != 4)) {
28881772Sjl139090 		/* XXX -- bad news */
28891772Sjl139090 		return (-1);
28901772Sjl139090 	}
28911772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, from_node, "sb-mem-ranges", (int *)ranget,
28921772Sjl139090 	    4);
28931772Sjl139090 	OPL_UPDATE_PROP_ARRAY(int, to_node, "sb-mem-ranges", (int *)rangef,
28941772Sjl139090 	    4);
28951772Sjl139090 
28961772Sjl139090 	OPL_FREE_PROP(ranget);
28971772Sjl139090 	OPL_FREE_PROP(rangef);
28981772Sjl139090 
28991772Sjl139090 	return (0);
29001772Sjl139090 }
2901