xref: /onnv-gate/usr/src/uts/i86pc/io/amd_iommu/amd_iommu_impl.c (revision 13050:515b1e9bea30)
110535SVikram.Hegde@Sun.COM /*
210535SVikram.Hegde@Sun.COM  * CDDL HEADER START
310535SVikram.Hegde@Sun.COM  *
410535SVikram.Hegde@Sun.COM  * The contents of this file are subject to the terms of the
510535SVikram.Hegde@Sun.COM  * Common Development and Distribution License (the "License").
610535SVikram.Hegde@Sun.COM  * You may not use this file except in compliance with the License.
710535SVikram.Hegde@Sun.COM  *
810535SVikram.Hegde@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910535SVikram.Hegde@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010535SVikram.Hegde@Sun.COM  * See the License for the specific language governing permissions
1110535SVikram.Hegde@Sun.COM  * and limitations under the License.
1210535SVikram.Hegde@Sun.COM  *
1310535SVikram.Hegde@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410535SVikram.Hegde@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510535SVikram.Hegde@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610535SVikram.Hegde@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710535SVikram.Hegde@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810535SVikram.Hegde@Sun.COM  *
1910535SVikram.Hegde@Sun.COM  * CDDL HEADER END
2010535SVikram.Hegde@Sun.COM  */
2110535SVikram.Hegde@Sun.COM 
2210535SVikram.Hegde@Sun.COM /*
2312203SJerry.Gilliam@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410535SVikram.Hegde@Sun.COM  */
2510535SVikram.Hegde@Sun.COM 
2610535SVikram.Hegde@Sun.COM #include <sys/sunddi.h>
2712203SJerry.Gilliam@Sun.COM #include <sys/sunndi.h>
2810535SVikram.Hegde@Sun.COM #include <sys/iommulib.h>
2910535SVikram.Hegde@Sun.COM #include <sys/amd_iommu.h>
3010535SVikram.Hegde@Sun.COM #include <sys/pci_cap.h>
3110535SVikram.Hegde@Sun.COM #include <sys/bootconf.h>
3210535SVikram.Hegde@Sun.COM #include <sys/ddidmareq.h>
3310535SVikram.Hegde@Sun.COM 
3410535SVikram.Hegde@Sun.COM #include "amd_iommu_impl.h"
3510535SVikram.Hegde@Sun.COM #include "amd_iommu_acpi.h"
3610535SVikram.Hegde@Sun.COM #include "amd_iommu_page_tables.h"
3710535SVikram.Hegde@Sun.COM 
3810536SVikram.Hegde@Sun.COM static int amd_iommu_fini(amd_iommu_t *iommu, int type);
3910535SVikram.Hegde@Sun.COM static void amd_iommu_teardown_interrupts(amd_iommu_t *iommu);
4010535SVikram.Hegde@Sun.COM static void amd_iommu_stop(amd_iommu_t *iommu);
4110535SVikram.Hegde@Sun.COM 
4210535SVikram.Hegde@Sun.COM static int amd_iommu_probe(iommulib_handle_t handle, dev_info_t *rdip);
4310535SVikram.Hegde@Sun.COM static int amd_iommu_allochdl(iommulib_handle_t handle,
4410535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
4510535SVikram.Hegde@Sun.COM     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep);
4610535SVikram.Hegde@Sun.COM static int amd_iommu_freehdl(iommulib_handle_t handle,
4710535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle);
4810535SVikram.Hegde@Sun.COM static int amd_iommu_bindhdl(iommulib_handle_t handle, dev_info_t *dip,
4910535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
5010535SVikram.Hegde@Sun.COM     struct ddi_dma_req *dmareq, ddi_dma_cookie_t *cookiep,
5110535SVikram.Hegde@Sun.COM     uint_t *ccountp);
5210535SVikram.Hegde@Sun.COM static int amd_iommu_unbindhdl(iommulib_handle_t handle,
5310535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle);
5410535SVikram.Hegde@Sun.COM static int amd_iommu_sync(iommulib_handle_t handle, dev_info_t *dip,
5510535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off,
5610535SVikram.Hegde@Sun.COM     size_t len, uint_t cache_flags);
5710535SVikram.Hegde@Sun.COM static int amd_iommu_win(iommulib_handle_t handle, dev_info_t *dip,
5810535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win,
5910535SVikram.Hegde@Sun.COM     off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep,
6010535SVikram.Hegde@Sun.COM     uint_t *ccountp);
61*13050Sfrank.van.der.linden@oracle.com static int amd_iommu_mapobject(iommulib_handle_t handle, dev_info_t *dip,
62*13050Sfrank.van.der.linden@oracle.com     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
63*13050Sfrank.van.der.linden@oracle.com     struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao);
64*13050Sfrank.van.der.linden@oracle.com static int amd_iommu_unmapobject(iommulib_handle_t handle, dev_info_t *dip,
65*13050Sfrank.van.der.linden@oracle.com     dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao);
6610535SVikram.Hegde@Sun.COM static int amd_iommu_map(iommulib_handle_t handle, dev_info_t *dip,
6710535SVikram.Hegde@Sun.COM     dev_info_t *rdip, struct ddi_dma_req *dmareq,
6810535SVikram.Hegde@Sun.COM     ddi_dma_handle_t *dma_handle);
6910535SVikram.Hegde@Sun.COM static int amd_iommu_mctl(iommulib_handle_t handle, dev_info_t *dip,
7010535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
7110535SVikram.Hegde@Sun.COM     enum ddi_dma_ctlops request, off_t *offp, size_t *lenp,
7210535SVikram.Hegde@Sun.COM     caddr_t *objpp, uint_t cache_flags);
7310535SVikram.Hegde@Sun.COM 
7410535SVikram.Hegde@Sun.COM static int unmap_current_window(amd_iommu_t *iommu, dev_info_t *rdip,
7510535SVikram.Hegde@Sun.COM     ddi_dma_cookie_t *cookie_array, uint_t ccount, int ncookies, int locked);
7610535SVikram.Hegde@Sun.COM 
7710535SVikram.Hegde@Sun.COM extern void *device_arena_alloc(size_t size, int vm_flag);
7810535SVikram.Hegde@Sun.COM extern void device_arena_free(void * vaddr, size_t size);
7910535SVikram.Hegde@Sun.COM 
8010535SVikram.Hegde@Sun.COM ddi_dma_attr_t amd_iommu_dma_attr = {
8110535SVikram.Hegde@Sun.COM 	DMA_ATTR_V0,
8210535SVikram.Hegde@Sun.COM 	0U,				/* dma_attr_addr_lo */
8310535SVikram.Hegde@Sun.COM 	0xffffffffffffffffULL,		/* dma_attr_addr_hi */
8410535SVikram.Hegde@Sun.COM 	0xffffffffU,			/* dma_attr_count_max */
8510535SVikram.Hegde@Sun.COM 	(uint64_t)4096,			/* dma_attr_align */
8610535SVikram.Hegde@Sun.COM 	1,				/* dma_attr_burstsizes */
8710535SVikram.Hegde@Sun.COM 	64,				/* dma_attr_minxfer */
8810535SVikram.Hegde@Sun.COM 	0xffffffffU,			/* dma_attr_maxxfer */
8910535SVikram.Hegde@Sun.COM 	0xffffffffU,			/* dma_attr_seg */
9010535SVikram.Hegde@Sun.COM 	1,				/* dma_attr_sgllen, variable */
9110535SVikram.Hegde@Sun.COM 	64,				/* dma_attr_granular */
9210535SVikram.Hegde@Sun.COM 	0				/* dma_attr_flags */
9310535SVikram.Hegde@Sun.COM };
9410535SVikram.Hegde@Sun.COM 
9510535SVikram.Hegde@Sun.COM ddi_device_acc_attr_t amd_iommu_devacc = {
9610535SVikram.Hegde@Sun.COM 	DDI_DEVICE_ATTR_V0,
9710535SVikram.Hegde@Sun.COM 	DDI_NEVERSWAP_ACC,
9810535SVikram.Hegde@Sun.COM 	DDI_STRICTORDER_ACC
9910535SVikram.Hegde@Sun.COM };
10010535SVikram.Hegde@Sun.COM 
10110535SVikram.Hegde@Sun.COM struct iommulib_ops amd_iommulib_ops = {
10210535SVikram.Hegde@Sun.COM 	IOMMU_OPS_VERSION,
10310535SVikram.Hegde@Sun.COM 	AMD_IOMMU,
10410535SVikram.Hegde@Sun.COM 	"AMD IOMMU Vers. 1",
10510535SVikram.Hegde@Sun.COM 	NULL,
10610535SVikram.Hegde@Sun.COM 	amd_iommu_probe,
10710535SVikram.Hegde@Sun.COM 	amd_iommu_allochdl,
10810535SVikram.Hegde@Sun.COM 	amd_iommu_freehdl,
10910535SVikram.Hegde@Sun.COM 	amd_iommu_bindhdl,
11010535SVikram.Hegde@Sun.COM 	amd_iommu_unbindhdl,
11110535SVikram.Hegde@Sun.COM 	amd_iommu_sync,
11210535SVikram.Hegde@Sun.COM 	amd_iommu_win,
113*13050Sfrank.van.der.linden@oracle.com 	amd_iommu_mapobject,
114*13050Sfrank.van.der.linden@oracle.com 	amd_iommu_unmapobject,
11510535SVikram.Hegde@Sun.COM 	amd_iommu_map,
11610535SVikram.Hegde@Sun.COM 	amd_iommu_mctl
11710535SVikram.Hegde@Sun.COM };
11810535SVikram.Hegde@Sun.COM 
11910535SVikram.Hegde@Sun.COM static kmutex_t amd_iommu_pgtable_lock;
12010535SVikram.Hegde@Sun.COM 
12110535SVikram.Hegde@Sun.COM static int
amd_iommu_register(amd_iommu_t * iommu)12210535SVikram.Hegde@Sun.COM amd_iommu_register(amd_iommu_t *iommu)
12310535SVikram.Hegde@Sun.COM {
12410535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
12510535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
12610535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
12710535SVikram.Hegde@Sun.COM 	iommulib_ops_t *iommulib_ops;
12810535SVikram.Hegde@Sun.COM 	iommulib_handle_t handle;
12910535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_register";
13010535SVikram.Hegde@Sun.COM 
13110535SVikram.Hegde@Sun.COM 	iommulib_ops = kmem_zalloc(sizeof (iommulib_ops_t), KM_SLEEP);
13210535SVikram.Hegde@Sun.COM 
13310535SVikram.Hegde@Sun.COM 	*iommulib_ops = amd_iommulib_ops;
13410535SVikram.Hegde@Sun.COM 
13510535SVikram.Hegde@Sun.COM 	iommulib_ops->ilops_data = (void *)iommu;
13610535SVikram.Hegde@Sun.COM 	iommu->aiomt_iommulib_ops = iommulib_ops;
13710535SVikram.Hegde@Sun.COM 
13810535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_register(dip, iommulib_ops, &handle)
13910535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS) {
14010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Register with iommulib "
14110535SVikram.Hegde@Sun.COM 		    "failed idx=%d", f, driver, instance, iommu->aiomt_idx);
14210535SVikram.Hegde@Sun.COM 		kmem_free(iommulib_ops, sizeof (iommulib_ops_t));
14310535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
14410535SVikram.Hegde@Sun.COM 	}
14510535SVikram.Hegde@Sun.COM 
14610535SVikram.Hegde@Sun.COM 	iommu->aiomt_iommulib_handle = handle;
14710535SVikram.Hegde@Sun.COM 
14810535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
14910535SVikram.Hegde@Sun.COM }
15010535SVikram.Hegde@Sun.COM 
15110535SVikram.Hegde@Sun.COM static int
amd_iommu_unregister(amd_iommu_t * iommu)15210535SVikram.Hegde@Sun.COM amd_iommu_unregister(amd_iommu_t *iommu)
15310535SVikram.Hegde@Sun.COM {
15410535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_iommulib_handle == NULL) {
15510535SVikram.Hegde@Sun.COM 		/* we never registered */
15610535SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
15710535SVikram.Hegde@Sun.COM 	}
15810535SVikram.Hegde@Sun.COM 
15910535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_unregister(iommu->aiomt_iommulib_handle)
16010535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS) {
16110535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
16210535SVikram.Hegde@Sun.COM 	}
16310535SVikram.Hegde@Sun.COM 
16410535SVikram.Hegde@Sun.COM 	kmem_free(iommu->aiomt_iommulib_ops, sizeof (iommulib_ops_t));
16510535SVikram.Hegde@Sun.COM 	iommu->aiomt_iommulib_ops = NULL;
16610535SVikram.Hegde@Sun.COM 	iommu->aiomt_iommulib_handle = NULL;
16710535SVikram.Hegde@Sun.COM 
16810535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
16910535SVikram.Hegde@Sun.COM }
17010535SVikram.Hegde@Sun.COM 
17110535SVikram.Hegde@Sun.COM static int
amd_iommu_setup_passthru(amd_iommu_t * iommu)17210535SVikram.Hegde@Sun.COM amd_iommu_setup_passthru(amd_iommu_t *iommu)
17310535SVikram.Hegde@Sun.COM {
17410535SVikram.Hegde@Sun.COM 	gfx_entry_t *gfxp;
17510535SVikram.Hegde@Sun.COM 	dev_info_t *dip;
17610535SVikram.Hegde@Sun.COM 
17710535SVikram.Hegde@Sun.COM 	/*
17810535SVikram.Hegde@Sun.COM 	 * Setup passthru mapping for "special" devices
17910535SVikram.Hegde@Sun.COM 	 */
18010535SVikram.Hegde@Sun.COM 	amd_iommu_set_passthru(iommu, NULL);
18110535SVikram.Hegde@Sun.COM 
18210535SVikram.Hegde@Sun.COM 	for (gfxp = gfx_devinfo_list; gfxp; gfxp = gfxp->g_next) {
18310535SVikram.Hegde@Sun.COM 		gfxp->g_ref++;
18410535SVikram.Hegde@Sun.COM 		dip = gfxp->g_dip;
18510535SVikram.Hegde@Sun.COM 		if (dip) {
18610535SVikram.Hegde@Sun.COM 			amd_iommu_set_passthru(iommu, dip);
18710535SVikram.Hegde@Sun.COM 		}
18810535SVikram.Hegde@Sun.COM 		gfxp->g_ref--;
18910535SVikram.Hegde@Sun.COM 	}
19010535SVikram.Hegde@Sun.COM 
19110535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
19210535SVikram.Hegde@Sun.COM }
19310535SVikram.Hegde@Sun.COM 
19410535SVikram.Hegde@Sun.COM static int
amd_iommu_start(amd_iommu_t * iommu)19510535SVikram.Hegde@Sun.COM amd_iommu_start(amd_iommu_t *iommu)
19610535SVikram.Hegde@Sun.COM {
19710535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
19810535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
19910535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
20010535SVikram.Hegde@Sun.COM 	amd_iommu_acpi_ivhd_t *hinfop;
20110535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_start";
20210535SVikram.Hegde@Sun.COM 
20310535SVikram.Hegde@Sun.COM 	hinfop = amd_iommu_lookup_all_ivhd();
20410535SVikram.Hegde@Sun.COM 
20510535SVikram.Hegde@Sun.COM 	/*
20610535SVikram.Hegde@Sun.COM 	 * Disable HT tunnel translation.
20710535SVikram.Hegde@Sun.COM 	 * XXX use ACPI
20810535SVikram.Hegde@Sun.COM 	 */
20910535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
21010535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_HT_TUN_ENABLE, 0);
21110535SVikram.Hegde@Sun.COM 
21210535SVikram.Hegde@Sun.COM 	if (hinfop) {
21310535SVikram.Hegde@Sun.COM 		if (amd_iommu_debug) {
21410535SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE,
21510535SVikram.Hegde@Sun.COM 			    "amd_iommu: using ACPI for CTRL registers");
21610535SVikram.Hegde@Sun.COM 		}
21710535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
21810535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_ISOC, hinfop->ach_Isoc);
21910535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
22010535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_RESPASSPW, hinfop->ach_ResPassPW);
22110535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
22210535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_PASSPW, hinfop->ach_PassPW);
22310535SVikram.Hegde@Sun.COM 	}
22410535SVikram.Hegde@Sun.COM 
22510535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
22610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_INVTO, 5);
22710535SVikram.Hegde@Sun.COM 
22810535SVikram.Hegde@Sun.COM 
22910535SVikram.Hegde@Sun.COM 	/*
23010535SVikram.Hegde@Sun.COM 	 * The Device table entry bit 0 (V) controls whether the device
23110535SVikram.Hegde@Sun.COM 	 * table entry is valid for address translation and Device table
23210535SVikram.Hegde@Sun.COM 	 * entry bit 128 (IV) controls whether interrupt remapping is valid.
23310535SVikram.Hegde@Sun.COM 	 * By setting both to zero we are essentially doing pass-thru. Since
23410535SVikram.Hegde@Sun.COM 	 * this table is zeroed on allocation, essentially we will have
23510535SVikram.Hegde@Sun.COM 	 * pass-thru when IOMMU is enabled.
23610535SVikram.Hegde@Sun.COM 	 */
23710535SVikram.Hegde@Sun.COM 
23810535SVikram.Hegde@Sun.COM 	/* Finally enable the IOMMU ... */
23910535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
24010535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_ENABLE, 1);
24110535SVikram.Hegde@Sun.COM 
24210535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug) {
24310535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
24410535SVikram.Hegde@Sun.COM 		    "Successfully started AMD IOMMU", f, driver, instance,
24510535SVikram.Hegde@Sun.COM 		    iommu->aiomt_idx);
24610535SVikram.Hegde@Sun.COM 	}
24710535SVikram.Hegde@Sun.COM 	cmn_err(CE_NOTE, "AMD IOMMU (%d,%d) enabled",
24810535SVikram.Hegde@Sun.COM 	    instance, iommu->aiomt_idx);
24910535SVikram.Hegde@Sun.COM 
25010535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
25110535SVikram.Hegde@Sun.COM }
25210535SVikram.Hegde@Sun.COM 
25310535SVikram.Hegde@Sun.COM static void
amd_iommu_stop(amd_iommu_t * iommu)25410535SVikram.Hegde@Sun.COM amd_iommu_stop(amd_iommu_t *iommu)
25510535SVikram.Hegde@Sun.COM {
25610535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
25710535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
25810535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
25910535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_stop";
26010535SVikram.Hegde@Sun.COM 
26110535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
26210535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_ENABLE, 0);
26310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
26410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTINT_ENABLE, 0);
26510535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
26610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMWAITINT_ENABLE, 0);
26710535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
26810535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLOG_ENABLE, 0);
26910535SVikram.Hegde@Sun.COM 
27010535SVikram.Hegde@Sun.COM 	/*
27110535SVikram.Hegde@Sun.COM 	 * Disable translation on HT tunnel traffic
27210535SVikram.Hegde@Sun.COM 	 */
27310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
27410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_HT_TUN_ENABLE, 0);
27510535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
27610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_ENABLE, 0);
27710535SVikram.Hegde@Sun.COM 
27810535SVikram.Hegde@Sun.COM 	cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMYU idx=%d. "
27910535SVikram.Hegde@Sun.COM 	    "Successfully stopped AMD IOMMU", f, driver, instance,
28010535SVikram.Hegde@Sun.COM 	    iommu->aiomt_idx);
28110535SVikram.Hegde@Sun.COM }
28210535SVikram.Hegde@Sun.COM 
28310535SVikram.Hegde@Sun.COM static int
amd_iommu_setup_tables_and_buffers(amd_iommu_t * iommu)28410535SVikram.Hegde@Sun.COM amd_iommu_setup_tables_and_buffers(amd_iommu_t *iommu)
28510535SVikram.Hegde@Sun.COM {
28610535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
28710535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
28810535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
28910535SVikram.Hegde@Sun.COM 	uint32_t dma_bufsz;
29010535SVikram.Hegde@Sun.COM 	caddr_t addr;
29110535SVikram.Hegde@Sun.COM 	uint32_t sz;
29210535SVikram.Hegde@Sun.COM 	uint32_t p2sz;
29310535SVikram.Hegde@Sun.COM 	int i;
29410535SVikram.Hegde@Sun.COM 	uint64_t *dentry;
29510535SVikram.Hegde@Sun.COM 	int err;
29610535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_setup_tables_and_buffers";
29710535SVikram.Hegde@Sun.COM 
29810535SVikram.Hegde@Sun.COM 	/*
29910535SVikram.Hegde@Sun.COM 	 * We will put the Device Table, Command Buffer and
30010535SVikram.Hegde@Sun.COM 	 * Event Log in contiguous memory. Allocate the maximum
30110535SVikram.Hegde@Sun.COM 	 * size allowed for such structures
30210535SVikram.Hegde@Sun.COM 	 * Device Table:  256b * 64K = 32B * 64K
30310535SVikram.Hegde@Sun.COM 	 * Command Buffer: 128b * 32K = 16B * 32K
30410535SVikram.Hegde@Sun.COM 	 * Event Log:  128b * 32K = 16B * 32K
30510535SVikram.Hegde@Sun.COM 	 */
30610535SVikram.Hegde@Sun.COM 	iommu->aiomt_devtbl_sz = (1<<AMD_IOMMU_DEVTBL_SZ) * AMD_IOMMU_DEVENT_SZ;
30710535SVikram.Hegde@Sun.COM 	iommu->aiomt_cmdbuf_sz = (1<<AMD_IOMMU_CMDBUF_SZ) * AMD_IOMMU_CMD_SZ;
30810535SVikram.Hegde@Sun.COM 	iommu->aiomt_eventlog_sz =
30910535SVikram.Hegde@Sun.COM 	    (1<<AMD_IOMMU_EVENTLOG_SZ) * AMD_IOMMU_EVENT_SZ;
31010535SVikram.Hegde@Sun.COM 
31110535SVikram.Hegde@Sun.COM 	dma_bufsz = iommu->aiomt_devtbl_sz + iommu->aiomt_cmdbuf_sz
31210535SVikram.Hegde@Sun.COM 	    + iommu->aiomt_eventlog_sz;
31310535SVikram.Hegde@Sun.COM 
31410535SVikram.Hegde@Sun.COM 	/*
31510535SVikram.Hegde@Sun.COM 	 * Alloc a DMA handle.
31610535SVikram.Hegde@Sun.COM 	 */
31710535SVikram.Hegde@Sun.COM 	err = ddi_dma_alloc_handle(dip, &amd_iommu_dma_attr,
31810535SVikram.Hegde@Sun.COM 	    DDI_DMA_SLEEP, NULL, &iommu->aiomt_dmahdl);
31910535SVikram.Hegde@Sun.COM 	if (err != DDI_SUCCESS) {
32010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot alloc DMA handle for "
32110535SVikram.Hegde@Sun.COM 		    "AMD IOMMU tables and buffers", f, driver, instance);
32210535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
32310535SVikram.Hegde@Sun.COM 	}
32410535SVikram.Hegde@Sun.COM 
32510535SVikram.Hegde@Sun.COM 	/*
32610535SVikram.Hegde@Sun.COM 	 * Alloc memory for tables and buffers
32710535SVikram.Hegde@Sun.COM 	 * XXX remove cast to size_t
32810535SVikram.Hegde@Sun.COM 	 */
32910535SVikram.Hegde@Sun.COM 	err = ddi_dma_mem_alloc(iommu->aiomt_dmahdl, dma_bufsz,
33010535SVikram.Hegde@Sun.COM 	    &amd_iommu_devacc, DDI_DMA_CONSISTENT|IOMEM_DATA_UNCACHED,
33110535SVikram.Hegde@Sun.COM 	    DDI_DMA_SLEEP,  NULL, (caddr_t *)&iommu->aiomt_dma_bufva,
33210535SVikram.Hegde@Sun.COM 	    (size_t *)&iommu->aiomt_dma_mem_realsz, &iommu->aiomt_dma_mem_hdl);
33310535SVikram.Hegde@Sun.COM 	if (err != DDI_SUCCESS) {
33410535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot alloc memory for DMA "
33510535SVikram.Hegde@Sun.COM 		    "to AMD IOMMU tables and buffers", f, driver, instance);
33610535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_bufva = NULL;
33710535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_mem_realsz = 0;
33810535SVikram.Hegde@Sun.COM 		ddi_dma_free_handle(&iommu->aiomt_dmahdl);
33910535SVikram.Hegde@Sun.COM 		iommu->aiomt_dmahdl = NULL;
34010535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
34110535SVikram.Hegde@Sun.COM 	}
34210535SVikram.Hegde@Sun.COM 
34310535SVikram.Hegde@Sun.COM 	/*
34410535SVikram.Hegde@Sun.COM 	 * The VA must be 4K aligned and >= table size
34510535SVikram.Hegde@Sun.COM 	 */
34610535SVikram.Hegde@Sun.COM 	ASSERT(((uintptr_t)iommu->aiomt_dma_bufva &
34710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_TABLE_ALIGN) == 0);
34810535SVikram.Hegde@Sun.COM 	ASSERT(iommu->aiomt_dma_mem_realsz >= dma_bufsz);
34910535SVikram.Hegde@Sun.COM 
35010535SVikram.Hegde@Sun.COM 	/*
35110535SVikram.Hegde@Sun.COM 	 * Now bind the handle
35210535SVikram.Hegde@Sun.COM 	 */
35310535SVikram.Hegde@Sun.COM 	err = ddi_dma_addr_bind_handle(iommu->aiomt_dmahdl, NULL,
35410535SVikram.Hegde@Sun.COM 	    iommu->aiomt_dma_bufva, iommu->aiomt_dma_mem_realsz,
35510535SVikram.Hegde@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
35610535SVikram.Hegde@Sun.COM 	    NULL, &iommu->aiomt_buf_dma_cookie, &iommu->aiomt_buf_dma_ncookie);
35710535SVikram.Hegde@Sun.COM 	if (err != DDI_DMA_MAPPED) {
35810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot bind memory for DMA "
35910535SVikram.Hegde@Sun.COM 		    "to AMD IOMMU tables and buffers. bufrealsz=%p",
36010535SVikram.Hegde@Sun.COM 		    f, driver, instance,
36110535SVikram.Hegde@Sun.COM 		    (void *)(uintptr_t)iommu->aiomt_dma_mem_realsz);
36210535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_laddress = 0;
36310535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_size = 0;
36410535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_type = 0;
36510535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_ncookie = 0;
36610535SVikram.Hegde@Sun.COM 		ddi_dma_mem_free(&iommu->aiomt_dma_mem_hdl);
36710535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_mem_hdl = NULL;
36810535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_bufva = NULL;
36910535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_mem_realsz = 0;
37010535SVikram.Hegde@Sun.COM 		ddi_dma_free_handle(&iommu->aiomt_dmahdl);
37110535SVikram.Hegde@Sun.COM 		iommu->aiomt_dmahdl = NULL;
37210535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
37310535SVikram.Hegde@Sun.COM 	}
37410535SVikram.Hegde@Sun.COM 
37510535SVikram.Hegde@Sun.COM 	/*
37610535SVikram.Hegde@Sun.COM 	 * We assume the DMA engine on the IOMMU is capable of handling the
37710535SVikram.Hegde@Sun.COM 	 * whole table buffer in a single cookie. If not and multiple cookies
37810535SVikram.Hegde@Sun.COM 	 * are needed we fail.
37910535SVikram.Hegde@Sun.COM 	 */
38010535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_buf_dma_ncookie != 1) {
38110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot handle multiple "
38210535SVikram.Hegde@Sun.COM 		    "cookies for DMA to AMD IOMMU tables and buffers. "
38310535SVikram.Hegde@Sun.COM 		    "#cookies=%u", f, driver, instance,
38410535SVikram.Hegde@Sun.COM 		    iommu->aiomt_buf_dma_ncookie);
38510535SVikram.Hegde@Sun.COM 		(void) ddi_dma_unbind_handle(iommu->aiomt_dmahdl);
38610535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_laddress = 0;
38710535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_size = 0;
38810535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_cookie.dmac_type = 0;
38910535SVikram.Hegde@Sun.COM 		iommu->aiomt_buf_dma_ncookie = 0;
39010535SVikram.Hegde@Sun.COM 		ddi_dma_mem_free(&iommu->aiomt_dma_mem_hdl);
39110535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_mem_hdl = NULL;
39210535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_bufva = NULL;
39310535SVikram.Hegde@Sun.COM 		iommu->aiomt_dma_mem_realsz = 0;
39410535SVikram.Hegde@Sun.COM 		ddi_dma_free_handle(&iommu->aiomt_dmahdl);
39510535SVikram.Hegde@Sun.COM 		iommu->aiomt_dmahdl = NULL;
39610535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
39710535SVikram.Hegde@Sun.COM 	}
39810535SVikram.Hegde@Sun.COM 
39910535SVikram.Hegde@Sun.COM 	/*
40010535SVikram.Hegde@Sun.COM 	 * The address in the cookie must be 4K aligned and >= table size
40110535SVikram.Hegde@Sun.COM 	 */
40210535SVikram.Hegde@Sun.COM 	ASSERT((iommu->aiomt_buf_dma_cookie.dmac_cookie_addr
40310535SVikram.Hegde@Sun.COM 	    & AMD_IOMMU_TABLE_ALIGN) == 0);
40410535SVikram.Hegde@Sun.COM 	ASSERT(iommu->aiomt_buf_dma_cookie.dmac_size
40510535SVikram.Hegde@Sun.COM 	    <= iommu->aiomt_dma_mem_realsz);
40610535SVikram.Hegde@Sun.COM 	ASSERT(iommu->aiomt_buf_dma_cookie.dmac_size >= dma_bufsz);
40710535SVikram.Hegde@Sun.COM 
40810535SVikram.Hegde@Sun.COM 	/*
40910535SVikram.Hegde@Sun.COM 	 * Setup the device table pointers in the iommu struct as
41010535SVikram.Hegde@Sun.COM 	 * well as the IOMMU device table register
41110535SVikram.Hegde@Sun.COM 	 */
41210535SVikram.Hegde@Sun.COM 	iommu->aiomt_devtbl = iommu->aiomt_dma_bufva;
41310535SVikram.Hegde@Sun.COM 	bzero(iommu->aiomt_devtbl, iommu->aiomt_devtbl_sz);
41410535SVikram.Hegde@Sun.COM 
41510535SVikram.Hegde@Sun.COM 	/*
41610535SVikram.Hegde@Sun.COM 	 * Set V=1 and TV = 0, so any inadvertant pass-thrus cause
41710535SVikram.Hegde@Sun.COM 	 * page faults. Also set SE bit so we aren't swamped with
41810535SVikram.Hegde@Sun.COM 	 * page fault messages
41910535SVikram.Hegde@Sun.COM 	 */
42010535SVikram.Hegde@Sun.COM 	for (i = 0; i <= AMD_IOMMU_MAX_DEVICEID; i++) {
42110535SVikram.Hegde@Sun.COM 		/*LINTED*/
42210535SVikram.Hegde@Sun.COM 		dentry = (uint64_t *)&iommu->aiomt_devtbl
42310535SVikram.Hegde@Sun.COM 		    [i * AMD_IOMMU_DEVTBL_ENTRY_SZ];
42410535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(dentry, AMD_IOMMU_DEVTBL_V, 1);
42510535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(&(dentry[1]), AMD_IOMMU_DEVTBL_SE, 1);
42610535SVikram.Hegde@Sun.COM 	}
42710535SVikram.Hegde@Sun.COM 
42810535SVikram.Hegde@Sun.COM 	addr = (caddr_t)(uintptr_t)iommu->aiomt_buf_dma_cookie.dmac_cookie_addr;
42910535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_devtbl_va),
43010535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_DEVTABBASE, ((uint64_t)(uintptr_t)addr) >> 12);
43110535SVikram.Hegde@Sun.COM 	sz = (iommu->aiomt_devtbl_sz >> 12) - 1;
43210535SVikram.Hegde@Sun.COM 	ASSERT(sz <= ((1 << 9) - 1));
43310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_devtbl_va),
43410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_DEVTABSIZE, sz);
43510535SVikram.Hegde@Sun.COM 
43610535SVikram.Hegde@Sun.COM 	/*
43710535SVikram.Hegde@Sun.COM 	 * Setup the command buffer pointers
43810535SVikram.Hegde@Sun.COM 	 */
43910535SVikram.Hegde@Sun.COM 	iommu->aiomt_cmdbuf = iommu->aiomt_devtbl +
44010535SVikram.Hegde@Sun.COM 	    iommu->aiomt_devtbl_sz;
44110535SVikram.Hegde@Sun.COM 	bzero(iommu->aiomt_cmdbuf, iommu->aiomt_cmdbuf_sz);
44210535SVikram.Hegde@Sun.COM 	addr += iommu->aiomt_devtbl_sz;
44310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_va),
44410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMBASE, ((uint64_t)(uintptr_t)addr) >> 12);
44510535SVikram.Hegde@Sun.COM 
44610535SVikram.Hegde@Sun.COM 	p2sz = AMD_IOMMU_CMDBUF_SZ;
44710535SVikram.Hegde@Sun.COM 	ASSERT(p2sz >= AMD_IOMMU_CMDBUF_MINSZ &&
44810535SVikram.Hegde@Sun.COM 	    p2sz <= AMD_IOMMU_CMDBUF_MAXSZ);
44910535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_va),
45010535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMLEN, p2sz);
45110535SVikram.Hegde@Sun.COM 	/*LINTED*/
45210535SVikram.Hegde@Sun.COM 	iommu->aiomt_cmd_tail = (uint32_t *)iommu->aiomt_cmdbuf;
45310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_head_va),
45410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDHEADPTR, 0);
45510535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_tail_va),
45610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDTAILPTR, 0);
45710535SVikram.Hegde@Sun.COM 
45810535SVikram.Hegde@Sun.COM 	/*
45910535SVikram.Hegde@Sun.COM 	 * Setup the event log pointers
46010535SVikram.Hegde@Sun.COM 	 */
46110535SVikram.Hegde@Sun.COM 	iommu->aiomt_eventlog = iommu->aiomt_cmdbuf +
46210535SVikram.Hegde@Sun.COM 	    iommu->aiomt_eventlog_sz;
46310535SVikram.Hegde@Sun.COM 	bzero(iommu->aiomt_eventlog, iommu->aiomt_eventlog_sz);
46410535SVikram.Hegde@Sun.COM 	addr += iommu->aiomt_cmdbuf_sz;
46510535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_va),
46610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTBASE, ((uint64_t)(uintptr_t)addr) >> 12);
46710535SVikram.Hegde@Sun.COM 	p2sz = AMD_IOMMU_EVENTLOG_SZ;
46810535SVikram.Hegde@Sun.COM 	ASSERT(p2sz >= AMD_IOMMU_EVENTLOG_MINSZ &&
46910535SVikram.Hegde@Sun.COM 	    p2sz <= AMD_IOMMU_EVENTLOG_MAXSZ);
47010535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_va),
47110535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLEN, sz);
47210535SVikram.Hegde@Sun.COM 	/*LINTED*/
47310535SVikram.Hegde@Sun.COM 	iommu->aiomt_event_head = (uint32_t *)iommu->aiomt_eventlog;
47410535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_head_va),
47510535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTHEADPTR, 0);
47610535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_tail_va),
47710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTTAILPTR, 0);
47810535SVikram.Hegde@Sun.COM 
47910535SVikram.Hegde@Sun.COM 	/* dma sync so device sees this init */
48010535SVikram.Hegde@Sun.COM 	SYNC_FORDEV(iommu->aiomt_dmahdl);
48110535SVikram.Hegde@Sun.COM 
48210535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_TABLES) {
48310535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: successfully setup AMD IOMMU "
48410535SVikram.Hegde@Sun.COM 		    "tables, idx=%d", f, driver, instance, iommu->aiomt_idx);
48510535SVikram.Hegde@Sun.COM 	}
48610535SVikram.Hegde@Sun.COM 
48710535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
48810535SVikram.Hegde@Sun.COM }
48910535SVikram.Hegde@Sun.COM 
49010535SVikram.Hegde@Sun.COM static void
amd_iommu_teardown_tables_and_buffers(amd_iommu_t * iommu,int type)49110536SVikram.Hegde@Sun.COM amd_iommu_teardown_tables_and_buffers(amd_iommu_t *iommu, int type)
49210535SVikram.Hegde@Sun.COM {
49310535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
49410535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
49510535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
49610535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_teardown_tables_and_buffers";
49710535SVikram.Hegde@Sun.COM 
49810535SVikram.Hegde@Sun.COM 	iommu->aiomt_eventlog = NULL;
49910535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_va),
50010535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTBASE, 0);
50110535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_va),
50210535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLEN, 0);
50310536SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_head_va),
50410536SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTHEADPTR, 0);
50510536SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_eventlog_head_va),
50610536SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTTAILPTR, 0);
50710536SVikram.Hegde@Sun.COM 
50810535SVikram.Hegde@Sun.COM 
50910535SVikram.Hegde@Sun.COM 	iommu->aiomt_cmdbuf = NULL;
51010535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_va),
51110535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMBASE, 0);
51210535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_va),
51310535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMLEN, 0);
51410536SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_head_va),
51510536SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDHEADPTR, 0);
51610536SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_head_va),
51710536SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDTAILPTR, 0);
51810536SVikram.Hegde@Sun.COM 
51910535SVikram.Hegde@Sun.COM 
52010535SVikram.Hegde@Sun.COM 	iommu->aiomt_devtbl = NULL;
52110535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_devtbl_va),
52210535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_DEVTABBASE, 0);
52310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_devtbl_va),
52410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_DEVTABSIZE, 0);
52510535SVikram.Hegde@Sun.COM 
52610536SVikram.Hegde@Sun.COM 	if (iommu->aiomt_dmahdl == NULL || type == AMD_IOMMU_QUIESCE)
52710535SVikram.Hegde@Sun.COM 		return;
52810535SVikram.Hegde@Sun.COM 
52910535SVikram.Hegde@Sun.COM 	/* Unbind the handle */
53010535SVikram.Hegde@Sun.COM 	if (ddi_dma_unbind_handle(iommu->aiomt_dmahdl) != DDI_SUCCESS) {
53110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: failed to unbind handle: "
53210535SVikram.Hegde@Sun.COM 		    "%p for IOMMU idx=%d", f, driver, instance,
53310535SVikram.Hegde@Sun.COM 		    (void *)iommu->aiomt_dmahdl, iommu->aiomt_idx);
53410535SVikram.Hegde@Sun.COM 	}
53510535SVikram.Hegde@Sun.COM 	iommu->aiomt_buf_dma_cookie.dmac_laddress = 0;
53610535SVikram.Hegde@Sun.COM 	iommu->aiomt_buf_dma_cookie.dmac_size = 0;
53710535SVikram.Hegde@Sun.COM 	iommu->aiomt_buf_dma_cookie.dmac_type = 0;
53810535SVikram.Hegde@Sun.COM 	iommu->aiomt_buf_dma_ncookie = 0;
53910535SVikram.Hegde@Sun.COM 
54010535SVikram.Hegde@Sun.COM 	/* Free the table memory allocated for DMA */
54110535SVikram.Hegde@Sun.COM 	ddi_dma_mem_free(&iommu->aiomt_dma_mem_hdl);
54210535SVikram.Hegde@Sun.COM 	iommu->aiomt_dma_mem_hdl = NULL;
54310535SVikram.Hegde@Sun.COM 	iommu->aiomt_dma_bufva = NULL;
54410535SVikram.Hegde@Sun.COM 	iommu->aiomt_dma_mem_realsz = 0;
54510535SVikram.Hegde@Sun.COM 
54610535SVikram.Hegde@Sun.COM 	/* Free the DMA handle */
54710535SVikram.Hegde@Sun.COM 	ddi_dma_free_handle(&iommu->aiomt_dmahdl);
54810535SVikram.Hegde@Sun.COM 	iommu->aiomt_dmahdl = NULL;
54910535SVikram.Hegde@Sun.COM }
55010535SVikram.Hegde@Sun.COM 
55110535SVikram.Hegde@Sun.COM static void
amd_iommu_enable_interrupts(amd_iommu_t * iommu)55210535SVikram.Hegde@Sun.COM amd_iommu_enable_interrupts(amd_iommu_t *iommu)
55310535SVikram.Hegde@Sun.COM {
55410535SVikram.Hegde@Sun.COM 	ASSERT(AMD_IOMMU_REG_GET64(REGADDR64(iommu->aiomt_reg_status_va),
55510535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_RUN) == 0);
55610535SVikram.Hegde@Sun.COM 	ASSERT(AMD_IOMMU_REG_GET64(REGADDR64(iommu->aiomt_reg_status_va),
55710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENT_LOG_RUN) == 0);
55810535SVikram.Hegde@Sun.COM 
55910535SVikram.Hegde@Sun.COM 	/* Must be set prior to enabling command buffer */
56010535SVikram.Hegde@Sun.COM 	/* Must be set prior to enabling event logging */
56110535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
56210535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_ENABLE, 1);
56310535SVikram.Hegde@Sun.COM 	/* No interrupts for completion wait  - too heavy weight. use polling */
56410535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
56510535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_COMWAITINT_ENABLE, 0);
56610535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
56710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLOG_ENABLE, 1);
56810535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
56910535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTINT_ENABLE, 1);
57010535SVikram.Hegde@Sun.COM }
57110535SVikram.Hegde@Sun.COM 
57210535SVikram.Hegde@Sun.COM static int
amd_iommu_setup_exclusion(amd_iommu_t * iommu)57310535SVikram.Hegde@Sun.COM amd_iommu_setup_exclusion(amd_iommu_t *iommu)
57410535SVikram.Hegde@Sun.COM {
57510535SVikram.Hegde@Sun.COM 	amd_iommu_acpi_ivmd_t *minfop;
57610535SVikram.Hegde@Sun.COM 
57710535SVikram.Hegde@Sun.COM 	minfop = amd_iommu_lookup_all_ivmd();
57810535SVikram.Hegde@Sun.COM 
57910535SVikram.Hegde@Sun.COM 	if (minfop && minfop->acm_ExclRange == 1) {
58010535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "Programming exclusion range");
58110535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
58210535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_ADDR,
58310535SVikram.Hegde@Sun.COM 		    minfop->acm_ivmd_phys_start >> 12);
58410535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
58510535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_ALLOW, 1);
58610535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
58710535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_EXEN, 1);
58810535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_lim_va),
58910535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_LIM, (minfop->acm_ivmd_phys_start +
59010535SVikram.Hegde@Sun.COM 		    minfop->acm_ivmd_phys_len) >> 12);
59110535SVikram.Hegde@Sun.COM 	} else {
59210535SVikram.Hegde@Sun.COM 		if (amd_iommu_debug) {
59310535SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE, "Skipping exclusion range");
59410535SVikram.Hegde@Sun.COM 		}
59510535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
59610535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_ADDR, 0);
59710535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
59810535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_ALLOW, 1);
59910535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_base_va),
60010535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_BASE_EXEN, 0);
60110535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_excl_lim_va),
60210535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EXCL_LIM, 0);
60310535SVikram.Hegde@Sun.COM 	}
60410535SVikram.Hegde@Sun.COM 
60510535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
60610535SVikram.Hegde@Sun.COM }
60710535SVikram.Hegde@Sun.COM 
60810535SVikram.Hegde@Sun.COM static void
amd_iommu_teardown_exclusion(amd_iommu_t * iommu)60910535SVikram.Hegde@Sun.COM amd_iommu_teardown_exclusion(amd_iommu_t *iommu)
61010535SVikram.Hegde@Sun.COM {
61110535SVikram.Hegde@Sun.COM 	(void) amd_iommu_setup_exclusion(iommu);
61210535SVikram.Hegde@Sun.COM }
61310535SVikram.Hegde@Sun.COM 
61410535SVikram.Hegde@Sun.COM static uint_t
amd_iommu_intr_handler(caddr_t arg1,caddr_t arg2)61510535SVikram.Hegde@Sun.COM amd_iommu_intr_handler(caddr_t arg1, caddr_t arg2)
61610535SVikram.Hegde@Sun.COM {
61710535SVikram.Hegde@Sun.COM 	/*LINTED*/
61810535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu = (amd_iommu_t *)arg1;
61910535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
62010535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
62110535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
62210535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_intr_handler";
62310535SVikram.Hegde@Sun.COM 
62410535SVikram.Hegde@Sun.COM 	ASSERT(arg1);
62510535SVikram.Hegde@Sun.COM 	ASSERT(arg2 == NULL);
62610535SVikram.Hegde@Sun.COM 
62710535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
62810535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: IOMMU unit idx=%d. In INTR handler",
62910535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx);
63010535SVikram.Hegde@Sun.COM 	}
63110535SVikram.Hegde@Sun.COM 
63210535SVikram.Hegde@Sun.COM 	if (AMD_IOMMU_REG_GET64(REGADDR64(iommu->aiomt_reg_status_va),
63310535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENT_LOG_INT) == 1) {
63410535SVikram.Hegde@Sun.COM 		if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
63510535SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE, "%s: %s%d: IOMMU unit idx=%d "
63610535SVikram.Hegde@Sun.COM 			    "Event Log Interrupt", f, driver, instance,
63710535SVikram.Hegde@Sun.COM 			    iommu->aiomt_idx);
63810535SVikram.Hegde@Sun.COM 		}
63910535SVikram.Hegde@Sun.COM 		(void) amd_iommu_read_log(iommu, AMD_IOMMU_LOG_DISPLAY);
64010535SVikram.Hegde@Sun.COM 		WAIT_SEC(1);
64110535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_status_va),
64210535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EVENT_LOG_INT, 1);
64310535SVikram.Hegde@Sun.COM 		return (DDI_INTR_CLAIMED);
64410535SVikram.Hegde@Sun.COM 	}
64510535SVikram.Hegde@Sun.COM 
64610535SVikram.Hegde@Sun.COM 	if (AMD_IOMMU_REG_GET64(REGADDR64(iommu->aiomt_reg_status_va),
64710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENT_OVERFLOW_INT) == 1) {
64810535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "!%s: %s%d: IOMMU unit idx=%d "
64910535SVikram.Hegde@Sun.COM 		    "Event Overflow Interrupt", f, driver, instance,
65010535SVikram.Hegde@Sun.COM 		    iommu->aiomt_idx);
65110535SVikram.Hegde@Sun.COM 		(void) amd_iommu_read_log(iommu, AMD_IOMMU_LOG_DISCARD);
65210535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_status_va),
65310535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EVENT_LOG_INT, 1);
65410535SVikram.Hegde@Sun.COM 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_status_va),
65510535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_EVENT_OVERFLOW_INT, 1);
65610535SVikram.Hegde@Sun.COM 		return (DDI_INTR_CLAIMED);
65710535SVikram.Hegde@Sun.COM 	}
65810535SVikram.Hegde@Sun.COM 
65910535SVikram.Hegde@Sun.COM 	return (DDI_INTR_UNCLAIMED);
66010535SVikram.Hegde@Sun.COM }
66110535SVikram.Hegde@Sun.COM 
66210535SVikram.Hegde@Sun.COM 
66310535SVikram.Hegde@Sun.COM static int
amd_iommu_setup_interrupts(amd_iommu_t * iommu)66410535SVikram.Hegde@Sun.COM amd_iommu_setup_interrupts(amd_iommu_t *iommu)
66510535SVikram.Hegde@Sun.COM {
66610535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
66710535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
66810535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
66910535SVikram.Hegde@Sun.COM 	int intrcap0;
67010535SVikram.Hegde@Sun.COM 	int intrcapN;
67110535SVikram.Hegde@Sun.COM 	int type;
67210535SVikram.Hegde@Sun.COM 	int err;
67310535SVikram.Hegde@Sun.COM 	int req;
67410535SVikram.Hegde@Sun.COM 	int avail;
67510535SVikram.Hegde@Sun.COM 	int p2req;
67610535SVikram.Hegde@Sun.COM 	int actual;
67710535SVikram.Hegde@Sun.COM 	int i;
67810535SVikram.Hegde@Sun.COM 	int j;
67910535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_setup_interrupts";
68010535SVikram.Hegde@Sun.COM 
68110535SVikram.Hegde@Sun.COM 	if (ddi_intr_get_supported_types(dip, &type) != DDI_SUCCESS) {
68210535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: ddi_intr_get_supported_types "
68310535SVikram.Hegde@Sun.COM 		    "failed: idx=%d", f, driver, instance, iommu->aiomt_idx);
68410535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
68510535SVikram.Hegde@Sun.COM 	}
68610535SVikram.Hegde@Sun.COM 
68710535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
68810535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
68910535SVikram.Hegde@Sun.COM 		    "Interrupt types supported = 0x%x", f, driver, instance,
69010535SVikram.Hegde@Sun.COM 		    iommu->aiomt_idx, type);
69110535SVikram.Hegde@Sun.COM 	}
69210535SVikram.Hegde@Sun.COM 
69310535SVikram.Hegde@Sun.COM 	/*
69410535SVikram.Hegde@Sun.COM 	 * for now we only support MSI
69510535SVikram.Hegde@Sun.COM 	 */
69610535SVikram.Hegde@Sun.COM 	if ((type & DDI_INTR_TYPE_MSI) == 0) {
69710535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d. "
69810535SVikram.Hegde@Sun.COM 		    "MSI interrupts not supported. Failing init.",
69910535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx);
70010535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
70110535SVikram.Hegde@Sun.COM 	}
70210535SVikram.Hegde@Sun.COM 
70310535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
70410535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. MSI supported",
70510535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx);
70610535SVikram.Hegde@Sun.COM 	}
70710535SVikram.Hegde@Sun.COM 
70810535SVikram.Hegde@Sun.COM 	err = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_MSI, &req);
70910535SVikram.Hegde@Sun.COM 	if (err != DDI_SUCCESS) {
71010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d. "
71110535SVikram.Hegde@Sun.COM 		    "ddi_intr_get_nintrs failed err = %d",
71210535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, err);
71310535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
71410535SVikram.Hegde@Sun.COM 	}
71510535SVikram.Hegde@Sun.COM 
71610535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
71710535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
71810535SVikram.Hegde@Sun.COM 		    "MSI number of interrupts requested: %d",
71910535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, req);
72010535SVikram.Hegde@Sun.COM 	}
72110535SVikram.Hegde@Sun.COM 
72210535SVikram.Hegde@Sun.COM 	if (req == 0) {
72310535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: 0 MSI "
72410535SVikram.Hegde@Sun.COM 		    "interrupts requested. Failing init", f,
72510535SVikram.Hegde@Sun.COM 		    driver, instance, iommu->aiomt_idx);
72610535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
72710535SVikram.Hegde@Sun.COM 	}
72810535SVikram.Hegde@Sun.COM 
72910535SVikram.Hegde@Sun.COM 	err = ddi_intr_get_navail(dip, DDI_INTR_TYPE_MSI, &avail);
73010535SVikram.Hegde@Sun.COM 	if (err != DDI_SUCCESS) {
73110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d "
73210535SVikram.Hegde@Sun.COM 		    "ddi_intr_get_navail failed err = %d", f,
73310535SVikram.Hegde@Sun.COM 		    driver, instance, iommu->aiomt_idx, err);
73410535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
73510535SVikram.Hegde@Sun.COM 	}
73610535SVikram.Hegde@Sun.COM 
73710535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
73810535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
73910535SVikram.Hegde@Sun.COM 		    "MSI number of interrupts available: %d",
74010535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, avail);
74110535SVikram.Hegde@Sun.COM 	}
74210535SVikram.Hegde@Sun.COM 
74310535SVikram.Hegde@Sun.COM 	if (avail == 0) {
74410535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: 0 MSI "
74510535SVikram.Hegde@Sun.COM 		    "interrupts available. Failing init", f,
74610535SVikram.Hegde@Sun.COM 		    driver, instance, iommu->aiomt_idx);
74710535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
74810535SVikram.Hegde@Sun.COM 	}
74910535SVikram.Hegde@Sun.COM 
75010535SVikram.Hegde@Sun.COM 	if (avail < req) {
75110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: MSI "
75210535SVikram.Hegde@Sun.COM 		    "interrupts: requested (%d) > available (%d). "
75310535SVikram.Hegde@Sun.COM 		    "Failing init", f, driver, instance, iommu->aiomt_idx,
75410535SVikram.Hegde@Sun.COM 		    req, avail);
75510535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
75610535SVikram.Hegde@Sun.COM 	}
75710535SVikram.Hegde@Sun.COM 
75810535SVikram.Hegde@Sun.COM 	/* Allocate memory for DDI interrupt handles */
75910535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_htable_sz = req * sizeof (ddi_intr_handle_t);
76010535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_htable = kmem_zalloc(iommu->aiomt_intr_htable_sz,
76110535SVikram.Hegde@Sun.COM 	    KM_SLEEP);
76210535SVikram.Hegde@Sun.COM 
76310535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_state = AMD_IOMMU_INTR_TABLE;
76410535SVikram.Hegde@Sun.COM 
76510535SVikram.Hegde@Sun.COM 	/* Convert req to a power of two as required by ddi_intr_alloc */
76610535SVikram.Hegde@Sun.COM 	p2req = 0;
76710535SVikram.Hegde@Sun.COM 	while (1<<p2req <= req)
76810535SVikram.Hegde@Sun.COM 		p2req++;
76910535SVikram.Hegde@Sun.COM 	p2req--;
77010535SVikram.Hegde@Sun.COM 	req = 1<<p2req;
77110535SVikram.Hegde@Sun.COM 
77210535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
77310535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
77410535SVikram.Hegde@Sun.COM 		    "MSI power of 2 number of interrupts: %d,%d",
77510535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, p2req, req);
77610535SVikram.Hegde@Sun.COM 	}
77710535SVikram.Hegde@Sun.COM 
77810535SVikram.Hegde@Sun.COM 	err = ddi_intr_alloc(iommu->aiomt_dip, iommu->aiomt_intr_htable,
77910535SVikram.Hegde@Sun.COM 	    DDI_INTR_TYPE_MSI, 0, req, &actual, DDI_INTR_ALLOC_STRICT);
78010535SVikram.Hegde@Sun.COM 	if (err != DDI_SUCCESS) {
78110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
78210535SVikram.Hegde@Sun.COM 		    "ddi_intr_alloc failed: err = %d",
78310535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, err);
78410535SVikram.Hegde@Sun.COM 		amd_iommu_teardown_interrupts(iommu);
78510535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
78610535SVikram.Hegde@Sun.COM 	}
78710535SVikram.Hegde@Sun.COM 
78810535SVikram.Hegde@Sun.COM 	iommu->aiomt_actual_intrs = actual;
78910535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_state = AMD_IOMMU_INTR_ALLOCED;
79010535SVikram.Hegde@Sun.COM 
79110535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
79210535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d. "
79310535SVikram.Hegde@Sun.COM 		    "number of interrupts actually allocated %d",
79410535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, actual);
79510535SVikram.Hegde@Sun.COM 	}
79610535SVikram.Hegde@Sun.COM 
79710535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_actual_intrs < req) {
79810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
79910535SVikram.Hegde@Sun.COM 		    "ddi_intr_alloc failed: actual (%d) < req (%d)",
80010535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx,
80110535SVikram.Hegde@Sun.COM 		    iommu->aiomt_actual_intrs, req);
80210535SVikram.Hegde@Sun.COM 		amd_iommu_teardown_interrupts(iommu);
80310535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
80410535SVikram.Hegde@Sun.COM 	}
80510535SVikram.Hegde@Sun.COM 
80610535SVikram.Hegde@Sun.COM 	for (i = 0; i < iommu->aiomt_actual_intrs; i++) {
80710535SVikram.Hegde@Sun.COM 		if (ddi_intr_add_handler(iommu->aiomt_intr_htable[i],
80810535SVikram.Hegde@Sun.COM 		    amd_iommu_intr_handler, (void *)iommu, NULL)
80910535SVikram.Hegde@Sun.COM 		    != DDI_SUCCESS) {
81010535SVikram.Hegde@Sun.COM 			cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
81110535SVikram.Hegde@Sun.COM 			    "ddi_intr_add_handler failed: intr = %d, err = %d",
81210535SVikram.Hegde@Sun.COM 			    f, driver, instance, iommu->aiomt_idx, i, err);
81310535SVikram.Hegde@Sun.COM 			for (j = 0; j < i; j++) {
81410535SVikram.Hegde@Sun.COM 				(void) ddi_intr_remove_handler(
81510535SVikram.Hegde@Sun.COM 				    iommu->aiomt_intr_htable[j]);
81610535SVikram.Hegde@Sun.COM 			}
81710535SVikram.Hegde@Sun.COM 			amd_iommu_teardown_interrupts(iommu);
81810535SVikram.Hegde@Sun.COM 			return (DDI_FAILURE);
81910535SVikram.Hegde@Sun.COM 		}
82010535SVikram.Hegde@Sun.COM 	}
82110535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_state = AMD_IOMMU_INTR_HANDLER;
82210535SVikram.Hegde@Sun.COM 
82310535SVikram.Hegde@Sun.COM 	intrcap0 = intrcapN = -1;
82410535SVikram.Hegde@Sun.COM 	if (ddi_intr_get_cap(iommu->aiomt_intr_htable[0], &intrcap0)
82510535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS ||
82610535SVikram.Hegde@Sun.COM 	    ddi_intr_get_cap(
82710535SVikram.Hegde@Sun.COM 	    iommu->aiomt_intr_htable[iommu->aiomt_actual_intrs - 1], &intrcapN)
82810535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS || intrcap0 != intrcapN) {
82910535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
83010535SVikram.Hegde@Sun.COM 		    "ddi_intr_get_cap failed or inconsistent cap among "
83110535SVikram.Hegde@Sun.COM 		    "interrupts: intrcap0 (%d) < intrcapN (%d)",
83210535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx, intrcap0, intrcapN);
83310535SVikram.Hegde@Sun.COM 		amd_iommu_teardown_interrupts(iommu);
83410535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
83510535SVikram.Hegde@Sun.COM 	}
83610535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_cap = intrcap0;
83710535SVikram.Hegde@Sun.COM 
83810535SVikram.Hegde@Sun.COM 	if (intrcap0 & DDI_INTR_FLAG_BLOCK) {
83910535SVikram.Hegde@Sun.COM 		/* Need to call block enable */
84010535SVikram.Hegde@Sun.COM 		if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
84110535SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d: "
84210535SVikram.Hegde@Sun.COM 			    "Need to call block enable",
84310535SVikram.Hegde@Sun.COM 			    f, driver, instance, iommu->aiomt_idx);
84410535SVikram.Hegde@Sun.COM 		}
84510535SVikram.Hegde@Sun.COM 		if (ddi_intr_block_enable(iommu->aiomt_intr_htable,
84610535SVikram.Hegde@Sun.COM 		    iommu->aiomt_actual_intrs) != DDI_SUCCESS) {
84710535SVikram.Hegde@Sun.COM 			cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
84810535SVikram.Hegde@Sun.COM 			    "ddi_intr_block enable failed ", f, driver,
84910535SVikram.Hegde@Sun.COM 			    instance, iommu->aiomt_idx);
85010535SVikram.Hegde@Sun.COM 			(void) ddi_intr_block_disable(iommu->aiomt_intr_htable,
85110535SVikram.Hegde@Sun.COM 			    iommu->aiomt_actual_intrs);
85210535SVikram.Hegde@Sun.COM 			amd_iommu_teardown_interrupts(iommu);
85310535SVikram.Hegde@Sun.COM 			return (DDI_FAILURE);
85410535SVikram.Hegde@Sun.COM 		}
85510535SVikram.Hegde@Sun.COM 	} else {
85610535SVikram.Hegde@Sun.COM 		if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
85710535SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d: "
85810535SVikram.Hegde@Sun.COM 			    "Need to call individual enable",
85910535SVikram.Hegde@Sun.COM 			    f, driver, instance, iommu->aiomt_idx);
86010535SVikram.Hegde@Sun.COM 		}
86110535SVikram.Hegde@Sun.COM 		for (i = 0; i < iommu->aiomt_actual_intrs; i++) {
86210535SVikram.Hegde@Sun.COM 			if (ddi_intr_enable(iommu->aiomt_intr_htable[i])
86310535SVikram.Hegde@Sun.COM 			    != DDI_SUCCESS) {
86410535SVikram.Hegde@Sun.COM 				cmn_err(CE_WARN, "%s: %s%d: AMD IOMMU idx=%d: "
86510535SVikram.Hegde@Sun.COM 				    "ddi_intr_enable failed: intr = %d", f,
86610535SVikram.Hegde@Sun.COM 				    driver, instance, iommu->aiomt_idx, i);
86710535SVikram.Hegde@Sun.COM 				for (j = 0; j < i; j++) {
86810535SVikram.Hegde@Sun.COM 					(void) ddi_intr_disable(
86910535SVikram.Hegde@Sun.COM 					    iommu->aiomt_intr_htable[j]);
87010535SVikram.Hegde@Sun.COM 				}
87110535SVikram.Hegde@Sun.COM 				amd_iommu_teardown_interrupts(iommu);
87210535SVikram.Hegde@Sun.COM 				return (DDI_FAILURE);
87310535SVikram.Hegde@Sun.COM 			}
87410535SVikram.Hegde@Sun.COM 		}
87510535SVikram.Hegde@Sun.COM 	}
87610535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_state = AMD_IOMMU_INTR_ENABLED;
87710535SVikram.Hegde@Sun.COM 
87810535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_INTR) {
87910535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: AMD IOMMU idx=%d: "
88010535SVikram.Hegde@Sun.COM 		    "Interrupts successfully %s enabled. # of interrupts = %d",
88110535SVikram.Hegde@Sun.COM 		    f, driver, instance, iommu->aiomt_idx,
88210535SVikram.Hegde@Sun.COM 		    (intrcap0 & DDI_INTR_FLAG_BLOCK) ? "(block)" :
88310535SVikram.Hegde@Sun.COM 		    "(individually)", iommu->aiomt_actual_intrs);
88410535SVikram.Hegde@Sun.COM 	}
88510535SVikram.Hegde@Sun.COM 
88610535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
88710535SVikram.Hegde@Sun.COM }
88810535SVikram.Hegde@Sun.COM 
88910535SVikram.Hegde@Sun.COM static void
amd_iommu_teardown_interrupts(amd_iommu_t * iommu)89010535SVikram.Hegde@Sun.COM amd_iommu_teardown_interrupts(amd_iommu_t *iommu)
89110535SVikram.Hegde@Sun.COM {
89210535SVikram.Hegde@Sun.COM 	int i;
89310535SVikram.Hegde@Sun.COM 
89410535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_intr_state & AMD_IOMMU_INTR_ENABLED) {
89510535SVikram.Hegde@Sun.COM 		if (iommu->aiomt_intr_cap & DDI_INTR_FLAG_BLOCK) {
89610535SVikram.Hegde@Sun.COM 			(void) ddi_intr_block_disable(iommu->aiomt_intr_htable,
89710535SVikram.Hegde@Sun.COM 			    iommu->aiomt_actual_intrs);
89810535SVikram.Hegde@Sun.COM 		} else {
89910535SVikram.Hegde@Sun.COM 			for (i = 0; i < iommu->aiomt_actual_intrs; i++) {
90010535SVikram.Hegde@Sun.COM 				(void) ddi_intr_disable(
90110535SVikram.Hegde@Sun.COM 				    iommu->aiomt_intr_htable[i]);
90210535SVikram.Hegde@Sun.COM 			}
90310535SVikram.Hegde@Sun.COM 		}
90410535SVikram.Hegde@Sun.COM 	}
90510535SVikram.Hegde@Sun.COM 
90610535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_intr_state & AMD_IOMMU_INTR_HANDLER) {
90710535SVikram.Hegde@Sun.COM 		for (i = 0; i < iommu->aiomt_actual_intrs; i++) {
90810535SVikram.Hegde@Sun.COM 			(void) ddi_intr_remove_handler(
90910535SVikram.Hegde@Sun.COM 			    iommu->aiomt_intr_htable[i]);
91010535SVikram.Hegde@Sun.COM 		}
91110535SVikram.Hegde@Sun.COM 	}
91210535SVikram.Hegde@Sun.COM 
91310535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_intr_state & AMD_IOMMU_INTR_ALLOCED) {
91410535SVikram.Hegde@Sun.COM 		for (i = 0; i < iommu->aiomt_actual_intrs; i++) {
91510535SVikram.Hegde@Sun.COM 			(void) ddi_intr_free(iommu->aiomt_intr_htable[i]);
91610535SVikram.Hegde@Sun.COM 		}
91710535SVikram.Hegde@Sun.COM 	}
91810535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_intr_state & AMD_IOMMU_INTR_TABLE) {
91910535SVikram.Hegde@Sun.COM 		kmem_free(iommu->aiomt_intr_htable,
92010535SVikram.Hegde@Sun.COM 		    iommu->aiomt_intr_htable_sz);
92110535SVikram.Hegde@Sun.COM 	}
92210535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_htable = NULL;
92310535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_htable_sz = 0;
92410535SVikram.Hegde@Sun.COM 	iommu->aiomt_intr_state = AMD_IOMMU_INTR_INVALID;
92510535SVikram.Hegde@Sun.COM }
92610535SVikram.Hegde@Sun.COM 
92710535SVikram.Hegde@Sun.COM static amd_iommu_t *
amd_iommu_init(dev_info_t * dip,ddi_acc_handle_t handle,int idx,uint16_t cap_base)92810535SVikram.Hegde@Sun.COM amd_iommu_init(dev_info_t *dip, ddi_acc_handle_t handle, int idx,
92910535SVikram.Hegde@Sun.COM     uint16_t cap_base)
93010535SVikram.Hegde@Sun.COM {
93110535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu;
93210535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
93310535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
93410535SVikram.Hegde@Sun.COM 	uint32_t caphdr;
93510535SVikram.Hegde@Sun.COM 	uint32_t low_addr32;
93610535SVikram.Hegde@Sun.COM 	uint32_t hi_addr32;
93710535SVikram.Hegde@Sun.COM 	uint32_t range;
93810535SVikram.Hegde@Sun.COM 	uint32_t misc;
93910535SVikram.Hegde@Sun.COM 	uint64_t pgoffset;
94010535SVikram.Hegde@Sun.COM 	amd_iommu_acpi_global_t *global;
94110535SVikram.Hegde@Sun.COM 	amd_iommu_acpi_ivhd_t *hinfop;
94212203SJerry.Gilliam@Sun.COM 	int bus, device, func;
94310535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_init";
94410535SVikram.Hegde@Sun.COM 
94510535SVikram.Hegde@Sun.COM 	low_addr32 = PCI_CAP_GET32(handle, 0, cap_base,
94610535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CAP_ADDR_LOW_OFF);
94710535SVikram.Hegde@Sun.COM 	if (!(low_addr32 & AMD_IOMMU_REG_ADDR_LOCKED)) {
94810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: capability registers not locked. "
94910535SVikram.Hegde@Sun.COM 		    "Unable to use IOMMU unit idx=%d - skipping ...", f, driver,
95010535SVikram.Hegde@Sun.COM 		    instance, idx);
95110535SVikram.Hegde@Sun.COM 		return (NULL);
95210535SVikram.Hegde@Sun.COM 	}
95310535SVikram.Hegde@Sun.COM 
95410535SVikram.Hegde@Sun.COM 	iommu = kmem_zalloc(sizeof (amd_iommu_t), KM_SLEEP);
95510535SVikram.Hegde@Sun.COM 	mutex_init(&iommu->aiomt_mutex, NULL, MUTEX_DRIVER, NULL);
95610535SVikram.Hegde@Sun.COM 	mutex_enter(&iommu->aiomt_mutex);
95710535SVikram.Hegde@Sun.COM 
95810535SVikram.Hegde@Sun.COM 	mutex_init(&iommu->aiomt_cmdlock, NULL, MUTEX_DRIVER, NULL);
95910535SVikram.Hegde@Sun.COM 	mutex_init(&iommu->aiomt_eventlock, NULL, MUTEX_DRIVER, NULL);
96010535SVikram.Hegde@Sun.COM 
96110535SVikram.Hegde@Sun.COM 	iommu->aiomt_dip = dip;
96210535SVikram.Hegde@Sun.COM 	iommu->aiomt_idx = idx;
96310535SVikram.Hegde@Sun.COM 
96412203SJerry.Gilliam@Sun.COM 	if (acpica_get_bdf(iommu->aiomt_dip, &bus, &device, &func)
96512203SJerry.Gilliam@Sun.COM 	    != DDI_SUCCESS) {
96612203SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Failed to get BDF"
96712203SJerry.Gilliam@Sun.COM 		    "Unable to use IOMMU unit idx=%d - skipping ...",
96812203SJerry.Gilliam@Sun.COM 		    f, driver, instance, idx);
96912203SJerry.Gilliam@Sun.COM 		return (NULL);
97012203SJerry.Gilliam@Sun.COM 	}
97112203SJerry.Gilliam@Sun.COM 
97212203SJerry.Gilliam@Sun.COM 	iommu->aiomt_bdf = ((uint8_t)bus << 8) | ((uint8_t)device << 3) |
97312203SJerry.Gilliam@Sun.COM 	    (uint8_t)func;
97412203SJerry.Gilliam@Sun.COM 
97510535SVikram.Hegde@Sun.COM 	/*
97610535SVikram.Hegde@Sun.COM 	 * Since everything in the capability block is locked and RO at this
97710535SVikram.Hegde@Sun.COM 	 * point, copy everything into the IOMMU struct
97810535SVikram.Hegde@Sun.COM 	 */
97910535SVikram.Hegde@Sun.COM 
98010535SVikram.Hegde@Sun.COM 	/* Get cap header */
98110535SVikram.Hegde@Sun.COM 	caphdr = PCI_CAP_GET32(handle, 0, cap_base, AMD_IOMMU_CAP_HDR_OFF);
98210535SVikram.Hegde@Sun.COM 	iommu->aiomt_cap_hdr = caphdr;
98310535SVikram.Hegde@Sun.COM 	iommu->aiomt_npcache = AMD_IOMMU_REG_GET32(&caphdr,
98410535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CAP_NPCACHE);
98510535SVikram.Hegde@Sun.COM 	iommu->aiomt_httun = AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_HTTUN);
98610535SVikram.Hegde@Sun.COM 
98712203SJerry.Gilliam@Sun.COM 	global = amd_iommu_lookup_acpi_global();
98812203SJerry.Gilliam@Sun.COM 	hinfop = amd_iommu_lookup_any_ivhd(iommu);
98912203SJerry.Gilliam@Sun.COM 
99010535SVikram.Hegde@Sun.COM 	if (hinfop)
99110535SVikram.Hegde@Sun.COM 		iommu->aiomt_iotlb = hinfop->ach_IotlbSup;
99210535SVikram.Hegde@Sun.COM 	else
99310535SVikram.Hegde@Sun.COM 		iommu->aiomt_iotlb =
99410535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_IOTLB);
99510535SVikram.Hegde@Sun.COM 
99610535SVikram.Hegde@Sun.COM 	iommu->aiomt_captype = AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_TYPE);
99710535SVikram.Hegde@Sun.COM 	iommu->aiomt_capid = AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_ID);
99810535SVikram.Hegde@Sun.COM 
99910535SVikram.Hegde@Sun.COM 	/*
100010535SVikram.Hegde@Sun.COM 	 * Get address of IOMMU control registers
100110535SVikram.Hegde@Sun.COM 	 */
100210535SVikram.Hegde@Sun.COM 	hi_addr32 = PCI_CAP_GET32(handle, 0, cap_base,
100310535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CAP_ADDR_HI_OFF);
100410535SVikram.Hegde@Sun.COM 	iommu->aiomt_low_addr32 = low_addr32;
100510535SVikram.Hegde@Sun.COM 	iommu->aiomt_hi_addr32 = hi_addr32;
100610535SVikram.Hegde@Sun.COM 	low_addr32 &= ~AMD_IOMMU_REG_ADDR_LOCKED;
100710535SVikram.Hegde@Sun.COM 
100810535SVikram.Hegde@Sun.COM 	if (hinfop) {
100910535SVikram.Hegde@Sun.COM 		iommu->aiomt_reg_pa =  hinfop->ach_IOMMU_reg_base;
101010535SVikram.Hegde@Sun.COM 		ASSERT(hinfop->ach_IOMMU_pci_seg == 0);
101110535SVikram.Hegde@Sun.COM 	} else {
101210535SVikram.Hegde@Sun.COM 		iommu->aiomt_reg_pa =  ((uint64_t)hi_addr32 << 32 | low_addr32);
101310535SVikram.Hegde@Sun.COM 	}
101410535SVikram.Hegde@Sun.COM 
101510535SVikram.Hegde@Sun.COM 	/*
101610535SVikram.Hegde@Sun.COM 	 * Get cap range reg
101710535SVikram.Hegde@Sun.COM 	 */
101810535SVikram.Hegde@Sun.COM 	range = PCI_CAP_GET32(handle, 0, cap_base, AMD_IOMMU_CAP_RANGE_OFF);
101910535SVikram.Hegde@Sun.COM 	iommu->aiomt_range = range;
102010535SVikram.Hegde@Sun.COM 	iommu->aiomt_rng_valid = AMD_IOMMU_REG_GET32(&range,
102110535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_RNG_VALID);
102210535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_rng_valid) {
102310535SVikram.Hegde@Sun.COM 		iommu->aiomt_rng_bus = AMD_IOMMU_REG_GET32(&range,
102410535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_RNG_BUS);
102510535SVikram.Hegde@Sun.COM 		iommu->aiomt_first_devfn = AMD_IOMMU_REG_GET32(&range,
102610535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_FIRST_DEVFN);
102710535SVikram.Hegde@Sun.COM 		iommu->aiomt_last_devfn = AMD_IOMMU_REG_GET32(&range,
102810535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_LAST_DEVFN);
102910535SVikram.Hegde@Sun.COM 	} else {
103010535SVikram.Hegde@Sun.COM 		iommu->aiomt_rng_bus = 0;
103110535SVikram.Hegde@Sun.COM 		iommu->aiomt_first_devfn = 0;
103210535SVikram.Hegde@Sun.COM 		iommu->aiomt_last_devfn = 0;
103310535SVikram.Hegde@Sun.COM 	}
103410535SVikram.Hegde@Sun.COM 
103510535SVikram.Hegde@Sun.COM 	if (hinfop)
103610535SVikram.Hegde@Sun.COM 		iommu->aiomt_ht_unitid = hinfop->ach_IOMMU_UnitID;
103710535SVikram.Hegde@Sun.COM 	else
103810535SVikram.Hegde@Sun.COM 		iommu->aiomt_ht_unitid = AMD_IOMMU_REG_GET32(&range,
103910535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_HT_UNITID);
104010535SVikram.Hegde@Sun.COM 
104110535SVikram.Hegde@Sun.COM 	/*
104210535SVikram.Hegde@Sun.COM 	 * Get cap misc reg
104310535SVikram.Hegde@Sun.COM 	 */
104410535SVikram.Hegde@Sun.COM 	misc = PCI_CAP_GET32(handle, 0, cap_base, AMD_IOMMU_CAP_MISC_OFF);
104510535SVikram.Hegde@Sun.COM 	iommu->aiomt_misc = misc;
104610535SVikram.Hegde@Sun.COM 
104710535SVikram.Hegde@Sun.COM 	if (global) {
104810535SVikram.Hegde@Sun.COM 		iommu->aiomt_htatsresv = global->acg_HtAtsResv;
104910535SVikram.Hegde@Sun.COM 		iommu->aiomt_vasize = global->acg_VAsize;
105010535SVikram.Hegde@Sun.COM 		iommu->aiomt_pasize = global->acg_PAsize;
105110535SVikram.Hegde@Sun.COM 	} else {
105210535SVikram.Hegde@Sun.COM 		iommu->aiomt_htatsresv = AMD_IOMMU_REG_GET32(&misc,
105310535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_HT_ATSRSV);
105410535SVikram.Hegde@Sun.COM 		iommu->aiomt_vasize = AMD_IOMMU_REG_GET32(&misc,
105510535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_VA_SIZE);
105610535SVikram.Hegde@Sun.COM 		iommu->aiomt_pasize = AMD_IOMMU_REG_GET32(&misc,
105710535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_PA_SIZE);
105810535SVikram.Hegde@Sun.COM 	}
105910535SVikram.Hegde@Sun.COM 
106010535SVikram.Hegde@Sun.COM 	if (hinfop) {
106110535SVikram.Hegde@Sun.COM 		iommu->aiomt_msinum = hinfop->ach_IOMMU_MSInum;
106210535SVikram.Hegde@Sun.COM 	} else {
106310535SVikram.Hegde@Sun.COM 		iommu->aiomt_msinum =
106410535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_REG_GET32(&misc, AMD_IOMMU_MSINUM);
106510535SVikram.Hegde@Sun.COM 	}
106610535SVikram.Hegde@Sun.COM 
106710535SVikram.Hegde@Sun.COM 	/*
106810535SVikram.Hegde@Sun.COM 	 * Set up mapping between control registers PA and VA
106910535SVikram.Hegde@Sun.COM 	 */
107010535SVikram.Hegde@Sun.COM 	pgoffset = iommu->aiomt_reg_pa & MMU_PAGEOFFSET;
107110535SVikram.Hegde@Sun.COM 	ASSERT(pgoffset == 0);
107210535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_pages = mmu_btopr(AMD_IOMMU_REG_SIZE + pgoffset);
107310535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_size = mmu_ptob(iommu->aiomt_reg_pages);
107410535SVikram.Hegde@Sun.COM 
107510535SVikram.Hegde@Sun.COM 	iommu->aiomt_va = (uintptr_t)device_arena_alloc(
107610535SVikram.Hegde@Sun.COM 	    ptob(iommu->aiomt_reg_pages), VM_SLEEP);
107710535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_va == 0) {
107810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Failed to alloc VA for IOMMU "
107910535SVikram.Hegde@Sun.COM 		    "control regs. Skipping IOMMU idx=%d", f, driver,
108010535SVikram.Hegde@Sun.COM 		    instance, idx);
108110535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
108210536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
108310535SVikram.Hegde@Sun.COM 		return (NULL);
108410535SVikram.Hegde@Sun.COM 	}
108510535SVikram.Hegde@Sun.COM 
108610535SVikram.Hegde@Sun.COM 	hat_devload(kas.a_hat, (void *)(uintptr_t)iommu->aiomt_va,
108710535SVikram.Hegde@Sun.COM 	    iommu->aiomt_reg_size,
108810535SVikram.Hegde@Sun.COM 	    mmu_btop(iommu->aiomt_reg_pa), PROT_READ | PROT_WRITE
108910535SVikram.Hegde@Sun.COM 	    | HAT_STRICTORDER, HAT_LOAD_LOCK);
109010535SVikram.Hegde@Sun.COM 
109110535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_va = iommu->aiomt_va + pgoffset;
109210535SVikram.Hegde@Sun.COM 
109310535SVikram.Hegde@Sun.COM 	/*
109410535SVikram.Hegde@Sun.COM 	 * Setup the various control register's VA
109510535SVikram.Hegde@Sun.COM 	 */
109610535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_devtbl_va = iommu->aiomt_reg_va +
109710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_DEVTBL_REG_OFF;
109810535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_cmdbuf_va = iommu->aiomt_reg_va +
109910535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_REG_OFF;
110010535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_eventlog_va = iommu->aiomt_reg_va +
110110535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLOG_REG_OFF;
110210535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_ctrl_va = iommu->aiomt_reg_va +
110310535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CTRL_REG_OFF;
110410535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_excl_base_va = iommu->aiomt_reg_va +
110510535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EXCL_BASE_REG_OFF;
110610535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_excl_lim_va = iommu->aiomt_reg_va +
110710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EXCL_LIM_REG_OFF;
110810535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_cmdbuf_head_va = iommu->aiomt_reg_va +
110910535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_HEAD_REG_OFF;
111010535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_cmdbuf_tail_va = iommu->aiomt_reg_va +
111110535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_CMDBUF_TAIL_REG_OFF;
111210535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_eventlog_head_va = iommu->aiomt_reg_va +
111310535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLOG_HEAD_REG_OFF;
111410535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_eventlog_tail_va = iommu->aiomt_reg_va +
111510535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_EVENTLOG_TAIL_REG_OFF;
111610535SVikram.Hegde@Sun.COM 	iommu->aiomt_reg_status_va = iommu->aiomt_reg_va +
111710535SVikram.Hegde@Sun.COM 	    AMD_IOMMU_STATUS_REG_OFF;
111810535SVikram.Hegde@Sun.COM 
111910535SVikram.Hegde@Sun.COM 
112010535SVikram.Hegde@Sun.COM 	/*
112110535SVikram.Hegde@Sun.COM 	 * Setup the DEVICE table, CMD buffer, and LOG buffer in
112210535SVikram.Hegde@Sun.COM 	 * memory and setup DMA access to this memory location
112310535SVikram.Hegde@Sun.COM 	 */
112410535SVikram.Hegde@Sun.COM 	if (amd_iommu_setup_tables_and_buffers(iommu) != DDI_SUCCESS) {
112510535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
112610536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
112710535SVikram.Hegde@Sun.COM 		return (NULL);
112810535SVikram.Hegde@Sun.COM 	}
112910535SVikram.Hegde@Sun.COM 
113010535SVikram.Hegde@Sun.COM 	if (amd_iommu_setup_exclusion(iommu) != DDI_SUCCESS) {
113110535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
113210536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
113310535SVikram.Hegde@Sun.COM 		return (NULL);
113410535SVikram.Hegde@Sun.COM 	}
113510535SVikram.Hegde@Sun.COM 
113610535SVikram.Hegde@Sun.COM 	amd_iommu_enable_interrupts(iommu);
113710535SVikram.Hegde@Sun.COM 
113810535SVikram.Hegde@Sun.COM 	if (amd_iommu_setup_interrupts(iommu) != DDI_SUCCESS) {
113910535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
114010536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
114110535SVikram.Hegde@Sun.COM 		return (NULL);
114210535SVikram.Hegde@Sun.COM 	}
114310535SVikram.Hegde@Sun.COM 
114410535SVikram.Hegde@Sun.COM 	/*
114510535SVikram.Hegde@Sun.COM 	 * need to setup domain table before gfx bypass
114610535SVikram.Hegde@Sun.COM 	 */
114710535SVikram.Hegde@Sun.COM 	amd_iommu_init_page_tables(iommu);
114810535SVikram.Hegde@Sun.COM 
114910535SVikram.Hegde@Sun.COM 	/*
115010535SVikram.Hegde@Sun.COM 	 * Set pass-thru for special devices like IOAPIC and HPET
115110535SVikram.Hegde@Sun.COM 	 *
115210535SVikram.Hegde@Sun.COM 	 * Also, gfx devices don't use DDI for DMA. No need to register
115310535SVikram.Hegde@Sun.COM 	 * before setting up gfx passthru
115410535SVikram.Hegde@Sun.COM 	 */
115510535SVikram.Hegde@Sun.COM 	if (amd_iommu_setup_passthru(iommu) != DDI_SUCCESS) {
115610535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
115710536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
115810535SVikram.Hegde@Sun.COM 		return (NULL);
115910535SVikram.Hegde@Sun.COM 	}
116010535SVikram.Hegde@Sun.COM 
116112203SJerry.Gilliam@Sun.COM 	/* Initialize device table entries based on ACPI settings */
116212203SJerry.Gilliam@Sun.COM 	if (amd_iommu_acpi_init_devtbl(iommu) !=  DDI_SUCCESS) {
116312203SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Can't initialize device table",
116412203SJerry.Gilliam@Sun.COM 		    f, driver, instance);
116512203SJerry.Gilliam@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
116612203SJerry.Gilliam@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
116712203SJerry.Gilliam@Sun.COM 		return (NULL);
116812203SJerry.Gilliam@Sun.COM 	}
116912203SJerry.Gilliam@Sun.COM 
117010535SVikram.Hegde@Sun.COM 	if (amd_iommu_start(iommu) != DDI_SUCCESS) {
117110535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
117210536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
117310535SVikram.Hegde@Sun.COM 		return (NULL);
117410535SVikram.Hegde@Sun.COM 	}
117510535SVikram.Hegde@Sun.COM 
117610535SVikram.Hegde@Sun.COM 	/* xxx register/start race  */
117710535SVikram.Hegde@Sun.COM 	if (amd_iommu_register(iommu) != DDI_SUCCESS) {
117810535SVikram.Hegde@Sun.COM 		mutex_exit(&iommu->aiomt_mutex);
117910536SVikram.Hegde@Sun.COM 		(void) amd_iommu_fini(iommu, AMD_IOMMU_TEARDOWN);
118010535SVikram.Hegde@Sun.COM 		return (NULL);
118110535SVikram.Hegde@Sun.COM 	}
118210535SVikram.Hegde@Sun.COM 
118310535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug) {
118410535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: IOMMU idx=%d inited.", f, driver,
118510535SVikram.Hegde@Sun.COM 		    instance, idx);
118610535SVikram.Hegde@Sun.COM 	}
118710535SVikram.Hegde@Sun.COM 
118810535SVikram.Hegde@Sun.COM 	return (iommu);
118910535SVikram.Hegde@Sun.COM }
119010535SVikram.Hegde@Sun.COM 
119110535SVikram.Hegde@Sun.COM static int
amd_iommu_fini(amd_iommu_t * iommu,int type)119210536SVikram.Hegde@Sun.COM amd_iommu_fini(amd_iommu_t *iommu, int type)
119310535SVikram.Hegde@Sun.COM {
119410535SVikram.Hegde@Sun.COM 	int idx = iommu->aiomt_idx;
119510535SVikram.Hegde@Sun.COM 	dev_info_t *dip = iommu->aiomt_dip;
119610535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
119710535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
119810535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_fini";
119910535SVikram.Hegde@Sun.COM 
120010536SVikram.Hegde@Sun.COM 	if (type == AMD_IOMMU_TEARDOWN) {
120110536SVikram.Hegde@Sun.COM 		mutex_enter(&iommu->aiomt_mutex);
120210536SVikram.Hegde@Sun.COM 		if (amd_iommu_unregister(iommu) != DDI_SUCCESS) {
120310536SVikram.Hegde@Sun.COM 			cmn_err(CE_NOTE, "%s: %s%d: Fini of IOMMU unit failed. "
120410536SVikram.Hegde@Sun.COM 			    "idx = %d", f, driver, instance, idx);
120510536SVikram.Hegde@Sun.COM 			return (DDI_FAILURE);
120610536SVikram.Hegde@Sun.COM 		}
120710535SVikram.Hegde@Sun.COM 	}
120810536SVikram.Hegde@Sun.COM 
120910535SVikram.Hegde@Sun.COM 	amd_iommu_stop(iommu);
121010536SVikram.Hegde@Sun.COM 
121110536SVikram.Hegde@Sun.COM 	if (type == AMD_IOMMU_TEARDOWN) {
121210536SVikram.Hegde@Sun.COM 		amd_iommu_fini_page_tables(iommu);
121310536SVikram.Hegde@Sun.COM 		amd_iommu_teardown_interrupts(iommu);
121410536SVikram.Hegde@Sun.COM 		amd_iommu_teardown_exclusion(iommu);
121510536SVikram.Hegde@Sun.COM 	}
121610536SVikram.Hegde@Sun.COM 
121710536SVikram.Hegde@Sun.COM 	amd_iommu_teardown_tables_and_buffers(iommu, type);
121810536SVikram.Hegde@Sun.COM 
121910536SVikram.Hegde@Sun.COM 	if (type == AMD_IOMMU_QUIESCE)
122010536SVikram.Hegde@Sun.COM 		return (DDI_SUCCESS);
122110536SVikram.Hegde@Sun.COM 
122210535SVikram.Hegde@Sun.COM 	if (iommu->aiomt_va != NULL) {
122310535SVikram.Hegde@Sun.COM 		hat_unload(kas.a_hat, (void *)(uintptr_t)iommu->aiomt_va,
122410535SVikram.Hegde@Sun.COM 		    iommu->aiomt_reg_size, HAT_UNLOAD_UNLOCK);
122510535SVikram.Hegde@Sun.COM 		device_arena_free((void *)(uintptr_t)iommu->aiomt_va,
122610535SVikram.Hegde@Sun.COM 		    ptob(iommu->aiomt_reg_pages));
122710535SVikram.Hegde@Sun.COM 		iommu->aiomt_va = NULL;
122810535SVikram.Hegde@Sun.COM 		iommu->aiomt_reg_va = NULL;
122910535SVikram.Hegde@Sun.COM 	}
123010535SVikram.Hegde@Sun.COM 	mutex_destroy(&iommu->aiomt_eventlock);
123110535SVikram.Hegde@Sun.COM 	mutex_destroy(&iommu->aiomt_cmdlock);
123210535SVikram.Hegde@Sun.COM 	mutex_exit(&iommu->aiomt_mutex);
123310535SVikram.Hegde@Sun.COM 	mutex_destroy(&iommu->aiomt_mutex);
123410535SVikram.Hegde@Sun.COM 	kmem_free(iommu, sizeof (amd_iommu_t));
123510535SVikram.Hegde@Sun.COM 
123610535SVikram.Hegde@Sun.COM 	cmn_err(CE_NOTE, "%s: %s%d: Fini of IOMMU unit complete. idx = %d",
123710535SVikram.Hegde@Sun.COM 	    f, driver, instance, idx);
123810535SVikram.Hegde@Sun.COM 
123910535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
124010535SVikram.Hegde@Sun.COM }
124110535SVikram.Hegde@Sun.COM 
124210535SVikram.Hegde@Sun.COM int
amd_iommu_setup(dev_info_t * dip,amd_iommu_state_t * statep)124310535SVikram.Hegde@Sun.COM amd_iommu_setup(dev_info_t *dip, amd_iommu_state_t *statep)
124410535SVikram.Hegde@Sun.COM {
124510535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
124610535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
124710535SVikram.Hegde@Sun.COM 	ddi_acc_handle_t handle;
124810535SVikram.Hegde@Sun.COM 	uint8_t base_class;
124910535SVikram.Hegde@Sun.COM 	uint8_t sub_class;
125010535SVikram.Hegde@Sun.COM 	uint8_t prog_class;
125110535SVikram.Hegde@Sun.COM 	int idx;
125210535SVikram.Hegde@Sun.COM 	uint32_t id;
125310535SVikram.Hegde@Sun.COM 	uint16_t cap_base;
125410535SVikram.Hegde@Sun.COM 	uint32_t caphdr;
125510535SVikram.Hegde@Sun.COM 	uint8_t cap_type;
125610535SVikram.Hegde@Sun.COM 	uint8_t cap_id;
125710535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu;
125810535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_setup";
125910535SVikram.Hegde@Sun.COM 
126010535SVikram.Hegde@Sun.COM 	ASSERT(instance >= 0);
126110535SVikram.Hegde@Sun.COM 	ASSERT(driver);
126210535SVikram.Hegde@Sun.COM 
126310535SVikram.Hegde@Sun.COM 	/* First setup PCI access to config space */
126410535SVikram.Hegde@Sun.COM 
126510535SVikram.Hegde@Sun.COM 	if (pci_config_setup(dip, &handle) != DDI_SUCCESS) {
126610535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: PCI config setup failed: %s%d",
126710535SVikram.Hegde@Sun.COM 		    f, driver, instance);
126810535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
126910535SVikram.Hegde@Sun.COM 	}
127010535SVikram.Hegde@Sun.COM 
127110535SVikram.Hegde@Sun.COM 	/*
127210535SVikram.Hegde@Sun.COM 	 * The AMD IOMMU is part of an independent PCI function. There may be
127310535SVikram.Hegde@Sun.COM 	 * more than one IOMMU in that PCI function
127410535SVikram.Hegde@Sun.COM 	 */
127510535SVikram.Hegde@Sun.COM 	base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
127610535SVikram.Hegde@Sun.COM 	sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
127710535SVikram.Hegde@Sun.COM 	prog_class = pci_config_get8(handle, PCI_CONF_PROGCLASS);
127810535SVikram.Hegde@Sun.COM 
127910535SVikram.Hegde@Sun.COM 	if (base_class != PCI_CLASS_PERIPH || sub_class != PCI_PERIPH_IOMMU ||
128010535SVikram.Hegde@Sun.COM 	    prog_class != AMD_IOMMU_PCI_PROG_IF) {
128110535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: invalid PCI class(0x%x)/"
128210535SVikram.Hegde@Sun.COM 		    "subclass(0x%x)/programming interface(0x%x)", f, driver,
128310535SVikram.Hegde@Sun.COM 		    instance, base_class, sub_class, prog_class);
128410535SVikram.Hegde@Sun.COM 		pci_config_teardown(&handle);
128510535SVikram.Hegde@Sun.COM 		return (DDI_FAILURE);
128610535SVikram.Hegde@Sun.COM 	}
128710535SVikram.Hegde@Sun.COM 
128810535SVikram.Hegde@Sun.COM 	/*
128910535SVikram.Hegde@Sun.COM 	 * Find and initialize all IOMMU units in this function
129010535SVikram.Hegde@Sun.COM 	 */
129110535SVikram.Hegde@Sun.COM 	for (idx = 0; ; idx++) {
129210535SVikram.Hegde@Sun.COM 		if (pci_cap_probe(handle, idx, &id, &cap_base) != DDI_SUCCESS)
129310535SVikram.Hegde@Sun.COM 			break;
129410535SVikram.Hegde@Sun.COM 
129510535SVikram.Hegde@Sun.COM 		/* check if cap ID is secure device cap id */
129610535SVikram.Hegde@Sun.COM 		if (id != PCI_CAP_ID_SECURE_DEV) {
129710535SVikram.Hegde@Sun.COM 			if (amd_iommu_debug) {
129810536SVikram.Hegde@Sun.COM 				cmn_err(CE_NOTE,
129910535SVikram.Hegde@Sun.COM 				    "%s: %s%d: skipping IOMMU: idx(0x%x) "
130010535SVikram.Hegde@Sun.COM 				    "cap ID (0x%x) != secure dev capid (0x%x)",
130110535SVikram.Hegde@Sun.COM 				    f, driver, instance, idx, id,
130210535SVikram.Hegde@Sun.COM 				    PCI_CAP_ID_SECURE_DEV);
130310535SVikram.Hegde@Sun.COM 			}
130410535SVikram.Hegde@Sun.COM 			continue;
130510535SVikram.Hegde@Sun.COM 		}
130610535SVikram.Hegde@Sun.COM 
130710535SVikram.Hegde@Sun.COM 		/* check if cap type is IOMMU cap type */
130810535SVikram.Hegde@Sun.COM 		caphdr = PCI_CAP_GET32(handle, 0, cap_base,
130910535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_CAP_HDR_OFF);
131010535SVikram.Hegde@Sun.COM 		cap_type = AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_TYPE);
131110535SVikram.Hegde@Sun.COM 		cap_id = AMD_IOMMU_REG_GET32(&caphdr, AMD_IOMMU_CAP_ID);
131210535SVikram.Hegde@Sun.COM 
131310535SVikram.Hegde@Sun.COM 		if (cap_type != AMD_IOMMU_CAP) {
131410535SVikram.Hegde@Sun.COM 			cmn_err(CE_WARN, "%s: %s%d: skipping IOMMU: idx(0x%x) "
131510535SVikram.Hegde@Sun.COM 			    "cap type (0x%x) != AMD IOMMU CAP (0x%x)", f,
131610535SVikram.Hegde@Sun.COM 			    driver, instance, idx, cap_type, AMD_IOMMU_CAP);
131710535SVikram.Hegde@Sun.COM 			continue;
131810535SVikram.Hegde@Sun.COM 		}
131910535SVikram.Hegde@Sun.COM 		ASSERT(cap_id == PCI_CAP_ID_SECURE_DEV);
132010535SVikram.Hegde@Sun.COM 		ASSERT(cap_id == id);
132110535SVikram.Hegde@Sun.COM 
132210535SVikram.Hegde@Sun.COM 		iommu = amd_iommu_init(dip, handle, idx, cap_base);
132310535SVikram.Hegde@Sun.COM 		if (iommu == NULL) {
132410535SVikram.Hegde@Sun.COM 			cmn_err(CE_WARN, "%s: %s%d: skipping IOMMU: idx(0x%x) "
132510535SVikram.Hegde@Sun.COM 			    "failed to init IOMMU", f,
132610535SVikram.Hegde@Sun.COM 			    driver, instance, idx);
132710535SVikram.Hegde@Sun.COM 			continue;
132810535SVikram.Hegde@Sun.COM 		}
132910535SVikram.Hegde@Sun.COM 
133010535SVikram.Hegde@Sun.COM 		if (statep->aioms_iommu_start == NULL) {
133110535SVikram.Hegde@Sun.COM 			statep->aioms_iommu_start = iommu;
133210535SVikram.Hegde@Sun.COM 		} else {
133310535SVikram.Hegde@Sun.COM 			statep->aioms_iommu_end->aiomt_next = iommu;
133410535SVikram.Hegde@Sun.COM 		}
133510535SVikram.Hegde@Sun.COM 		statep->aioms_iommu_end = iommu;
133610535SVikram.Hegde@Sun.COM 
133710535SVikram.Hegde@Sun.COM 		statep->aioms_nunits++;
133810535SVikram.Hegde@Sun.COM 	}
133910535SVikram.Hegde@Sun.COM 
134010535SVikram.Hegde@Sun.COM 	pci_config_teardown(&handle);
134110535SVikram.Hegde@Sun.COM 
134210535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug) {
134310535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: state=%p: setup %d IOMMU units",
134410535SVikram.Hegde@Sun.COM 		    f, driver, instance, (void *)statep, statep->aioms_nunits);
134510535SVikram.Hegde@Sun.COM 	}
134610535SVikram.Hegde@Sun.COM 
134710535SVikram.Hegde@Sun.COM 	return (DDI_SUCCESS);
134810535SVikram.Hegde@Sun.COM }
134910535SVikram.Hegde@Sun.COM 
135010535SVikram.Hegde@Sun.COM int
amd_iommu_teardown(dev_info_t * dip,amd_iommu_state_t * statep,int type)135110536SVikram.Hegde@Sun.COM amd_iommu_teardown(dev_info_t *dip, amd_iommu_state_t *statep, int type)
135210535SVikram.Hegde@Sun.COM {
135310535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(dip);
135410535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(dip);
135510536SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu, *next_iommu;
135610535SVikram.Hegde@Sun.COM 	int teardown;
135710535SVikram.Hegde@Sun.COM 	int error = DDI_SUCCESS;
135810535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_teardown";
135910535SVikram.Hegde@Sun.COM 
136010535SVikram.Hegde@Sun.COM 	teardown = 0;
136110535SVikram.Hegde@Sun.COM 	for (iommu = statep->aioms_iommu_start; iommu;
136210536SVikram.Hegde@Sun.COM 	    iommu = next_iommu) {
136310535SVikram.Hegde@Sun.COM 		ASSERT(statep->aioms_nunits > 0);
136410536SVikram.Hegde@Sun.COM 		next_iommu = iommu->aiomt_next;
136510536SVikram.Hegde@Sun.COM 		if (amd_iommu_fini(iommu, type) != DDI_SUCCESS) {
136610535SVikram.Hegde@Sun.COM 			error = DDI_FAILURE;
136710535SVikram.Hegde@Sun.COM 			continue;
136810535SVikram.Hegde@Sun.COM 		}
136910535SVikram.Hegde@Sun.COM 		statep->aioms_nunits--;
137010535SVikram.Hegde@Sun.COM 		teardown++;
137110535SVikram.Hegde@Sun.COM 	}
137210535SVikram.Hegde@Sun.COM 
137310535SVikram.Hegde@Sun.COM 	cmn_err(CE_NOTE, "%s: %s%d: state=%p: toredown %d units. "
137410535SVikram.Hegde@Sun.COM 	    "%d units left", f, driver, instance, (void *)statep,
137510535SVikram.Hegde@Sun.COM 	    teardown, statep->aioms_nunits);
137610535SVikram.Hegde@Sun.COM 
137710535SVikram.Hegde@Sun.COM 	return (error);
137810535SVikram.Hegde@Sun.COM }
137910535SVikram.Hegde@Sun.COM 
138012203SJerry.Gilliam@Sun.COM dev_info_t *
amd_iommu_pci_dip(dev_info_t * rdip,const char * path)138112203SJerry.Gilliam@Sun.COM amd_iommu_pci_dip(dev_info_t *rdip, const char *path)
138212203SJerry.Gilliam@Sun.COM {
138312203SJerry.Gilliam@Sun.COM 	dev_info_t *pdip;
138412203SJerry.Gilliam@Sun.COM 	const char *driver = ddi_driver_name(rdip);
138512203SJerry.Gilliam@Sun.COM 	int instance = ddi_get_instance(rdip);
138612203SJerry.Gilliam@Sun.COM 	const char *f = "amd_iommu_pci_dip";
138712203SJerry.Gilliam@Sun.COM 
138812203SJerry.Gilliam@Sun.COM 	/* Hold rdip so it and its parents don't go away */
138912203SJerry.Gilliam@Sun.COM 	ndi_hold_devi(rdip);
139012203SJerry.Gilliam@Sun.COM 
139112203SJerry.Gilliam@Sun.COM 	if (ddi_is_pci_dip(rdip))
139212203SJerry.Gilliam@Sun.COM 		return (rdip);
139312203SJerry.Gilliam@Sun.COM 
139412203SJerry.Gilliam@Sun.COM 	pdip = rdip;
139512203SJerry.Gilliam@Sun.COM 	while (pdip = ddi_get_parent(pdip)) {
139612203SJerry.Gilliam@Sun.COM 		if (ddi_is_pci_dip(pdip)) {
139712203SJerry.Gilliam@Sun.COM 			ndi_hold_devi(pdip);
139812203SJerry.Gilliam@Sun.COM 			ndi_rele_devi(rdip);
139912203SJerry.Gilliam@Sun.COM 			return (pdip);
140012203SJerry.Gilliam@Sun.COM 		}
140112203SJerry.Gilliam@Sun.COM 	}
140212203SJerry.Gilliam@Sun.COM 
140312203SJerry.Gilliam@Sun.COM 	cmn_err(
140412203SJerry.Gilliam@Sun.COM #ifdef	DEBUG
140512203SJerry.Gilliam@Sun.COM 	    CE_PANIC,
140612203SJerry.Gilliam@Sun.COM #else
140712203SJerry.Gilliam@Sun.COM 	    CE_WARN,
140812203SJerry.Gilliam@Sun.COM #endif	/* DEBUG */
140912203SJerry.Gilliam@Sun.COM 	    "%s: %s%d dip = %p has no PCI parent, path = %s",
141012203SJerry.Gilliam@Sun.COM 	    f, driver, instance, (void *)rdip, path);
141112203SJerry.Gilliam@Sun.COM 
141212203SJerry.Gilliam@Sun.COM 	ndi_rele_devi(rdip);
141312203SJerry.Gilliam@Sun.COM 
141412203SJerry.Gilliam@Sun.COM 	return (NULL);
141512203SJerry.Gilliam@Sun.COM }
141612203SJerry.Gilliam@Sun.COM 
141710535SVikram.Hegde@Sun.COM /* Interface with IOMMULIB */
141810535SVikram.Hegde@Sun.COM /*ARGSUSED*/
141910535SVikram.Hegde@Sun.COM static int
amd_iommu_probe(iommulib_handle_t handle,dev_info_t * rdip)142010535SVikram.Hegde@Sun.COM amd_iommu_probe(iommulib_handle_t handle, dev_info_t *rdip)
142110535SVikram.Hegde@Sun.COM {
142210535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(rdip);
142310535SVikram.Hegde@Sun.COM 	char *s;
142412203SJerry.Gilliam@Sun.COM 	int bus, device, func, bdf;
142512203SJerry.Gilliam@Sun.COM 	amd_iommu_acpi_ivhd_t *hinfop;
142612203SJerry.Gilliam@Sun.COM 	dev_info_t *pci_dip;
142710535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu = iommulib_iommu_getdata(handle);
142812203SJerry.Gilliam@Sun.COM 	const char *f = "amd_iommu_probe";
142912203SJerry.Gilliam@Sun.COM 	int instance = ddi_get_instance(iommu->aiomt_dip);
143012203SJerry.Gilliam@Sun.COM 	const char *idriver = ddi_driver_name(iommu->aiomt_dip);
143112203SJerry.Gilliam@Sun.COM 	char *path, *pathp;
143210535SVikram.Hegde@Sun.COM 
143310535SVikram.Hegde@Sun.COM 	if (amd_iommu_disable_list) {
143410535SVikram.Hegde@Sun.COM 		s = strstr(amd_iommu_disable_list, driver);
143510535SVikram.Hegde@Sun.COM 		if (s == NULL)
143610535SVikram.Hegde@Sun.COM 			return (DDI_SUCCESS);
143710535SVikram.Hegde@Sun.COM 		if (s == amd_iommu_disable_list || *(s - 1) == ':') {
143810535SVikram.Hegde@Sun.COM 			s += strlen(driver);
143910535SVikram.Hegde@Sun.COM 			if (*s == '\0' || *s == ':') {
144010535SVikram.Hegde@Sun.COM 				amd_iommu_set_passthru(iommu, rdip);
144110535SVikram.Hegde@Sun.COM 				return (DDI_FAILURE);
144210535SVikram.Hegde@Sun.COM 			}
144310535SVikram.Hegde@Sun.COM 		}
144410535SVikram.Hegde@Sun.COM 	}
144510535SVikram.Hegde@Sun.COM 
144612203SJerry.Gilliam@Sun.COM 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
144712203SJerry.Gilliam@Sun.COM 	if ((pathp = ddi_pathname(rdip, path)) == NULL)
144812203SJerry.Gilliam@Sun.COM 		pathp = "<unknown>";
144912203SJerry.Gilliam@Sun.COM 
145012203SJerry.Gilliam@Sun.COM 	pci_dip = amd_iommu_pci_dip(rdip, path);
145112203SJerry.Gilliam@Sun.COM 	if (pci_dip == NULL) {
145212203SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: idx = %d, failed to get PCI dip "
145312203SJerry.Gilliam@Sun.COM 		    "for rdip=%p, path = %s",
145412203SJerry.Gilliam@Sun.COM 		    f, idriver, instance, iommu->aiomt_idx, (void *)rdip,
145512203SJerry.Gilliam@Sun.COM 		    pathp);
145612203SJerry.Gilliam@Sun.COM 		kmem_free(path, MAXPATHLEN);
145712203SJerry.Gilliam@Sun.COM 		return (DDI_FAILURE);
145812203SJerry.Gilliam@Sun.COM 	}
145912203SJerry.Gilliam@Sun.COM 
146012203SJerry.Gilliam@Sun.COM 	if (acpica_get_bdf(pci_dip, &bus, &device, &func) != DDI_SUCCESS) {
146112203SJerry.Gilliam@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: idx = %d, failed to get BDF "
146212203SJerry.Gilliam@Sun.COM 		    "for rdip=%p, path = %s",
146312203SJerry.Gilliam@Sun.COM 		    f, idriver, instance, iommu->aiomt_idx, (void *)rdip,
146412203SJerry.Gilliam@Sun.COM 		    pathp);
146512203SJerry.Gilliam@Sun.COM 		kmem_free(path, MAXPATHLEN);
146612203SJerry.Gilliam@Sun.COM 		return (DDI_FAILURE);
146712203SJerry.Gilliam@Sun.COM 	}
146812203SJerry.Gilliam@Sun.COM 	kmem_free(path, MAXPATHLEN);
146912203SJerry.Gilliam@Sun.COM 
147012203SJerry.Gilliam@Sun.COM 	/*
147112203SJerry.Gilliam@Sun.COM 	 * See whether device is described by IVRS as being managed
147212203SJerry.Gilliam@Sun.COM 	 * by this IOMMU
147312203SJerry.Gilliam@Sun.COM 	 */
147412203SJerry.Gilliam@Sun.COM 	bdf = ((uint8_t)bus << 8) | ((uint8_t)device << 3) | (uint8_t)func;
147512203SJerry.Gilliam@Sun.COM 	hinfop = amd_iommu_lookup_ivhd(bdf);
147612203SJerry.Gilliam@Sun.COM 	if (hinfop && hinfop->ach_IOMMU_deviceid == iommu->aiomt_bdf)
147712203SJerry.Gilliam@Sun.COM 		return (DDI_SUCCESS);
147812203SJerry.Gilliam@Sun.COM 
147912203SJerry.Gilliam@Sun.COM 	return (DDI_FAILURE);
148010535SVikram.Hegde@Sun.COM }
148110535SVikram.Hegde@Sun.COM 
148210535SVikram.Hegde@Sun.COM /*ARGSUSED*/
148310535SVikram.Hegde@Sun.COM static int
amd_iommu_allochdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_attr_t * attr,int (* waitfp)(caddr_t),caddr_t arg,ddi_dma_handle_t * dma_handlep)148410535SVikram.Hegde@Sun.COM amd_iommu_allochdl(iommulib_handle_t handle,
148510535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr,
148610535SVikram.Hegde@Sun.COM     int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep)
148710535SVikram.Hegde@Sun.COM {
148810535SVikram.Hegde@Sun.COM 	return (iommulib_iommu_dma_allochdl(dip, rdip, attr, waitfp,
148910535SVikram.Hegde@Sun.COM 	    arg, dma_handlep));
149010535SVikram.Hegde@Sun.COM }
149110535SVikram.Hegde@Sun.COM 
149210535SVikram.Hegde@Sun.COM /*ARGSUSED*/
149310535SVikram.Hegde@Sun.COM static int
amd_iommu_freehdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle)149410535SVikram.Hegde@Sun.COM amd_iommu_freehdl(iommulib_handle_t handle,
149510535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle)
149610535SVikram.Hegde@Sun.COM {
149710535SVikram.Hegde@Sun.COM 	return (iommulib_iommu_dma_freehdl(dip, rdip, dma_handle));
149810535SVikram.Hegde@Sun.COM }
149910535SVikram.Hegde@Sun.COM 
150010535SVikram.Hegde@Sun.COM /*ARGSUSED*/
150110535SVikram.Hegde@Sun.COM static int
map_current_window(amd_iommu_t * iommu,dev_info_t * rdip,ddi_dma_attr_t * attrp,struct ddi_dma_req * dmareq,ddi_dma_cookie_t * cookie_array,uint_t ccount,int km_flags)150210535SVikram.Hegde@Sun.COM map_current_window(amd_iommu_t *iommu, dev_info_t *rdip, ddi_dma_attr_t *attrp,
150310535SVikram.Hegde@Sun.COM     struct ddi_dma_req *dmareq, ddi_dma_cookie_t *cookie_array, uint_t ccount,
150410535SVikram.Hegde@Sun.COM     int km_flags)
150510535SVikram.Hegde@Sun.COM {
150610535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
150710535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(iommu->aiomt_dip);
150810535SVikram.Hegde@Sun.COM 	int idx = iommu->aiomt_idx;
150910535SVikram.Hegde@Sun.COM 	int i;
151010535SVikram.Hegde@Sun.COM 	uint64_t start_va;
151110535SVikram.Hegde@Sun.COM 	char *path;
151210535SVikram.Hegde@Sun.COM 	int error = DDI_FAILURE;
151310535SVikram.Hegde@Sun.COM 	const char *f = "map_current_window";
151410535SVikram.Hegde@Sun.COM 
151510535SVikram.Hegde@Sun.COM 	path = kmem_alloc(MAXPATHLEN, km_flags);
151610535SVikram.Hegde@Sun.COM 	if (path == NULL) {
151710535SVikram.Hegde@Sun.COM 		return (DDI_DMA_NORESOURCES);
151810535SVikram.Hegde@Sun.COM 	}
151910535SVikram.Hegde@Sun.COM 
152010535SVikram.Hegde@Sun.COM 	(void) ddi_pathname(rdip, path);
152110535SVikram.Hegde@Sun.COM 	mutex_enter(&amd_iommu_pgtable_lock);
152210535SVikram.Hegde@Sun.COM 
152310535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug == AMD_IOMMU_DEBUG_PAGE_TABLES) {
152410536SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s%d: idx=%d Attempting to get cookies "
152510535SVikram.Hegde@Sun.COM 		    "from handle for device %s",
152610535SVikram.Hegde@Sun.COM 		    f, driver, instance, idx, path);
152710535SVikram.Hegde@Sun.COM 	}
152810535SVikram.Hegde@Sun.COM 
152910535SVikram.Hegde@Sun.COM 	start_va = 0;
153010535SVikram.Hegde@Sun.COM 	for (i = 0; i < ccount; i++) {
153110535SVikram.Hegde@Sun.COM 		if ((error = amd_iommu_map_pa2va(iommu, rdip, attrp, dmareq,
153210535SVikram.Hegde@Sun.COM 		    cookie_array[i].dmac_cookie_addr,
153310535SVikram.Hegde@Sun.COM 		    cookie_array[i].dmac_size,
153410535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_VMEM_MAP, &start_va, km_flags)) != DDI_SUCCESS) {
153510535SVikram.Hegde@Sun.COM 			break;
153610535SVikram.Hegde@Sun.COM 		}
153710535SVikram.Hegde@Sun.COM 		cookie_array[i].dmac_cookie_addr = (uintptr_t)start_va;
153810535SVikram.Hegde@Sun.COM 		cookie_array[i].dmac_type = 0;
153910535SVikram.Hegde@Sun.COM 	}
154010535SVikram.Hegde@Sun.COM 
154110535SVikram.Hegde@Sun.COM 	if (i != ccount) {
154210535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: idx=%d Cannot map cookie# %d "
154310535SVikram.Hegde@Sun.COM 		    "for device %s", f, driver, instance, idx, i, path);
154410535SVikram.Hegde@Sun.COM 		(void) unmap_current_window(iommu, rdip, cookie_array,
154510535SVikram.Hegde@Sun.COM 		    ccount, i, 1);
154610535SVikram.Hegde@Sun.COM 		goto out;
154710535SVikram.Hegde@Sun.COM 	}
154810535SVikram.Hegde@Sun.COM 
154910535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_PAGE_TABLES) {
155010535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: return SUCCESS", f);
155110535SVikram.Hegde@Sun.COM 	}
155210535SVikram.Hegde@Sun.COM 
155310535SVikram.Hegde@Sun.COM 	error = DDI_DMA_MAPPED;
155410535SVikram.Hegde@Sun.COM out:
155510535SVikram.Hegde@Sun.COM 	mutex_exit(&amd_iommu_pgtable_lock);
155610535SVikram.Hegde@Sun.COM 	kmem_free(path, MAXPATHLEN);
155710535SVikram.Hegde@Sun.COM 	return (error);
155810535SVikram.Hegde@Sun.COM }
155910535SVikram.Hegde@Sun.COM 
156010535SVikram.Hegde@Sun.COM /*ARGSUSED*/
156110535SVikram.Hegde@Sun.COM static int
unmap_current_window(amd_iommu_t * iommu,dev_info_t * rdip,ddi_dma_cookie_t * cookie_array,uint_t ccount,int ncookies,int locked)156210535SVikram.Hegde@Sun.COM unmap_current_window(amd_iommu_t *iommu, dev_info_t *rdip,
156310535SVikram.Hegde@Sun.COM     ddi_dma_cookie_t *cookie_array, uint_t ccount, int ncookies, int locked)
156410535SVikram.Hegde@Sun.COM {
156510535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
156610535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(iommu->aiomt_dip);
156710535SVikram.Hegde@Sun.COM 	int idx = iommu->aiomt_idx;
156810535SVikram.Hegde@Sun.COM 	int i;
156910535SVikram.Hegde@Sun.COM 	int error = DDI_FAILURE;
157010535SVikram.Hegde@Sun.COM 	char *path;
157110535SVikram.Hegde@Sun.COM 	int pathfree;
157210535SVikram.Hegde@Sun.COM 	const char *f = "unmap_current_window";
157310535SVikram.Hegde@Sun.COM 
157410535SVikram.Hegde@Sun.COM 	if (!locked)
157510535SVikram.Hegde@Sun.COM 		mutex_enter(&amd_iommu_pgtable_lock);
157610535SVikram.Hegde@Sun.COM 
157710535SVikram.Hegde@Sun.COM 	path = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
157810535SVikram.Hegde@Sun.COM 	if (path) {
157910535SVikram.Hegde@Sun.COM 		(void) ddi_pathname(rdip, path);
158010535SVikram.Hegde@Sun.COM 		pathfree = 1;
158110535SVikram.Hegde@Sun.COM 	} else {
158210535SVikram.Hegde@Sun.COM 		path = "<path-mem-alloc-failed>";
158310535SVikram.Hegde@Sun.COM 		pathfree = 0;
158410535SVikram.Hegde@Sun.COM 	}
158510535SVikram.Hegde@Sun.COM 
158610535SVikram.Hegde@Sun.COM 	if (ncookies == -1)
158710535SVikram.Hegde@Sun.COM 		ncookies = ccount;
158810535SVikram.Hegde@Sun.COM 
158910535SVikram.Hegde@Sun.COM 	for (i = 0; i < ncookies; i++) {
159010535SVikram.Hegde@Sun.COM 		if (amd_iommu_unmap_va(iommu, rdip,
159110535SVikram.Hegde@Sun.COM 		    cookie_array[i].dmac_cookie_addr,
159210535SVikram.Hegde@Sun.COM 		    cookie_array[i].dmac_size,
159310535SVikram.Hegde@Sun.COM 		    AMD_IOMMU_VMEM_MAP) != DDI_SUCCESS) {
159410535SVikram.Hegde@Sun.COM 			break;
159510535SVikram.Hegde@Sun.COM 		}
159610535SVikram.Hegde@Sun.COM 	}
159710535SVikram.Hegde@Sun.COM 
159810535SVikram.Hegde@Sun.COM 	if (amd_iommu_cmd(iommu, AMD_IOMMU_CMD_COMPL_WAIT, NULL, 0, 0)
159910535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS) {
160010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: AMD IOMMU completion wait failed for: %s",
160110535SVikram.Hegde@Sun.COM 		    f, path);
160210535SVikram.Hegde@Sun.COM 	}
160310535SVikram.Hegde@Sun.COM 
160410535SVikram.Hegde@Sun.COM 	if (i != ncookies) {
160510535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: idx=%d Cannot unmap cookie# %d "
160610535SVikram.Hegde@Sun.COM 		    "for device %s", f, driver, instance, idx, i, path);
160710535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
160810535SVikram.Hegde@Sun.COM 		goto out;
160910535SVikram.Hegde@Sun.COM 	}
161010535SVikram.Hegde@Sun.COM 
161110535SVikram.Hegde@Sun.COM 	error = DDI_SUCCESS;
161210535SVikram.Hegde@Sun.COM 
161310535SVikram.Hegde@Sun.COM out:
161410535SVikram.Hegde@Sun.COM 	if (pathfree)
161510535SVikram.Hegde@Sun.COM 		kmem_free(path, MAXPATHLEN);
161610535SVikram.Hegde@Sun.COM 	if (!locked)
161710535SVikram.Hegde@Sun.COM 		mutex_exit(&amd_iommu_pgtable_lock);
161810535SVikram.Hegde@Sun.COM 	return (error);
161910535SVikram.Hegde@Sun.COM }
162010535SVikram.Hegde@Sun.COM 
162110535SVikram.Hegde@Sun.COM /*ARGSUSED*/
162210535SVikram.Hegde@Sun.COM static int
amd_iommu_bindhdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,struct ddi_dma_req * dmareq,ddi_dma_cookie_t * cookiep,uint_t * ccountp)162310535SVikram.Hegde@Sun.COM amd_iommu_bindhdl(iommulib_handle_t handle, dev_info_t *dip,
162410535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
162510535SVikram.Hegde@Sun.COM     struct ddi_dma_req *dmareq, ddi_dma_cookie_t *cookiep,
162610535SVikram.Hegde@Sun.COM     uint_t *ccountp)
162710535SVikram.Hegde@Sun.COM {
162810535SVikram.Hegde@Sun.COM 	int dma_error = DDI_DMA_NOMAPPING;
162910535SVikram.Hegde@Sun.COM 	int error;
163010535SVikram.Hegde@Sun.COM 	char *path;
163110535SVikram.Hegde@Sun.COM 	ddi_dma_cookie_t *cookie_array = NULL;
163210535SVikram.Hegde@Sun.COM 	uint_t ccount = 0;
163310535SVikram.Hegde@Sun.COM 	ddi_dma_impl_t *hp;
163410535SVikram.Hegde@Sun.COM 	ddi_dma_attr_t *attrp;
163510535SVikram.Hegde@Sun.COM 	int km_flags;
163610535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu = iommulib_iommu_getdata(handle);
163710535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(rdip);
163810535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(rdip);
163910535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_bindhdl";
164010535SVikram.Hegde@Sun.COM 
164110535SVikram.Hegde@Sun.COM 	dma_error = iommulib_iommu_dma_bindhdl(dip, rdip, dma_handle,
164210535SVikram.Hegde@Sun.COM 	    dmareq, cookiep, ccountp);
164310535SVikram.Hegde@Sun.COM 
164410535SVikram.Hegde@Sun.COM 	if (dma_error != DDI_DMA_MAPPED && dma_error != DDI_DMA_PARTIAL_MAP)
164510535SVikram.Hegde@Sun.COM 		return (dma_error);
164610535SVikram.Hegde@Sun.COM 
164710535SVikram.Hegde@Sun.COM 	km_flags = iommulib_iommu_dma_get_sleep_flags(dip, dma_handle);
164810535SVikram.Hegde@Sun.COM 
164910535SVikram.Hegde@Sun.COM 	path = kmem_alloc(MAXPATHLEN, km_flags);
165010535SVikram.Hegde@Sun.COM 	if (path) {
165110535SVikram.Hegde@Sun.COM 		(void) ddi_pathname(rdip, path);
165210535SVikram.Hegde@Sun.COM 	} else {
165310535SVikram.Hegde@Sun.COM 		dma_error = DDI_DMA_NORESOURCES;
165410535SVikram.Hegde@Sun.COM 		goto unbind;
165510535SVikram.Hegde@Sun.COM 	}
165610535SVikram.Hegde@Sun.COM 
165710535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_BIND) {
165810535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s got cookie (%p), #cookies: %d",
165910535SVikram.Hegde@Sun.COM 		    f, path,
166010535SVikram.Hegde@Sun.COM 		    (void *)cookiep->dmac_cookie_addr,
166110535SVikram.Hegde@Sun.COM 		    *ccountp);
166210535SVikram.Hegde@Sun.COM 	}
166310535SVikram.Hegde@Sun.COM 
166410535SVikram.Hegde@Sun.COM 	cookie_array = NULL;
166510535SVikram.Hegde@Sun.COM 	ccount = 0;
166610535SVikram.Hegde@Sun.COM 	if ((error = iommulib_iommu_dma_get_cookies(dip, dma_handle,
166710535SVikram.Hegde@Sun.COM 	    &cookie_array, &ccount)) != DDI_SUCCESS) {
166810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot get cookies "
166910535SVikram.Hegde@Sun.COM 		    "for device %s", f, driver, instance, path);
167010535SVikram.Hegde@Sun.COM 		dma_error = error;
167110535SVikram.Hegde@Sun.COM 		goto unbind;
167210535SVikram.Hegde@Sun.COM 	}
167310535SVikram.Hegde@Sun.COM 
167410535SVikram.Hegde@Sun.COM 	hp = (ddi_dma_impl_t *)dma_handle;
167510535SVikram.Hegde@Sun.COM 	attrp = &hp->dmai_attr;
167610535SVikram.Hegde@Sun.COM 
167710535SVikram.Hegde@Sun.COM 	error = map_current_window(iommu, rdip, attrp, dmareq,
167810535SVikram.Hegde@Sun.COM 	    cookie_array, ccount, km_flags);
167910535SVikram.Hegde@Sun.COM 	if (error != DDI_SUCCESS) {
168010535SVikram.Hegde@Sun.COM 		dma_error = error;
168110535SVikram.Hegde@Sun.COM 		goto unbind;
168210535SVikram.Hegde@Sun.COM 	}
168310535SVikram.Hegde@Sun.COM 
168410535SVikram.Hegde@Sun.COM 	if ((error = iommulib_iommu_dma_set_cookies(dip, dma_handle,
168510535SVikram.Hegde@Sun.COM 	    cookie_array, ccount)) != DDI_SUCCESS) {
168610535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot set cookies "
168710535SVikram.Hegde@Sun.COM 		    "for device %s", f, driver, instance, path);
168810535SVikram.Hegde@Sun.COM 		dma_error = error;
168910535SVikram.Hegde@Sun.COM 		goto unbind;
169010535SVikram.Hegde@Sun.COM 	}
169110535SVikram.Hegde@Sun.COM 
169210535SVikram.Hegde@Sun.COM 	*cookiep = cookie_array[0];
169310535SVikram.Hegde@Sun.COM 
169410535SVikram.Hegde@Sun.COM 	if (amd_iommu_debug & AMD_IOMMU_DEBUG_BIND) {
169510535SVikram.Hegde@Sun.COM 		cmn_err(CE_NOTE, "%s: %s remapped cookie (%p), #cookies: %d",
169610535SVikram.Hegde@Sun.COM 		    f, path,
169710535SVikram.Hegde@Sun.COM 		    (void *)(uintptr_t)cookiep->dmac_cookie_addr,
169810535SVikram.Hegde@Sun.COM 		    *ccountp);
169910535SVikram.Hegde@Sun.COM 	}
170010535SVikram.Hegde@Sun.COM 
170110535SVikram.Hegde@Sun.COM 	kmem_free(path, MAXPATHLEN);
170210535SVikram.Hegde@Sun.COM 	ASSERT(dma_error == DDI_DMA_MAPPED || dma_error == DDI_DMA_PARTIAL_MAP);
170310535SVikram.Hegde@Sun.COM 	return (dma_error);
170410535SVikram.Hegde@Sun.COM unbind:
170510535SVikram.Hegde@Sun.COM 	kmem_free(path, MAXPATHLEN);
170610535SVikram.Hegde@Sun.COM 	(void) iommulib_iommu_dma_unbindhdl(dip, rdip, dma_handle);
170710535SVikram.Hegde@Sun.COM 	return (dma_error);
170810535SVikram.Hegde@Sun.COM }
170910535SVikram.Hegde@Sun.COM 
171010535SVikram.Hegde@Sun.COM /*ARGSUSED*/
171110535SVikram.Hegde@Sun.COM static int
amd_iommu_unbindhdl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle)171210535SVikram.Hegde@Sun.COM amd_iommu_unbindhdl(iommulib_handle_t handle,
171310535SVikram.Hegde@Sun.COM     dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle)
171410535SVikram.Hegde@Sun.COM {
171510535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu = iommulib_iommu_getdata(handle);
171610535SVikram.Hegde@Sun.COM 	ddi_dma_cookie_t *cookie_array = NULL;
171710535SVikram.Hegde@Sun.COM 	uint_t ccount = 0;
171810535SVikram.Hegde@Sun.COM 	int error = DDI_FAILURE;
171910535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(rdip);
172010535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(rdip);
172110535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_unbindhdl";
172210535SVikram.Hegde@Sun.COM 
172310535SVikram.Hegde@Sun.COM 	cookie_array = NULL;
172410535SVikram.Hegde@Sun.COM 	ccount = 0;
172510535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_get_cookies(dip, dma_handle, &cookie_array,
172610535SVikram.Hegde@Sun.COM 	    &ccount) != DDI_SUCCESS) {
172710535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot get cookies "
172810535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
172910535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
173010535SVikram.Hegde@Sun.COM 		goto out;
173110535SVikram.Hegde@Sun.COM 	}
173210535SVikram.Hegde@Sun.COM 
173310535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_clear_cookies(dip, dma_handle) != DDI_SUCCESS) {
173410535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot clear cookies "
173510535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
173610535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
173710535SVikram.Hegde@Sun.COM 		goto out;
173810535SVikram.Hegde@Sun.COM 	}
173910535SVikram.Hegde@Sun.COM 
174010535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_unbindhdl(dip, rdip, dma_handle)
174110535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS) {
174210535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: failed to unbindhdl for dip=%p",
174310535SVikram.Hegde@Sun.COM 		    f, driver, instance, (void *)rdip);
174410535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
174510535SVikram.Hegde@Sun.COM 		goto out;
174610535SVikram.Hegde@Sun.COM 	}
174710535SVikram.Hegde@Sun.COM 
174810535SVikram.Hegde@Sun.COM 	if (unmap_current_window(iommu, rdip, cookie_array, ccount, -1, 0)
174910535SVikram.Hegde@Sun.COM 	    != DDI_SUCCESS) {
175010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: failed to unmap current window "
175110535SVikram.Hegde@Sun.COM 		    "for dip=%p", f, driver, instance, (void *)rdip);
175210535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
175310535SVikram.Hegde@Sun.COM 	} else {
175410535SVikram.Hegde@Sun.COM 		error = DDI_SUCCESS;
175510535SVikram.Hegde@Sun.COM 	}
175610535SVikram.Hegde@Sun.COM out:
175710535SVikram.Hegde@Sun.COM 	if (cookie_array)
175810535SVikram.Hegde@Sun.COM 		kmem_free(cookie_array, sizeof (ddi_dma_cookie_t) * ccount);
175910535SVikram.Hegde@Sun.COM 	return (error);
176010535SVikram.Hegde@Sun.COM }
176110535SVikram.Hegde@Sun.COM 
176210535SVikram.Hegde@Sun.COM /*ARGSUSED*/
176310535SVikram.Hegde@Sun.COM static int
amd_iommu_sync(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,off_t off,size_t len,uint_t cache_flags)176410535SVikram.Hegde@Sun.COM amd_iommu_sync(iommulib_handle_t handle, dev_info_t *dip,
176510535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off,
176610535SVikram.Hegde@Sun.COM     size_t len, uint_t cache_flags)
176710535SVikram.Hegde@Sun.COM {
176810535SVikram.Hegde@Sun.COM 	ddi_dma_cookie_t *cookie_array = NULL;
176910535SVikram.Hegde@Sun.COM 	uint_t ccount = 0;
177010535SVikram.Hegde@Sun.COM 	int error;
177110535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_sync";
177210535SVikram.Hegde@Sun.COM 
177310535SVikram.Hegde@Sun.COM 	cookie_array = NULL;
177410535SVikram.Hegde@Sun.COM 	ccount = 0;
177510535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_get_cookies(dip, dma_handle, &cookie_array,
177610535SVikram.Hegde@Sun.COM 	    &ccount) != DDI_SUCCESS) {
177710535SVikram.Hegde@Sun.COM 		ASSERT(cookie_array == NULL);
177810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: Cannot get cookies "
177910535SVikram.Hegde@Sun.COM 		    "for device %p", f, (void *)rdip);
178010535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
178110535SVikram.Hegde@Sun.COM 		goto out;
178210535SVikram.Hegde@Sun.COM 	}
178310535SVikram.Hegde@Sun.COM 
178410535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_clear_cookies(dip, dma_handle) != DDI_SUCCESS) {
178510535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: Cannot clear cookies "
178610535SVikram.Hegde@Sun.COM 		    "for device %p", f, (void *)rdip);
178710535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
178810535SVikram.Hegde@Sun.COM 		goto out;
178910535SVikram.Hegde@Sun.COM 	}
179010535SVikram.Hegde@Sun.COM 
179110535SVikram.Hegde@Sun.COM 	error = iommulib_iommu_dma_sync(dip, rdip, dma_handle, off,
179210535SVikram.Hegde@Sun.COM 	    len, cache_flags);
179310535SVikram.Hegde@Sun.COM 
179410535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_set_cookies(dip, dma_handle, cookie_array,
179510535SVikram.Hegde@Sun.COM 	    ccount) != DDI_SUCCESS) {
179610535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: Cannot set cookies "
179710535SVikram.Hegde@Sun.COM 		    "for device %p", f, (void *)rdip);
179810535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
179910535SVikram.Hegde@Sun.COM 	} else {
180010535SVikram.Hegde@Sun.COM 		cookie_array = NULL;
180110535SVikram.Hegde@Sun.COM 		ccount = 0;
180210535SVikram.Hegde@Sun.COM 	}
180310535SVikram.Hegde@Sun.COM 
180410535SVikram.Hegde@Sun.COM out:
180510535SVikram.Hegde@Sun.COM 	if (cookie_array)
180610535SVikram.Hegde@Sun.COM 		kmem_free(cookie_array, sizeof (ddi_dma_cookie_t) * ccount);
180710535SVikram.Hegde@Sun.COM 	return (error);
180810535SVikram.Hegde@Sun.COM }
180910535SVikram.Hegde@Sun.COM 
181010535SVikram.Hegde@Sun.COM /*ARGSUSED*/
181110535SVikram.Hegde@Sun.COM static int
amd_iommu_win(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,uint_t win,off_t * offp,size_t * lenp,ddi_dma_cookie_t * cookiep,uint_t * ccountp)181210535SVikram.Hegde@Sun.COM amd_iommu_win(iommulib_handle_t handle, dev_info_t *dip,
181310535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win,
181410535SVikram.Hegde@Sun.COM     off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep,
181510535SVikram.Hegde@Sun.COM     uint_t *ccountp)
181610535SVikram.Hegde@Sun.COM {
181710535SVikram.Hegde@Sun.COM 	int error = DDI_FAILURE;
181810535SVikram.Hegde@Sun.COM 	amd_iommu_t *iommu = iommulib_iommu_getdata(handle);
181910535SVikram.Hegde@Sun.COM 	ddi_dma_cookie_t *cookie_array = NULL;
182010535SVikram.Hegde@Sun.COM 	uint_t ccount = 0;
182110535SVikram.Hegde@Sun.COM 	int km_flags;
182210535SVikram.Hegde@Sun.COM 	ddi_dma_impl_t *hp;
182310535SVikram.Hegde@Sun.COM 	ddi_dma_attr_t *attrp;
182410535SVikram.Hegde@Sun.COM 	struct ddi_dma_req sdmareq = {0};
182510535SVikram.Hegde@Sun.COM 	int instance = ddi_get_instance(rdip);
182610535SVikram.Hegde@Sun.COM 	const char *driver = ddi_driver_name(rdip);
182710535SVikram.Hegde@Sun.COM 	const char *f = "amd_iommu_win";
182810535SVikram.Hegde@Sun.COM 
182910535SVikram.Hegde@Sun.COM 	km_flags = iommulib_iommu_dma_get_sleep_flags(dip, dma_handle);
183010535SVikram.Hegde@Sun.COM 
183110535SVikram.Hegde@Sun.COM 	cookie_array = NULL;
183210535SVikram.Hegde@Sun.COM 	ccount = 0;
183310535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_get_cookies(dip, dma_handle, &cookie_array,
183410535SVikram.Hegde@Sun.COM 	    &ccount) != DDI_SUCCESS) {
183510535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot get cookies "
183610535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
183710535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
183810535SVikram.Hegde@Sun.COM 		goto out;
183910535SVikram.Hegde@Sun.COM 	}
184010535SVikram.Hegde@Sun.COM 
184110535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_clear_cookies(dip, dma_handle) != DDI_SUCCESS) {
184210535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot clear cookies "
184310535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
184410535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
184510535SVikram.Hegde@Sun.COM 		goto out;
184610535SVikram.Hegde@Sun.COM 	}
184710535SVikram.Hegde@Sun.COM 
184810535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_win(dip, rdip, dma_handle, win,
184910535SVikram.Hegde@Sun.COM 	    offp, lenp, cookiep, ccountp) != DDI_SUCCESS) {
185010535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: failed switch windows for dip=%p",
185110535SVikram.Hegde@Sun.COM 		    f, driver, instance, (void *)rdip);
185210535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
185310535SVikram.Hegde@Sun.COM 		goto out;
185410535SVikram.Hegde@Sun.COM 	}
185510535SVikram.Hegde@Sun.COM 
185610535SVikram.Hegde@Sun.COM 	(void) unmap_current_window(iommu, rdip, cookie_array, ccount, -1, 0);
185710535SVikram.Hegde@Sun.COM 
185810535SVikram.Hegde@Sun.COM 	if (cookie_array) {
185910535SVikram.Hegde@Sun.COM 		kmem_free(cookie_array, sizeof (ddi_dma_cookie_t) * ccount);
186010535SVikram.Hegde@Sun.COM 		cookie_array = NULL;
186110535SVikram.Hegde@Sun.COM 		ccount = 0;
186210535SVikram.Hegde@Sun.COM 	}
186310535SVikram.Hegde@Sun.COM 
186410535SVikram.Hegde@Sun.COM 	cookie_array = NULL;
186510535SVikram.Hegde@Sun.COM 	ccount = 0;
186610535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_get_cookies(dip, dma_handle, &cookie_array,
186710535SVikram.Hegde@Sun.COM 	    &ccount) != DDI_SUCCESS) {
186810535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot get cookies "
186910535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
187010535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
187110535SVikram.Hegde@Sun.COM 		goto out;
187210535SVikram.Hegde@Sun.COM 	}
187310535SVikram.Hegde@Sun.COM 
187410535SVikram.Hegde@Sun.COM 	hp = (ddi_dma_impl_t *)dma_handle;
187510535SVikram.Hegde@Sun.COM 	attrp = &hp->dmai_attr;
187610535SVikram.Hegde@Sun.COM 
187710535SVikram.Hegde@Sun.COM 	sdmareq.dmar_flags = DDI_DMA_RDWR;
187810535SVikram.Hegde@Sun.COM 	error = map_current_window(iommu, rdip, attrp, &sdmareq,
187910535SVikram.Hegde@Sun.COM 	    cookie_array, ccount, km_flags);
188010535SVikram.Hegde@Sun.COM 
188110535SVikram.Hegde@Sun.COM 	if (iommulib_iommu_dma_set_cookies(dip, dma_handle, cookie_array,
188210535SVikram.Hegde@Sun.COM 	    ccount) != DDI_SUCCESS) {
188310535SVikram.Hegde@Sun.COM 		cmn_err(CE_WARN, "%s: %s%d: Cannot set cookies "
188410535SVikram.Hegde@Sun.COM 		    "for device %p", f, driver, instance, (void *)rdip);
188510535SVikram.Hegde@Sun.COM 		error = DDI_FAILURE;
188610535SVikram.Hegde@Sun.COM 		goto out;
188710535SVikram.Hegde@Sun.COM 	}
188810535SVikram.Hegde@Sun.COM 
188910535SVikram.Hegde@Sun.COM 	*cookiep = cookie_array[0];
189010535SVikram.Hegde@Sun.COM 
189110535SVikram.Hegde@Sun.COM 	return (error == DDI_SUCCESS ? DDI_SUCCESS : DDI_FAILURE);
189210535SVikram.Hegde@Sun.COM out:
189310535SVikram.Hegde@Sun.COM 	if (cookie_array)
189410535SVikram.Hegde@Sun.COM 		kmem_free(cookie_array, sizeof (ddi_dma_cookie_t) * ccount);
189510535SVikram.Hegde@Sun.COM 
189610535SVikram.Hegde@Sun.COM 	return (error);
189710535SVikram.Hegde@Sun.COM }
189810535SVikram.Hegde@Sun.COM 
189910535SVikram.Hegde@Sun.COM /* Obsoleted DMA routines */
190010535SVikram.Hegde@Sun.COM 
190110535SVikram.Hegde@Sun.COM /*ARGSUSED*/
190210535SVikram.Hegde@Sun.COM static int
amd_iommu_map(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,struct ddi_dma_req * dmareq,ddi_dma_handle_t * dma_handle)190310535SVikram.Hegde@Sun.COM amd_iommu_map(iommulib_handle_t handle, dev_info_t *dip,
190410535SVikram.Hegde@Sun.COM     dev_info_t *rdip, struct ddi_dma_req *dmareq,
190510535SVikram.Hegde@Sun.COM     ddi_dma_handle_t *dma_handle)
190610535SVikram.Hegde@Sun.COM {
190710535SVikram.Hegde@Sun.COM 	ASSERT(0);
190810535SVikram.Hegde@Sun.COM 	return (iommulib_iommu_dma_map(dip, rdip, dmareq, dma_handle));
190910535SVikram.Hegde@Sun.COM }
191010535SVikram.Hegde@Sun.COM 
191110535SVikram.Hegde@Sun.COM /*ARGSUSED*/
191210535SVikram.Hegde@Sun.COM static int
amd_iommu_mctl(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,enum ddi_dma_ctlops request,off_t * offp,size_t * lenp,caddr_t * objpp,uint_t cache_flags)191310535SVikram.Hegde@Sun.COM amd_iommu_mctl(iommulib_handle_t handle, dev_info_t *dip,
191410535SVikram.Hegde@Sun.COM     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
191510535SVikram.Hegde@Sun.COM     enum ddi_dma_ctlops request, off_t *offp, size_t *lenp,
191610535SVikram.Hegde@Sun.COM     caddr_t *objpp, uint_t cache_flags)
191710535SVikram.Hegde@Sun.COM {
191810535SVikram.Hegde@Sun.COM 	ASSERT(0);
191910535SVikram.Hegde@Sun.COM 	return (iommulib_iommu_dma_mctl(dip, rdip, dma_handle,
192010535SVikram.Hegde@Sun.COM 	    request, offp, lenp, objpp, cache_flags));
192110535SVikram.Hegde@Sun.COM }
192210535SVikram.Hegde@Sun.COM 
1923*13050Sfrank.van.der.linden@oracle.com /*ARGSUSED*/
1924*13050Sfrank.van.der.linden@oracle.com static int
amd_iommu_mapobject(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,struct ddi_dma_req * dmareq,ddi_dma_obj_t * dmao)1925*13050Sfrank.van.der.linden@oracle.com amd_iommu_mapobject(iommulib_handle_t handle, dev_info_t *dip,
1926*13050Sfrank.van.der.linden@oracle.com     dev_info_t *rdip, ddi_dma_handle_t dma_handle,
1927*13050Sfrank.van.der.linden@oracle.com     struct ddi_dma_req *dmareq, ddi_dma_obj_t *dmao)
1928*13050Sfrank.van.der.linden@oracle.com {
1929*13050Sfrank.van.der.linden@oracle.com 	return (DDI_ENOTSUP);
1930*13050Sfrank.van.der.linden@oracle.com }
1931*13050Sfrank.van.der.linden@oracle.com 
1932*13050Sfrank.van.der.linden@oracle.com /*ARGSUSED*/
1933*13050Sfrank.van.der.linden@oracle.com static int
amd_iommu_unmapobject(iommulib_handle_t handle,dev_info_t * dip,dev_info_t * rdip,ddi_dma_handle_t dma_handle,ddi_dma_obj_t * dmao)1934*13050Sfrank.van.der.linden@oracle.com amd_iommu_unmapobject(iommulib_handle_t handle, dev_info_t *dip,
1935*13050Sfrank.van.der.linden@oracle.com     dev_info_t *rdip, ddi_dma_handle_t dma_handle, ddi_dma_obj_t *dmao)
1936*13050Sfrank.van.der.linden@oracle.com {
1937*13050Sfrank.van.der.linden@oracle.com 	return (DDI_ENOTSUP);
1938*13050Sfrank.van.der.linden@oracle.com }
1939*13050Sfrank.van.der.linden@oracle.com 
194010535SVikram.Hegde@Sun.COM uint64_t
amd_iommu_reg_get64_workaround(uint64_t * regp,uint32_t bits)194110535SVikram.Hegde@Sun.COM amd_iommu_reg_get64_workaround(uint64_t *regp, uint32_t bits)
194210535SVikram.Hegde@Sun.COM {
194310535SVikram.Hegde@Sun.COM 	split_t s;
194410535SVikram.Hegde@Sun.COM 	uint32_t *ptr32 = (uint32_t *)regp;
194510535SVikram.Hegde@Sun.COM 	uint64_t *s64p = &(s.u64);
194610535SVikram.Hegde@Sun.COM 
194710535SVikram.Hegde@Sun.COM 	s.u32[0] = ptr32[0];
194810535SVikram.Hegde@Sun.COM 	s.u32[1] = ptr32[1];
194910535SVikram.Hegde@Sun.COM 
195010535SVikram.Hegde@Sun.COM 	return (AMD_IOMMU_REG_GET64_IMPL(s64p, bits));
195110535SVikram.Hegde@Sun.COM }
195210535SVikram.Hegde@Sun.COM 
195310535SVikram.Hegde@Sun.COM uint64_t
amd_iommu_reg_set64_workaround(uint64_t * regp,uint32_t bits,uint64_t value)195410535SVikram.Hegde@Sun.COM amd_iommu_reg_set64_workaround(uint64_t *regp, uint32_t bits, uint64_t value)
195510535SVikram.Hegde@Sun.COM {
195610535SVikram.Hegde@Sun.COM 	split_t s;
195710535SVikram.Hegde@Sun.COM 	uint32_t *ptr32 = (uint32_t *)regp;
195810535SVikram.Hegde@Sun.COM 	uint64_t *s64p = &(s.u64);
195910535SVikram.Hegde@Sun.COM 
196010535SVikram.Hegde@Sun.COM 	s.u32[0] = ptr32[0];
196110535SVikram.Hegde@Sun.COM 	s.u32[1] = ptr32[1];
196210535SVikram.Hegde@Sun.COM 
196310535SVikram.Hegde@Sun.COM 	AMD_IOMMU_REG_SET64_IMPL(s64p, bits, value);
196410535SVikram.Hegde@Sun.COM 
196510535SVikram.Hegde@Sun.COM 	*regp = s.u64;
196610535SVikram.Hegde@Sun.COM 
196710535SVikram.Hegde@Sun.COM 	return (s.u64);
196810535SVikram.Hegde@Sun.COM }
196910535SVikram.Hegde@Sun.COM 
197010535SVikram.Hegde@Sun.COM void
amd_iommu_read_boot_props(void)197110535SVikram.Hegde@Sun.COM amd_iommu_read_boot_props(void)
197210535SVikram.Hegde@Sun.COM {
197310535SVikram.Hegde@Sun.COM 	char *propval;
197410535SVikram.Hegde@Sun.COM 
197510535SVikram.Hegde@Sun.COM 	/*
197610535SVikram.Hegde@Sun.COM 	 * if "amd-iommu = no/false" boot property is set,
197710535SVikram.Hegde@Sun.COM 	 * ignore AMD iommu
197810535SVikram.Hegde@Sun.COM 	 */
197910535SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
198010535SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS, "amd-iommu", &propval) == DDI_SUCCESS) {
198110535SVikram.Hegde@Sun.COM 		if (strcmp(propval, "no") == 0 ||
198210535SVikram.Hegde@Sun.COM 		    strcmp(propval, "false") == 0) {
198310535SVikram.Hegde@Sun.COM 			amd_iommu_disable = 1;
198410535SVikram.Hegde@Sun.COM 		}
198510535SVikram.Hegde@Sun.COM 		ddi_prop_free(propval);
198610535SVikram.Hegde@Sun.COM 	}
198710535SVikram.Hegde@Sun.COM 
198810535SVikram.Hegde@Sun.COM 	/*
198910535SVikram.Hegde@Sun.COM 	 * Copy the list of drivers for which IOMMU is disabled by user.
199010535SVikram.Hegde@Sun.COM 	 */
199110535SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
199210535SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS, "amd-iommu-disable-list", &propval)
199310535SVikram.Hegde@Sun.COM 	    == DDI_SUCCESS) {
199410535SVikram.Hegde@Sun.COM 		amd_iommu_disable_list = kmem_alloc(strlen(propval) + 1,
199510535SVikram.Hegde@Sun.COM 		    KM_SLEEP);
199610535SVikram.Hegde@Sun.COM 		(void) strcpy(amd_iommu_disable_list, propval);
199710535SVikram.Hegde@Sun.COM 		ddi_prop_free(propval);
199810535SVikram.Hegde@Sun.COM 	}
199910535SVikram.Hegde@Sun.COM 
200010535SVikram.Hegde@Sun.COM }
200110535SVikram.Hegde@Sun.COM 
200210535SVikram.Hegde@Sun.COM void
amd_iommu_lookup_conf_props(dev_info_t * dip)200310535SVikram.Hegde@Sun.COM amd_iommu_lookup_conf_props(dev_info_t *dip)
200410535SVikram.Hegde@Sun.COM {
200510535SVikram.Hegde@Sun.COM 	char *disable;
200610535SVikram.Hegde@Sun.COM 
200710535SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
200810535SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS|DDI_PROP_NOTPROM, "amd-iommu", &disable)
200910535SVikram.Hegde@Sun.COM 	    == DDI_PROP_SUCCESS) {
201010535SVikram.Hegde@Sun.COM 		if (strcmp(disable, "no") == 0) {
201110535SVikram.Hegde@Sun.COM 			amd_iommu_disable = 1;
201210535SVikram.Hegde@Sun.COM 		}
201310535SVikram.Hegde@Sun.COM 		ddi_prop_free(disable);
201410535SVikram.Hegde@Sun.COM 	}
201510535SVikram.Hegde@Sun.COM 
201610535SVikram.Hegde@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
201710535SVikram.Hegde@Sun.COM 	    DDI_PROP_DONTPASS|DDI_PROP_NOTPROM, "amd-iommu-disable-list",
201810535SVikram.Hegde@Sun.COM 	    &disable) == DDI_PROP_SUCCESS) {
201910535SVikram.Hegde@Sun.COM 		amd_iommu_disable_list = kmem_alloc(strlen(disable) + 1,
202010535SVikram.Hegde@Sun.COM 		    KM_SLEEP);
202110535SVikram.Hegde@Sun.COM 		(void) strcpy(amd_iommu_disable_list, disable);
202210535SVikram.Hegde@Sun.COM 		ddi_prop_free(disable);
202310535SVikram.Hegde@Sun.COM 	}
202410535SVikram.Hegde@Sun.COM }
2025