17125fcbdSVikram Hegde /*
27125fcbdSVikram Hegde  * CDDL HEADER START
37125fcbdSVikram Hegde  *
47125fcbdSVikram Hegde  * The contents of this file are subject to the terms of the
57125fcbdSVikram Hegde  * Common Development and Distribution License (the "License").
67125fcbdSVikram Hegde  * You may not use this file except in compliance with the License.
77125fcbdSVikram Hegde  *
87125fcbdSVikram Hegde  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97125fcbdSVikram Hegde  * or http://www.opensolaris.org/os/licensing.
107125fcbdSVikram Hegde  * See the License for the specific language governing permissions
117125fcbdSVikram Hegde  * and limitations under the License.
127125fcbdSVikram Hegde  *
137125fcbdSVikram Hegde  * When distributing Covered Code, include this CDDL HEADER in each
147125fcbdSVikram Hegde  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157125fcbdSVikram Hegde  * If applicable, add the following below this CDDL HEADER, with the
167125fcbdSVikram Hegde  * fields enclosed by brackets "[]" replaced with your own identifying
177125fcbdSVikram Hegde  * information: Portions Copyright [yyyy] [name of copyright owner]
187125fcbdSVikram Hegde  *
197125fcbdSVikram Hegde  * CDDL HEADER END
207125fcbdSVikram Hegde  */
217125fcbdSVikram Hegde 
227125fcbdSVikram Hegde /*
23*6732dbb3SVikram Hegde  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247125fcbdSVikram Hegde  * Use is subject to license terms.
257125fcbdSVikram Hegde  */
267125fcbdSVikram Hegde 
277125fcbdSVikram Hegde #include <sys/sunddi.h>
287125fcbdSVikram Hegde #include <sys/amd_iommu.h>
297125fcbdSVikram Hegde #include "amd_iommu_impl.h"
307125fcbdSVikram Hegde 
3194f1124eSVikram Hegde extern int servicing_interrupt(void);
3294f1124eSVikram Hegde 
337125fcbdSVikram Hegde static void
amd_iommu_wait_for_completion(amd_iommu_t * iommu)347125fcbdSVikram Hegde amd_iommu_wait_for_completion(amd_iommu_t *iommu)
357125fcbdSVikram Hegde {
367125fcbdSVikram Hegde 	ASSERT(MUTEX_HELD(&iommu->aiomt_cmdlock));
3794f1124eSVikram Hegde 	while (AMD_IOMMU_REG_GET64(REGADDR64(
3894f1124eSVikram Hegde 	    iommu->aiomt_reg_status_va), AMD_IOMMU_COMWAIT_INT) != 1) {
3994f1124eSVikram Hegde 		AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
4094f1124eSVikram Hegde 		    AMD_IOMMU_CMDBUF_ENABLE, 1);
4194f1124eSVikram Hegde 		WAIT_SEC(1);
4294f1124eSVikram Hegde 	}
437125fcbdSVikram Hegde }
447125fcbdSVikram Hegde 
457125fcbdSVikram Hegde static int
create_compl_wait_cmd(amd_iommu_t * iommu,amd_iommu_cmdargs_t * cmdargsp,amd_iommu_cmd_flags_t flags,uint32_t * cmdptr)467125fcbdSVikram Hegde create_compl_wait_cmd(amd_iommu_t *iommu, amd_iommu_cmdargs_t *cmdargsp,
477125fcbdSVikram Hegde     amd_iommu_cmd_flags_t flags, uint32_t *cmdptr)
487125fcbdSVikram Hegde {
497125fcbdSVikram Hegde 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
507125fcbdSVikram Hegde 	int instance = ddi_get_instance(iommu->aiomt_dip);
517125fcbdSVikram Hegde 	const char *f = "create_compl_wait_cmd";
527125fcbdSVikram Hegde 
537125fcbdSVikram Hegde 	ASSERT(cmdargsp == NULL);
547125fcbdSVikram Hegde 
557125fcbdSVikram Hegde 	if (flags & AMD_IOMMU_CMD_FLAGS_COMPL_WAIT_S) {
567125fcbdSVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: idx=%d: 'store' completion "
577125fcbdSVikram Hegde 		    "not supported for completion wait command",
587125fcbdSVikram Hegde 		    f, driver, instance, iommu->aiomt_idx);
597125fcbdSVikram Hegde 		return (DDI_FAILURE);
607125fcbdSVikram Hegde 	}
617125fcbdSVikram Hegde 
6294f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_COMPL_WAIT_S, 0);
6394f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_COMPL_WAIT_I, 1);
6494f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_COMPL_WAIT_F,
657125fcbdSVikram Hegde 	    (flags & AMD_IOMMU_CMD_FLAGS_COMPL_WAIT_F) != 0);
6694f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_COMPL_WAIT_STORE_ADDR_LO,
6794f1124eSVikram Hegde 	    0);
6894f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_OPCODE, 0x01);
6994f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_COMPL_WAIT_STORE_ADDR_HI,
7094f1124eSVikram Hegde 	    0);
717125fcbdSVikram Hegde 	cmdptr[2] = 0;
727125fcbdSVikram Hegde 	cmdptr[3] = 0;
737125fcbdSVikram Hegde 
747125fcbdSVikram Hegde 	return (DDI_SUCCESS);
757125fcbdSVikram Hegde }
767125fcbdSVikram Hegde 
777125fcbdSVikram Hegde static int
create_inval_devtab_entry_cmd(amd_iommu_t * iommu,amd_iommu_cmdargs_t * cmdargsp,amd_iommu_cmd_flags_t flags,uint32_t * cmdptr)787125fcbdSVikram Hegde create_inval_devtab_entry_cmd(amd_iommu_t *iommu, amd_iommu_cmdargs_t *cmdargsp,
797125fcbdSVikram Hegde     amd_iommu_cmd_flags_t flags, uint32_t *cmdptr)
807125fcbdSVikram Hegde {
817125fcbdSVikram Hegde 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
827125fcbdSVikram Hegde 	int instance = ddi_get_instance(iommu->aiomt_dip);
837125fcbdSVikram Hegde 	const char *f = "create_inval_devtab_entry_cmd";
847125fcbdSVikram Hegde 	uint16_t deviceid;
857125fcbdSVikram Hegde 
867125fcbdSVikram Hegde 	ASSERT(cmdargsp);
877125fcbdSVikram Hegde 
887125fcbdSVikram Hegde 	if (flags != AMD_IOMMU_CMD_FLAGS_NONE) {
897125fcbdSVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: idx=%d: invalidate devtab entry "
907125fcbdSVikram Hegde 		    "no flags supported", f, driver, instance,
917125fcbdSVikram Hegde 		    iommu->aiomt_idx);
927125fcbdSVikram Hegde 		return (DDI_FAILURE);
937125fcbdSVikram Hegde 	}
947125fcbdSVikram Hegde 
957125fcbdSVikram Hegde 	deviceid = cmdargsp->ca_deviceid;
967125fcbdSVikram Hegde 
9794f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_INVAL_DEVTAB_DEVICEID,
987125fcbdSVikram Hegde 	    deviceid);
9994f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_OPCODE, 0x02);
1007125fcbdSVikram Hegde 	cmdptr[2] = 0;
1017125fcbdSVikram Hegde 	cmdptr[3] = 0;
1027125fcbdSVikram Hegde 
1037125fcbdSVikram Hegde 	return (DDI_SUCCESS);
1047125fcbdSVikram Hegde }
1057125fcbdSVikram Hegde 
1067125fcbdSVikram Hegde /*ARGSUSED*/
1077125fcbdSVikram Hegde static int
create_inval_iommu_pages_cmd(amd_iommu_t * iommu,amd_iommu_cmdargs_t * cmdargsp,amd_iommu_cmd_flags_t flags,uint32_t * cmdptr)1087125fcbdSVikram Hegde create_inval_iommu_pages_cmd(amd_iommu_t *iommu, amd_iommu_cmdargs_t *cmdargsp,
1097125fcbdSVikram Hegde     amd_iommu_cmd_flags_t flags, uint32_t *cmdptr)
1107125fcbdSVikram Hegde {
1117125fcbdSVikram Hegde 	uint32_t addr_lo;
1127125fcbdSVikram Hegde 	uint32_t addr_hi;
1137125fcbdSVikram Hegde 
1147125fcbdSVikram Hegde 	ASSERT(cmdargsp);
1157125fcbdSVikram Hegde 
11694f1124eSVikram Hegde 	addr_lo = AMD_IOMMU_REG_GET64(REGADDR64(&cmdargsp->ca_addr),
1177125fcbdSVikram Hegde 	    AMD_IOMMU_CMD_INVAL_PAGES_ADDR_LO);
11894f1124eSVikram Hegde 	addr_hi = AMD_IOMMU_REG_GET64(REGADDR64(&cmdargsp->ca_addr),
1197125fcbdSVikram Hegde 	    AMD_IOMMU_CMD_INVAL_PAGES_ADDR_HI);
1207125fcbdSVikram Hegde 
1217125fcbdSVikram Hegde 	cmdptr[0] = 0;
12294f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_INVAL_PAGES_DOMAINID,
1237125fcbdSVikram Hegde 	    cmdargsp->ca_domainid);
12494f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_OPCODE, 0x03);
12594f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[2], AMD_IOMMU_CMD_INVAL_PAGES_PDE,
1267125fcbdSVikram Hegde 	    (flags & AMD_IOMMU_CMD_FLAGS_PAGE_PDE_INVAL) != 0);
12794f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[2], AMD_IOMMU_CMD_INVAL_PAGES_S,
1287125fcbdSVikram Hegde 	    (flags & AMD_IOMMU_CMD_FLAGS_PAGE_INVAL_S) != 0);
12994f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[2], AMD_IOMMU_CMD_INVAL_PAGES_ADDR_LO,
1307125fcbdSVikram Hegde 	    addr_lo);
1317125fcbdSVikram Hegde 	cmdptr[3] = addr_hi;
1327125fcbdSVikram Hegde 
1337125fcbdSVikram Hegde 	return (DDI_SUCCESS);
1347125fcbdSVikram Hegde 
1357125fcbdSVikram Hegde }
1367125fcbdSVikram Hegde 
1377125fcbdSVikram Hegde /*ARGSUSED*/
1387125fcbdSVikram Hegde static int
create_inval_iotlb_pages_cmd(amd_iommu_t * iommu,amd_iommu_cmdargs_t * cmdargsp,amd_iommu_cmd_flags_t flags,uint32_t * cmdptr)1397125fcbdSVikram Hegde create_inval_iotlb_pages_cmd(amd_iommu_t *iommu, amd_iommu_cmdargs_t *cmdargsp,
1407125fcbdSVikram Hegde     amd_iommu_cmd_flags_t flags, uint32_t *cmdptr)
1417125fcbdSVikram Hegde {
1427125fcbdSVikram Hegde 	uint32_t addr_lo;
1437125fcbdSVikram Hegde 	uint32_t addr_hi;
1447125fcbdSVikram Hegde 
1457125fcbdSVikram Hegde 	ASSERT(cmdargsp);
1467125fcbdSVikram Hegde 
14794f1124eSVikram Hegde 	addr_lo = AMD_IOMMU_REG_GET64(REGADDR64(&cmdargsp->ca_addr),
1487125fcbdSVikram Hegde 	    AMD_IOMMU_CMD_INVAL_IOTLB_ADDR_LO);
14994f1124eSVikram Hegde 
15094f1124eSVikram Hegde 	addr_hi = AMD_IOMMU_REG_GET64(REGADDR64(&cmdargsp->ca_addr),
1517125fcbdSVikram Hegde 	    AMD_IOMMU_CMD_INVAL_IOTLB_ADDR_HI);
1527125fcbdSVikram Hegde 
15394f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_INVAL_IOTLB_DEVICEID,
1547125fcbdSVikram Hegde 	    cmdargsp->ca_deviceid);
15594f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_INVAL_IOTLB_MAXPEND,
1567125fcbdSVikram Hegde 	    AMD_IOMMU_DEFAULT_MAXPEND);
15794f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_OPCODE, 0x04);
15894f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_INVAL_IOTLB_QUEUEID,
1597125fcbdSVikram Hegde 	    cmdargsp->ca_deviceid);
16094f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[2], AMD_IOMMU_CMD_INVAL_IOTLB_ADDR_LO,
1617125fcbdSVikram Hegde 	    addr_lo);
16294f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[2], AMD_IOMMU_CMD_INVAL_IOTLB_S,
1637125fcbdSVikram Hegde 	    (flags & AMD_IOMMU_CMD_FLAGS_IOTLB_INVAL_S) != 0);
1647125fcbdSVikram Hegde 	cmdptr[3] = addr_hi;
1657125fcbdSVikram Hegde 
1667125fcbdSVikram Hegde 	return (DDI_SUCCESS);
1677125fcbdSVikram Hegde }
1687125fcbdSVikram Hegde 
1697125fcbdSVikram Hegde static int
create_inval_intr_table_cmd(amd_iommu_t * iommu,amd_iommu_cmdargs_t * cmdargsp,amd_iommu_cmd_flags_t flags,uint32_t * cmdptr)1707125fcbdSVikram Hegde create_inval_intr_table_cmd(amd_iommu_t *iommu, amd_iommu_cmdargs_t *cmdargsp,
1717125fcbdSVikram Hegde     amd_iommu_cmd_flags_t flags, uint32_t *cmdptr)
1727125fcbdSVikram Hegde {
1737125fcbdSVikram Hegde 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
1747125fcbdSVikram Hegde 	int instance = ddi_get_instance(iommu->aiomt_dip);
1757125fcbdSVikram Hegde 	const char *f = "create_inval_intr_table_cmd";
1767125fcbdSVikram Hegde 
1777125fcbdSVikram Hegde 	ASSERT(cmdargsp);
1787125fcbdSVikram Hegde 
1797125fcbdSVikram Hegde 	if (flags != AMD_IOMMU_CMD_FLAGS_NONE) {
1807125fcbdSVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: idx=%d: flags not supported "
1817125fcbdSVikram Hegde 		    "for invalidate interrupt table command",
1827125fcbdSVikram Hegde 		    f, driver, instance, iommu->aiomt_idx);
1837125fcbdSVikram Hegde 		return (DDI_FAILURE);
1847125fcbdSVikram Hegde 	}
1857125fcbdSVikram Hegde 
18694f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[0], AMD_IOMMU_CMD_INVAL_INTR_DEVICEID,
1877125fcbdSVikram Hegde 	    cmdargsp->ca_deviceid);
18894f1124eSVikram Hegde 	AMD_IOMMU_REG_SET32(&cmdptr[1], AMD_IOMMU_CMD_OPCODE, 0x05);
1897125fcbdSVikram Hegde 	cmdptr[2] = 0;
1907125fcbdSVikram Hegde 	cmdptr[3] = 0;
1917125fcbdSVikram Hegde 
1927125fcbdSVikram Hegde 	return (DDI_SUCCESS);
1937125fcbdSVikram Hegde }
1947125fcbdSVikram Hegde 
1957125fcbdSVikram Hegde int
amd_iommu_cmd(amd_iommu_t * iommu,amd_iommu_cmd_t cmd,amd_iommu_cmdargs_t * cmdargs,amd_iommu_cmd_flags_t flags,int lock_held)1967125fcbdSVikram Hegde amd_iommu_cmd(amd_iommu_t *iommu, amd_iommu_cmd_t cmd,
1977125fcbdSVikram Hegde     amd_iommu_cmdargs_t *cmdargs, amd_iommu_cmd_flags_t flags, int lock_held)
1987125fcbdSVikram Hegde {
1997125fcbdSVikram Hegde 	int error;
2007125fcbdSVikram Hegde 	int i;
2017125fcbdSVikram Hegde 	uint32_t cmdptr[4] = {0};
2027125fcbdSVikram Hegde 	const char *driver = ddi_driver_name(iommu->aiomt_dip);
2037125fcbdSVikram Hegde 	int instance = ddi_get_instance(iommu->aiomt_dip);
2047125fcbdSVikram Hegde 	uint64_t cmdhead_off;
2057125fcbdSVikram Hegde 	uint64_t cmdtail_off;
2067125fcbdSVikram Hegde 	const char *f = "amd_iommu_cmd";
2077125fcbdSVikram Hegde 
2087125fcbdSVikram Hegde 	ASSERT(lock_held == 0 || lock_held == 1);
2097125fcbdSVikram Hegde 	ASSERT(lock_held == 0 || MUTEX_HELD(&iommu->aiomt_cmdlock));
2107125fcbdSVikram Hegde 
2117125fcbdSVikram Hegde 	if (!lock_held)
2127125fcbdSVikram Hegde 		mutex_enter(&iommu->aiomt_cmdlock);
2137125fcbdSVikram Hegde 
2147125fcbdSVikram Hegde 	/*
2157125fcbdSVikram Hegde 	 * Prepare the command
2167125fcbdSVikram Hegde 	 */
2177125fcbdSVikram Hegde 	switch (cmd) {
2187125fcbdSVikram Hegde 	case AMD_IOMMU_CMD_COMPL_WAIT:
2197125fcbdSVikram Hegde 		if (flags & AMD_IOMMU_CMD_FLAGS_COMPL_WAIT) {
2207125fcbdSVikram Hegde 			cmn_err(CE_WARN, "%s: %s%d: idx=%d: No completion wait "
2217125fcbdSVikram Hegde 			    " after completion wait command",
2227125fcbdSVikram Hegde 			    f, driver, instance, iommu->aiomt_idx);
2237125fcbdSVikram Hegde 			error = DDI_FAILURE;
2247125fcbdSVikram Hegde 			goto out;
2257125fcbdSVikram Hegde 		}
2267125fcbdSVikram Hegde 		error = create_compl_wait_cmd(iommu, cmdargs, flags, cmdptr);
2277125fcbdSVikram Hegde 		break;
2287125fcbdSVikram Hegde 	case AMD_IOMMU_CMD_INVAL_DEVTAB_ENTRY:
2297125fcbdSVikram Hegde 		error = create_inval_devtab_entry_cmd(iommu, cmdargs,
2307125fcbdSVikram Hegde 		    flags & ~AMD_IOMMU_CMD_FLAGS_COMPL_WAIT, cmdptr);
2317125fcbdSVikram Hegde 		break;
2327125fcbdSVikram Hegde 	case AMD_IOMMU_CMD_INVAL_IOMMU_PAGES:
2337125fcbdSVikram Hegde 		error = create_inval_iommu_pages_cmd(iommu, cmdargs,
2347125fcbdSVikram Hegde 		    flags & ~AMD_IOMMU_CMD_FLAGS_COMPL_WAIT, cmdptr);
2357125fcbdSVikram Hegde 		break;
2367125fcbdSVikram Hegde 	case AMD_IOMMU_CMD_INVAL_IOTLB_PAGES:
2377125fcbdSVikram Hegde 		error = create_inval_iotlb_pages_cmd(iommu, cmdargs,
2387125fcbdSVikram Hegde 		    flags & ~AMD_IOMMU_CMD_FLAGS_COMPL_WAIT, cmdptr);
2397125fcbdSVikram Hegde 		break;
2407125fcbdSVikram Hegde 	case AMD_IOMMU_CMD_INVAL_INTR_TABLE:
2417125fcbdSVikram Hegde 		error = create_inval_intr_table_cmd(iommu, cmdargs,
2427125fcbdSVikram Hegde 		    flags & ~AMD_IOMMU_CMD_FLAGS_COMPL_WAIT, cmdptr);
2437125fcbdSVikram Hegde 		break;
2447125fcbdSVikram Hegde 	default:
2457125fcbdSVikram Hegde 		cmn_err(CE_WARN, "%s: %s%d: idx=%d: Unsupported cmd: %d",
2467125fcbdSVikram Hegde 		    f, driver, instance, iommu->aiomt_idx, cmd);
2477125fcbdSVikram Hegde 		error = DDI_FAILURE;
2487125fcbdSVikram Hegde 		goto out;
2497125fcbdSVikram Hegde 	}
2507125fcbdSVikram Hegde 
2517125fcbdSVikram Hegde 	if (error != DDI_SUCCESS) {
2527125fcbdSVikram Hegde 		error = DDI_FAILURE;
2537125fcbdSVikram Hegde 		goto out;
2547125fcbdSVikram Hegde 	}
2557125fcbdSVikram Hegde 
25694f1124eSVikram Hegde 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_ctrl_va),
25794f1124eSVikram Hegde 	    AMD_IOMMU_CMDBUF_ENABLE, 1);
25894f1124eSVikram Hegde 
2597125fcbdSVikram Hegde 	ASSERT(iommu->aiomt_cmd_tail != NULL);
2607125fcbdSVikram Hegde 
2617125fcbdSVikram Hegde 	for (i = 0; i < 4; i++) {
2627125fcbdSVikram Hegde 		iommu->aiomt_cmd_tail[i] = cmdptr[i];
2637125fcbdSVikram Hegde 	}
2647125fcbdSVikram Hegde 
2657125fcbdSVikram Hegde wait_for_drain:
26694f1124eSVikram Hegde 	cmdhead_off = AMD_IOMMU_REG_GET64(
26794f1124eSVikram Hegde 	    REGADDR64(iommu->aiomt_reg_cmdbuf_head_va),
2687125fcbdSVikram Hegde 	    AMD_IOMMU_CMDHEADPTR);
2697125fcbdSVikram Hegde 
2707125fcbdSVikram Hegde 	cmdhead_off = CMD2OFF(cmdhead_off);
2717125fcbdSVikram Hegde 
2727125fcbdSVikram Hegde 	ASSERT(cmdhead_off < iommu->aiomt_cmdbuf_sz);
2737125fcbdSVikram Hegde 
2747125fcbdSVikram Hegde 	/* check for overflow */
2757125fcbdSVikram Hegde 	if ((caddr_t)iommu->aiomt_cmd_tail <
2767125fcbdSVikram Hegde 	    (cmdhead_off + iommu->aiomt_cmdbuf)) {
2777125fcbdSVikram Hegde 		if ((caddr_t)iommu->aiomt_cmd_tail + 16 >=
2787125fcbdSVikram Hegde 		    (cmdhead_off + iommu->aiomt_cmdbuf))
2797125fcbdSVikram Hegde #ifdef DEBUG
2807125fcbdSVikram Hegde 			cmn_err(CE_WARN, "cmdbuffer overflow: waiting for "
2817125fcbdSVikram Hegde 			    "drain");
2827125fcbdSVikram Hegde #endif
2837125fcbdSVikram Hegde 			goto wait_for_drain;
2847125fcbdSVikram Hegde 	}
2857125fcbdSVikram Hegde 
2867125fcbdSVikram Hegde 	SYNC_FORDEV(iommu->aiomt_dmahdl);
2877125fcbdSVikram Hegde 
2887125fcbdSVikram Hegde 	/*
2897125fcbdSVikram Hegde 	 * Update the tail pointer in soft state
2907125fcbdSVikram Hegde 	 * and the tail pointer register
2917125fcbdSVikram Hegde 	 */
2927125fcbdSVikram Hegde 	iommu->aiomt_cmd_tail += 4;
2937125fcbdSVikram Hegde 	if ((caddr_t)iommu->aiomt_cmd_tail >= (iommu->aiomt_cmdbuf
2947125fcbdSVikram Hegde 	    + iommu->aiomt_cmdbuf_sz)) {
2957125fcbdSVikram Hegde 		/* wraparound */
2967125fcbdSVikram Hegde 		/*LINTED*/
2977125fcbdSVikram Hegde 		iommu->aiomt_cmd_tail = (uint32_t *)iommu->aiomt_cmdbuf;
2987125fcbdSVikram Hegde 		cmdtail_off = 0;
2997125fcbdSVikram Hegde 	} else {
3007125fcbdSVikram Hegde 		cmdtail_off = (caddr_t)iommu->aiomt_cmd_tail
3017125fcbdSVikram Hegde 		/*LINTED*/
3027125fcbdSVikram Hegde 		    - iommu->aiomt_cmdbuf;
3037125fcbdSVikram Hegde 	}
3047125fcbdSVikram Hegde 
3057125fcbdSVikram Hegde 	ASSERT(cmdtail_off < iommu->aiomt_cmdbuf_sz);
3067125fcbdSVikram Hegde 
30794f1124eSVikram Hegde 	AMD_IOMMU_REG_SET64(REGADDR64(iommu->aiomt_reg_cmdbuf_tail_va),
3087125fcbdSVikram Hegde 	    AMD_IOMMU_CMDTAILPTR, OFF2CMD(cmdtail_off));
3097125fcbdSVikram Hegde 
3107125fcbdSVikram Hegde 	if (cmd == AMD_IOMMU_CMD_COMPL_WAIT) {
3117125fcbdSVikram Hegde 		amd_iommu_wait_for_completion(iommu);
3127125fcbdSVikram Hegde 	} else if (flags & AMD_IOMMU_CMD_FLAGS_COMPL_WAIT) {
3137125fcbdSVikram Hegde 		error = amd_iommu_cmd(iommu, AMD_IOMMU_CMD_COMPL_WAIT,
31494f1124eSVikram Hegde 		    NULL, 0, 1);
3157125fcbdSVikram Hegde 	}
3167125fcbdSVikram Hegde 
3177125fcbdSVikram Hegde out:
3187125fcbdSVikram Hegde 	if (!lock_held)
3197125fcbdSVikram Hegde 		mutex_exit(&iommu->aiomt_cmdlock);
3207125fcbdSVikram Hegde 	return (error);
3217125fcbdSVikram Hegde }
322