19e39c5baSBill Taylor /*
29e39c5baSBill Taylor  * CDDL HEADER START
39e39c5baSBill Taylor  *
49e39c5baSBill Taylor  * The contents of this file are subject to the terms of the
59e39c5baSBill Taylor  * Common Development and Distribution License (the "License").
69e39c5baSBill Taylor  * You may not use this file except in compliance with the License.
79e39c5baSBill Taylor  *
89e39c5baSBill Taylor  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99e39c5baSBill Taylor  * or http://www.opensolaris.org/os/licensing.
109e39c5baSBill Taylor  * See the License for the specific language governing permissions
119e39c5baSBill Taylor  * and limitations under the License.
129e39c5baSBill Taylor  *
139e39c5baSBill Taylor  * When distributing Covered Code, include this CDDL HEADER in each
149e39c5baSBill Taylor  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159e39c5baSBill Taylor  * If applicable, add the following below this CDDL HEADER, with the
169e39c5baSBill Taylor  * fields enclosed by brackets "[]" replaced with your own identifying
179e39c5baSBill Taylor  * information: Portions Copyright [yyyy] [name of copyright owner]
189e39c5baSBill Taylor  *
199e39c5baSBill Taylor  * CDDL HEADER END
209e39c5baSBill Taylor  */
219e39c5baSBill Taylor 
229e39c5baSBill Taylor /*
23*13cc0a0bSBill Taylor  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249e39c5baSBill Taylor  */
259e39c5baSBill Taylor 
269e39c5baSBill Taylor /*
279e39c5baSBill Taylor  * hermon_ioctl.c
289e39c5baSBill Taylor  *    Hemron IOCTL Routines
299e39c5baSBill Taylor  *
309e39c5baSBill Taylor  *    Implements all ioctl access into the driver.  This includes all routines
319e39c5baSBill Taylor  *    necessary for updating firmware, accessing the hermon flash device, and
329e39c5baSBill Taylor  *    providing interfaces for VTS.
339e39c5baSBill Taylor  */
349e39c5baSBill Taylor 
359e39c5baSBill Taylor #include <sys/types.h>
369e39c5baSBill Taylor #include <sys/conf.h>
379e39c5baSBill Taylor #include <sys/ddi.h>
389e39c5baSBill Taylor #include <sys/sunddi.h>
399e39c5baSBill Taylor #include <sys/modctl.h>
409e39c5baSBill Taylor #include <sys/file.h>
419e39c5baSBill Taylor 
429e39c5baSBill Taylor #include <sys/ib/adapters/hermon/hermon.h>
439e39c5baSBill Taylor 
449e39c5baSBill Taylor /* Hemron HCA state pointer (extern) */
459e39c5baSBill Taylor extern void	*hermon_statep;
469e39c5baSBill Taylor extern int	hermon_verbose;
479e39c5baSBill Taylor 
489e39c5baSBill Taylor #define	DO_WRCONF	1
499e39c5baSBill Taylor static int do_bar0 = 1;
509e39c5baSBill Taylor 
519e39c5baSBill Taylor /*
529e39c5baSBill Taylor  * The ioctl declarations (for firmware flash burning, register read/write
539e39c5baSBill Taylor  * (DEBUG-only), and VTS interfaces)
549e39c5baSBill Taylor  */
559e39c5baSBill Taylor static int hermon_ioctl_flash_read(hermon_state_t *state, dev_t dev,
569e39c5baSBill Taylor     intptr_t arg, int mode);
579e39c5baSBill Taylor static int hermon_ioctl_flash_write(hermon_state_t *state, dev_t dev,
589e39c5baSBill Taylor     intptr_t arg, int mode);
599e39c5baSBill Taylor static int hermon_ioctl_flash_erase(hermon_state_t *state, dev_t dev,
609e39c5baSBill Taylor     intptr_t arg, int mode);
619e39c5baSBill Taylor static int hermon_ioctl_flash_init(hermon_state_t *state, dev_t dev,
629e39c5baSBill Taylor     intptr_t arg, int mode);
639e39c5baSBill Taylor static int hermon_ioctl_flash_fini(hermon_state_t *state, dev_t dev);
649e39c5baSBill Taylor static int hermon_ioctl_flash_cleanup(hermon_state_t *state);
659e39c5baSBill Taylor static int hermon_ioctl_flash_cleanup_nolock(hermon_state_t *state);
669e39c5baSBill Taylor #ifdef	DEBUG
679e39c5baSBill Taylor static int hermon_ioctl_reg_write(hermon_state_t *state, intptr_t arg,
689e39c5baSBill Taylor     int mode);
699e39c5baSBill Taylor static int hermon_ioctl_reg_read(hermon_state_t *state, intptr_t arg,
709e39c5baSBill Taylor     int mode);
719e39c5baSBill Taylor #endif	/* DEBUG */
729e39c5baSBill Taylor static int hermon_ioctl_write_boot_addr(hermon_state_t *state, dev_t dev,
739e39c5baSBill Taylor     intptr_t arg, int mode);
749e39c5baSBill Taylor static int hermon_ioctl_info(hermon_state_t *state, dev_t dev,
759e39c5baSBill Taylor     intptr_t arg, int mode);
769e39c5baSBill Taylor static int hermon_ioctl_ports(hermon_state_t *state, intptr_t arg,
779e39c5baSBill Taylor     int mode);
789e39c5baSBill Taylor static int hermon_ioctl_loopback(hermon_state_t *state, intptr_t arg,
799e39c5baSBill Taylor     int mode);
809e39c5baSBill Taylor 
819e39c5baSBill Taylor /* Hemron Flash Functions */
829e39c5baSBill Taylor static void hermon_flash_spi_exec_command(hermon_state_t *state,
839e39c5baSBill Taylor     ddi_acc_handle_t hdl, uint32_t cmd);
849e39c5baSBill Taylor static int hermon_flash_read_sector(hermon_state_t *state,
859e39c5baSBill Taylor     uint32_t sector_num);
869e39c5baSBill Taylor static int hermon_flash_read_quadlet(hermon_state_t *state, uint32_t *data,
879e39c5baSBill Taylor     uint32_t addr);
889e39c5baSBill Taylor static int hermon_flash_write_sector(hermon_state_t *state,
899e39c5baSBill Taylor     uint32_t sector_num);
909e39c5baSBill Taylor static int hermon_flash_spi_write_dword(hermon_state_t *state,
919e39c5baSBill Taylor     uint32_t addr, uint32_t data);
929e39c5baSBill Taylor static int hermon_flash_write_byte(hermon_state_t *state, uint32_t addr,
939e39c5baSBill Taylor     uchar_t data);
949e39c5baSBill Taylor static int hermon_flash_erase_sector(hermon_state_t *state,
959e39c5baSBill Taylor     uint32_t sector_num);
969e39c5baSBill Taylor static int hermon_flash_erase_chip(hermon_state_t *state);
979e39c5baSBill Taylor static int hermon_flash_bank(hermon_state_t *state, uint32_t addr);
989e39c5baSBill Taylor static uint32_t hermon_flash_read(hermon_state_t *state, uint32_t addr,
999e39c5baSBill Taylor     int *err);
1009e39c5baSBill Taylor static void hermon_flash_write(hermon_state_t *state, uint32_t addr,
1019e39c5baSBill Taylor     uchar_t data, int *err);
1029e39c5baSBill Taylor static int hermon_flash_spi_wait_wip(hermon_state_t *state);
1039e39c5baSBill Taylor static void hermon_flash_spi_write_enable(hermon_state_t *state);
1049e39c5baSBill Taylor static int hermon_flash_init(hermon_state_t *state);
1059e39c5baSBill Taylor static int hermon_flash_cfi_init(hermon_state_t *state, uint32_t *cfi_info,
1069e39c5baSBill Taylor     int *intel_xcmd);
1079e39c5baSBill Taylor static int hermon_flash_fini(hermon_state_t *state);
1089e39c5baSBill Taylor static int hermon_flash_reset(hermon_state_t *state);
1099e39c5baSBill Taylor static uint32_t hermon_flash_read_cfg(hermon_state_t *state,
1109e39c5baSBill Taylor     ddi_acc_handle_t pci_config_hdl, uint32_t addr);
1119e39c5baSBill Taylor #ifdef DO_WRCONF
1129e39c5baSBill Taylor static void hermon_flash_write_cfg(hermon_state_t *state,
1139e39c5baSBill Taylor     ddi_acc_handle_t pci_config_hdl, uint32_t addr, uint32_t data);
1149e39c5baSBill Taylor static void hermon_flash_write_cfg_helper(hermon_state_t *state,
1159e39c5baSBill Taylor     ddi_acc_handle_t pci_config_hdl, uint32_t addr, uint32_t data);
1169e39c5baSBill Taylor static void hermon_flash_write_confirm(hermon_state_t *state,
1179e39c5baSBill Taylor     ddi_acc_handle_t pci_config_hdl);
1189e39c5baSBill Taylor #endif
1199e39c5baSBill Taylor static void hermon_flash_cfi_byte(uint8_t *ch, uint32_t dword, int i);
1209e39c5baSBill Taylor static void hermon_flash_cfi_dword(uint32_t *dword, uint8_t *ch, int i);
1219e39c5baSBill Taylor 
1229e39c5baSBill Taylor /* Hemron loopback test functions */
1239e39c5baSBill Taylor static void hermon_loopback_free_qps(hermon_loopback_state_t *lstate);
1249e39c5baSBill Taylor static void hermon_loopback_free_state(hermon_loopback_state_t *lstate);
1259e39c5baSBill Taylor static int hermon_loopback_init(hermon_state_t *state,
1269e39c5baSBill Taylor     hermon_loopback_state_t *lstate);
1279e39c5baSBill Taylor static void hermon_loopback_init_qp_info(hermon_loopback_state_t *lstate,
1289e39c5baSBill Taylor     hermon_loopback_comm_t *comm);
1299e39c5baSBill Taylor static int hermon_loopback_alloc_mem(hermon_loopback_state_t *lstate,
1309e39c5baSBill Taylor     hermon_loopback_comm_t *comm, int sz);
1319e39c5baSBill Taylor static int hermon_loopback_alloc_qps(hermon_loopback_state_t *lstate,
1329e39c5baSBill Taylor     hermon_loopback_comm_t *comm);
1339e39c5baSBill Taylor static int hermon_loopback_modify_qp(hermon_loopback_state_t *lstate,
1349e39c5baSBill Taylor     hermon_loopback_comm_t *comm, uint_t qp_num);
1359e39c5baSBill Taylor static int hermon_loopback_copyout(hermon_loopback_ioctl_t *lb,
1369e39c5baSBill Taylor     intptr_t arg, int mode);
1379e39c5baSBill Taylor static int hermon_loopback_post_send(hermon_loopback_state_t *lstate,
1389e39c5baSBill Taylor     hermon_loopback_comm_t *tx, hermon_loopback_comm_t *rx);
1399e39c5baSBill Taylor static int hermon_loopback_poll_cq(hermon_loopback_state_t *lstate,
1409e39c5baSBill Taylor     hermon_loopback_comm_t *comm);
1419e39c5baSBill Taylor 
1429e39c5baSBill Taylor /* Patchable timeout values for flash operations */
1439e39c5baSBill Taylor int hermon_hw_flash_timeout_gpio_sema = HERMON_HW_FLASH_TIMEOUT_GPIO_SEMA;
1449e39c5baSBill Taylor int hermon_hw_flash_timeout_config = HERMON_HW_FLASH_TIMEOUT_CONFIG;
1459e39c5baSBill Taylor int hermon_hw_flash_timeout_write = HERMON_HW_FLASH_TIMEOUT_WRITE;
1469e39c5baSBill Taylor int hermon_hw_flash_timeout_erase = HERMON_HW_FLASH_TIMEOUT_ERASE;
1479e39c5baSBill Taylor 
1489e39c5baSBill Taylor /*
1499e39c5baSBill Taylor  * hermon_ioctl()
1509e39c5baSBill Taylor  */
1519e39c5baSBill Taylor /* ARGSUSED */
1529e39c5baSBill Taylor int
hermon_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1539e39c5baSBill Taylor hermon_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1549e39c5baSBill Taylor     int *rvalp)
1559e39c5baSBill Taylor {
1569e39c5baSBill Taylor 	hermon_state_t	*state;
1579e39c5baSBill Taylor 	minor_t		instance;
1589e39c5baSBill Taylor 	int		status;
1599e39c5baSBill Taylor 
1609e39c5baSBill Taylor 	if (drv_priv(credp) != 0) {
1619e39c5baSBill Taylor 		return (EPERM);
1629e39c5baSBill Taylor 	}
1639e39c5baSBill Taylor 
1649e39c5baSBill Taylor 	instance = HERMON_DEV_INSTANCE(dev);
1659e39c5baSBill Taylor 	if (instance == (minor_t)-1) {
1669e39c5baSBill Taylor 		return (EBADF);
1679e39c5baSBill Taylor 	}
1689e39c5baSBill Taylor 
1699e39c5baSBill Taylor 	state = ddi_get_soft_state(hermon_statep, instance);
1709e39c5baSBill Taylor 	if (state == NULL) {
1719e39c5baSBill Taylor 		return (EBADF);
1729e39c5baSBill Taylor 	}
1739e39c5baSBill Taylor 
1749e39c5baSBill Taylor 	status = 0;
1759e39c5baSBill Taylor 
1769e39c5baSBill Taylor 	switch (cmd) {
1779e39c5baSBill Taylor 	case HERMON_IOCTL_FLASH_READ:
1789e39c5baSBill Taylor 		status = hermon_ioctl_flash_read(state, dev, arg, mode);
1799e39c5baSBill Taylor 		break;
1809e39c5baSBill Taylor 
1819e39c5baSBill Taylor 	case HERMON_IOCTL_FLASH_WRITE:
1829e39c5baSBill Taylor 		status = hermon_ioctl_flash_write(state, dev, arg, mode);
1839e39c5baSBill Taylor 		break;
1849e39c5baSBill Taylor 
1859e39c5baSBill Taylor 	case HERMON_IOCTL_FLASH_ERASE:
1869e39c5baSBill Taylor 		status = hermon_ioctl_flash_erase(state, dev, arg, mode);
1879e39c5baSBill Taylor 		break;
1889e39c5baSBill Taylor 
1899e39c5baSBill Taylor 	case HERMON_IOCTL_FLASH_INIT:
1909e39c5baSBill Taylor 		status = hermon_ioctl_flash_init(state, dev, arg, mode);
1919e39c5baSBill Taylor 		break;
1929e39c5baSBill Taylor 
1939e39c5baSBill Taylor 	case HERMON_IOCTL_FLASH_FINI:
1949e39c5baSBill Taylor 		status = hermon_ioctl_flash_fini(state, dev);
1959e39c5baSBill Taylor 		break;
1969e39c5baSBill Taylor 
1979e39c5baSBill Taylor 	case HERMON_IOCTL_INFO:
1989e39c5baSBill Taylor 		status = hermon_ioctl_info(state, dev, arg, mode);
1999e39c5baSBill Taylor 		break;
2009e39c5baSBill Taylor 
2019e39c5baSBill Taylor 	case HERMON_IOCTL_PORTS:
2029e39c5baSBill Taylor 		status = hermon_ioctl_ports(state, arg, mode);
2039e39c5baSBill Taylor 		break;
2049e39c5baSBill Taylor 
2059e39c5baSBill Taylor 	case HERMON_IOCTL_LOOPBACK:
2069e39c5baSBill Taylor 		status = hermon_ioctl_loopback(state, arg, mode);
2079e39c5baSBill Taylor 		break;
2089e39c5baSBill Taylor 
2099e39c5baSBill Taylor #ifdef	DEBUG
2109e39c5baSBill Taylor 	case HERMON_IOCTL_REG_WRITE:
2119e39c5baSBill Taylor 		status = hermon_ioctl_reg_write(state, arg, mode);
2129e39c5baSBill Taylor 		break;
2139e39c5baSBill Taylor 
2149e39c5baSBill Taylor 	case HERMON_IOCTL_REG_READ:
2159e39c5baSBill Taylor 		status = hermon_ioctl_reg_read(state, arg, mode);
2169e39c5baSBill Taylor 		break;
2179e39c5baSBill Taylor #endif	/* DEBUG */
2189e39c5baSBill Taylor 
2199e39c5baSBill Taylor 	case HERMON_IOCTL_DDR_READ:
2209e39c5baSBill Taylor 		/* XXX guard until the ioctl header is cleaned up */
2219e39c5baSBill Taylor 		status = ENODEV;
2229e39c5baSBill Taylor 		break;
2239e39c5baSBill Taylor 
2249e39c5baSBill Taylor 	case HERMON_IOCTL_WRITE_BOOT_ADDR:
2259e39c5baSBill Taylor 		status = hermon_ioctl_write_boot_addr(state, dev, arg, mode);
2269e39c5baSBill Taylor 		break;
2279e39c5baSBill Taylor 
2289e39c5baSBill Taylor 	default:
2299e39c5baSBill Taylor 		status = ENOTTY;
2309e39c5baSBill Taylor 		break;
2319e39c5baSBill Taylor 	}
2329e39c5baSBill Taylor 	*rvalp = status;
2339e39c5baSBill Taylor 
2349e39c5baSBill Taylor 	return (status);
2359e39c5baSBill Taylor }
2369e39c5baSBill Taylor 
2379e39c5baSBill Taylor /*
2389e39c5baSBill Taylor  * hermon_ioctl_flash_read()
2399e39c5baSBill Taylor  */
2409e39c5baSBill Taylor static int
hermon_ioctl_flash_read(hermon_state_t * state,dev_t dev,intptr_t arg,int mode)2419e39c5baSBill Taylor hermon_ioctl_flash_read(hermon_state_t *state, dev_t dev, intptr_t arg,
2429e39c5baSBill Taylor     int mode)
2439e39c5baSBill Taylor {
2449e39c5baSBill Taylor 	hermon_flash_ioctl_t ioctl_info;
2459e39c5baSBill Taylor 	int status = 0;
2469e39c5baSBill Taylor 
2479e39c5baSBill Taylor 	/*
2489e39c5baSBill Taylor 	 * Check that flash init ioctl has been called first.  And check
2499e39c5baSBill Taylor 	 * that the same dev_t that called init is the one calling read now.
2509e39c5baSBill Taylor 	 */
2519e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
2529e39c5baSBill Taylor 	if ((state->hs_fw_flashdev != dev) ||
2539e39c5baSBill Taylor 	    (state->hs_fw_flashstarted == 0)) {
2549e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
2559e39c5baSBill Taylor 		return (EIO);
2569e39c5baSBill Taylor 	}
2579e39c5baSBill Taylor 
2589e39c5baSBill Taylor 	/* copy user struct to kernel */
2599e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
2609e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2619e39c5baSBill Taylor 		hermon_flash_ioctl32_t info32;
2629e39c5baSBill Taylor 
2639e39c5baSBill Taylor 		if (ddi_copyin((void *)arg, &info32,
2649e39c5baSBill Taylor 		    sizeof (hermon_flash_ioctl32_t), mode) != 0) {
2659e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
2669e39c5baSBill Taylor 			return (EFAULT);
2679e39c5baSBill Taylor 		}
2689e39c5baSBill Taylor 		ioctl_info.af_type = info32.af_type;
2699e39c5baSBill Taylor 		ioctl_info.af_sector = (caddr_t)(uintptr_t)info32.af_sector;
2709e39c5baSBill Taylor 		ioctl_info.af_sector_num = info32.af_sector_num;
2719e39c5baSBill Taylor 		ioctl_info.af_addr = info32.af_addr;
2729e39c5baSBill Taylor 	} else
2739e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
2749e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &ioctl_info, sizeof (hermon_flash_ioctl_t),
2759e39c5baSBill Taylor 	    mode) != 0) {
2769e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
2779e39c5baSBill Taylor 		return (EFAULT);
2789e39c5baSBill Taylor 	}
2799e39c5baSBill Taylor 
2809e39c5baSBill Taylor 	/*
2819e39c5baSBill Taylor 	 * Determine type of READ ioctl
2829e39c5baSBill Taylor 	 */
2839e39c5baSBill Taylor 	switch (ioctl_info.af_type) {
2849e39c5baSBill Taylor 	case HERMON_FLASH_READ_SECTOR:
2859e39c5baSBill Taylor 		/* Check if sector num is too large for flash device */
2869e39c5baSBill Taylor 		if (ioctl_info.af_sector_num >=
2879e39c5baSBill Taylor 		    (state->hs_fw_device_sz >> state->hs_fw_log_sector_sz)) {
2889e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
2899e39c5baSBill Taylor 			return (EFAULT);
2909e39c5baSBill Taylor 		}
2919e39c5baSBill Taylor 
2929e39c5baSBill Taylor 		/* Perform the Sector Read */
2939e39c5baSBill Taylor 		if ((status = hermon_flash_reset(state)) != 0 ||
2949e39c5baSBill Taylor 		    (status = hermon_flash_read_sector(state,
2959e39c5baSBill Taylor 		    ioctl_info.af_sector_num)) != 0) {
2969e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
2979e39c5baSBill Taylor 			return (status);
2989e39c5baSBill Taylor 		}
2999e39c5baSBill Taylor 
3009e39c5baSBill Taylor 		/* copyout the firmware sector image data */
3019e39c5baSBill Taylor 		if (ddi_copyout(&state->hs_fw_sector[0],
3029e39c5baSBill Taylor 		    &ioctl_info.af_sector[0], 1 << state->hs_fw_log_sector_sz,
3039e39c5baSBill Taylor 		    mode) != 0) {
3049e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
3059e39c5baSBill Taylor 			return (EFAULT);
3069e39c5baSBill Taylor 		}
3079e39c5baSBill Taylor 		break;
3089e39c5baSBill Taylor 
3099e39c5baSBill Taylor 	case HERMON_FLASH_READ_QUADLET:
3109e39c5baSBill Taylor 		/* Check if addr is too large for flash device */
3119e39c5baSBill Taylor 		if (ioctl_info.af_addr >= state->hs_fw_device_sz) {
3129e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
3139e39c5baSBill Taylor 			return (EFAULT);
3149e39c5baSBill Taylor 		}
3159e39c5baSBill Taylor 
3169e39c5baSBill Taylor 		/* Perform the Quadlet Read */
3179e39c5baSBill Taylor 		if ((status = hermon_flash_reset(state)) != 0 ||
3189e39c5baSBill Taylor 		    (status = hermon_flash_read_quadlet(state,
3199e39c5baSBill Taylor 		    &ioctl_info.af_quadlet, ioctl_info.af_addr)) != 0) {
3209e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
3219e39c5baSBill Taylor 			return (status);
3229e39c5baSBill Taylor 		}
3239e39c5baSBill Taylor 		break;
3249e39c5baSBill Taylor 
3259e39c5baSBill Taylor 	default:
3269e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
3279e39c5baSBill Taylor 		return (EINVAL);
3289e39c5baSBill Taylor 	}
3299e39c5baSBill Taylor 
3309e39c5baSBill Taylor 	/* copy results back to userland */
3319e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
3329e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
3339e39c5baSBill Taylor 		hermon_flash_ioctl32_t info32;
3349e39c5baSBill Taylor 
3359e39c5baSBill Taylor 		info32.af_quadlet = ioctl_info.af_quadlet;
3369e39c5baSBill Taylor 		info32.af_type = ioctl_info.af_type;
3379e39c5baSBill Taylor 		info32.af_sector_num = ioctl_info.af_sector_num;
3389e39c5baSBill Taylor 		info32.af_sector = (caddr32_t)(uintptr_t)ioctl_info.af_sector;
3399e39c5baSBill Taylor 		info32.af_addr = ioctl_info.af_addr;
3409e39c5baSBill Taylor 
3419e39c5baSBill Taylor 		if (ddi_copyout(&info32, (void *)arg,
3429e39c5baSBill Taylor 		    sizeof (hermon_flash_ioctl32_t), mode) != 0) {
3439e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
3449e39c5baSBill Taylor 			return (EFAULT);
3459e39c5baSBill Taylor 		}
3469e39c5baSBill Taylor 	} else
3479e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
3489e39c5baSBill Taylor 	if (ddi_copyout(&ioctl_info, (void *)arg,
3499e39c5baSBill Taylor 	    sizeof (hermon_flash_ioctl_t), mode) != 0) {
3509e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
3519e39c5baSBill Taylor 		return (EFAULT);
3529e39c5baSBill Taylor 	}
3539e39c5baSBill Taylor 
3549e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
3559e39c5baSBill Taylor 	return (status);
3569e39c5baSBill Taylor }
3579e39c5baSBill Taylor 
3589e39c5baSBill Taylor /*
3599e39c5baSBill Taylor  * hermon_ioctl_flash_write()
3609e39c5baSBill Taylor  */
3619e39c5baSBill Taylor static int
hermon_ioctl_flash_write(hermon_state_t * state,dev_t dev,intptr_t arg,int mode)3629e39c5baSBill Taylor hermon_ioctl_flash_write(hermon_state_t *state, dev_t dev, intptr_t arg,
3639e39c5baSBill Taylor     int mode)
3649e39c5baSBill Taylor {
3659e39c5baSBill Taylor 	hermon_flash_ioctl_t	ioctl_info;
3669e39c5baSBill Taylor 	int status = 0;
3679e39c5baSBill Taylor 
3689e39c5baSBill Taylor 	/*
3699e39c5baSBill Taylor 	 * Check that flash init ioctl has been called first.  And check
3709e39c5baSBill Taylor 	 * that the same dev_t that called init is the one calling write now.
3719e39c5baSBill Taylor 	 */
3729e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
3739e39c5baSBill Taylor 	if ((state->hs_fw_flashdev != dev) ||
3749e39c5baSBill Taylor 	    (state->hs_fw_flashstarted == 0)) {
3759e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
3769e39c5baSBill Taylor 		return (EIO);
3779e39c5baSBill Taylor 	}
3789e39c5baSBill Taylor 
3799e39c5baSBill Taylor 	/* copy user struct to kernel */
3809e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
3819e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
3829e39c5baSBill Taylor 		hermon_flash_ioctl32_t info32;
3839e39c5baSBill Taylor 
3849e39c5baSBill Taylor 		if (ddi_copyin((void *)arg, &info32,
3859e39c5baSBill Taylor 		    sizeof (hermon_flash_ioctl32_t), mode) != 0) {
3869e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
3879e39c5baSBill Taylor 			return (EFAULT);
3889e39c5baSBill Taylor 		}
3899e39c5baSBill Taylor 		ioctl_info.af_type = info32.af_type;
3909e39c5baSBill Taylor 		ioctl_info.af_sector = (caddr_t)(uintptr_t)info32.af_sector;
3919e39c5baSBill Taylor 		ioctl_info.af_sector_num = info32.af_sector_num;
3929e39c5baSBill Taylor 		ioctl_info.af_addr = info32.af_addr;
3939e39c5baSBill Taylor 		ioctl_info.af_byte = info32.af_byte;
3949e39c5baSBill Taylor 	} else
3959e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
3969e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &ioctl_info,
3979e39c5baSBill Taylor 	    sizeof (hermon_flash_ioctl_t), mode) != 0) {
3989e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
3999e39c5baSBill Taylor 		return (EFAULT);
4009e39c5baSBill Taylor 	}
4019e39c5baSBill Taylor 
4029e39c5baSBill Taylor 	/*
4039e39c5baSBill Taylor 	 * Determine type of WRITE ioctl
4049e39c5baSBill Taylor 	 */
4059e39c5baSBill Taylor 	switch (ioctl_info.af_type) {
4069e39c5baSBill Taylor 	case HERMON_FLASH_WRITE_SECTOR:
4079e39c5baSBill Taylor 		/* Check if sector num is too large for flash device */
4089e39c5baSBill Taylor 		if (ioctl_info.af_sector_num >=
4099e39c5baSBill Taylor 		    (state->hs_fw_device_sz >> state->hs_fw_log_sector_sz)) {
4109e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
4119e39c5baSBill Taylor 			return (EFAULT);
4129e39c5baSBill Taylor 		}
4139e39c5baSBill Taylor 
4149e39c5baSBill Taylor 		/* copy in fw sector image data */
4159e39c5baSBill Taylor 		if (ddi_copyin(&ioctl_info.af_sector[0],
4169e39c5baSBill Taylor 		    &state->hs_fw_sector[0], 1 << state->hs_fw_log_sector_sz,
4179e39c5baSBill Taylor 		    mode) != 0) {
4189e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
4199e39c5baSBill Taylor 			return (EFAULT);
4209e39c5baSBill Taylor 		}
4219e39c5baSBill Taylor 
4229e39c5baSBill Taylor 		/* Perform Write Sector */
4239e39c5baSBill Taylor 		status = hermon_flash_write_sector(state,
4249e39c5baSBill Taylor 		    ioctl_info.af_sector_num);
4259e39c5baSBill Taylor 		break;
4269e39c5baSBill Taylor 
4279e39c5baSBill Taylor 	case HERMON_FLASH_WRITE_BYTE:
4289e39c5baSBill Taylor 		/* Check if addr is too large for flash device */
4299e39c5baSBill Taylor 		if (ioctl_info.af_addr >= state->hs_fw_device_sz) {
4309e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
4319e39c5baSBill Taylor 			return (EFAULT);
4329e39c5baSBill Taylor 		}
4339e39c5baSBill Taylor 
4349e39c5baSBill Taylor 		/* Perform Write Byte */
4359e39c5baSBill Taylor 		/*
4369e39c5baSBill Taylor 		 * CMJ -- is a reset really needed before and after writing
4379e39c5baSBill Taylor 		 * each byte?  This code came from arbel, but we should look
4389e39c5baSBill Taylor 		 * into this.  Also, for SPI, no reset is actually performed.
4399e39c5baSBill Taylor 		 */
4409e39c5baSBill Taylor 		if ((status = hermon_flash_bank(state,
4419e39c5baSBill Taylor 		    ioctl_info.af_addr)) != 0 ||
4429e39c5baSBill Taylor 		    (status = hermon_flash_reset(state)) != 0 ||
4439e39c5baSBill Taylor 		    (status = hermon_flash_write_byte(state,
4449e39c5baSBill Taylor 		    ioctl_info.af_addr, ioctl_info.af_byte)) != 0 ||
4459e39c5baSBill Taylor 		    (status = hermon_flash_reset(state)) != 0) {
4469e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
4479e39c5baSBill Taylor 			return (status);
4489e39c5baSBill Taylor 		}
4499e39c5baSBill Taylor 		break;
4509e39c5baSBill Taylor 
4519e39c5baSBill Taylor 	default:
4529e39c5baSBill Taylor 		status = EINVAL;
4539e39c5baSBill Taylor 		break;
4549e39c5baSBill Taylor 	}
4559e39c5baSBill Taylor 
4569e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
4579e39c5baSBill Taylor 	return (status);
4589e39c5baSBill Taylor }
4599e39c5baSBill Taylor 
4609e39c5baSBill Taylor /*
4619e39c5baSBill Taylor  * hermon_ioctl_flash_erase()
4629e39c5baSBill Taylor  */
4639e39c5baSBill Taylor static int
hermon_ioctl_flash_erase(hermon_state_t * state,dev_t dev,intptr_t arg,int mode)4649e39c5baSBill Taylor hermon_ioctl_flash_erase(hermon_state_t *state, dev_t dev, intptr_t arg,
4659e39c5baSBill Taylor     int mode)
4669e39c5baSBill Taylor {
4679e39c5baSBill Taylor 	hermon_flash_ioctl_t	ioctl_info;
4689e39c5baSBill Taylor 	int status = 0;
4699e39c5baSBill Taylor 
4709e39c5baSBill Taylor 	/*
4719e39c5baSBill Taylor 	 * Check that flash init ioctl has been called first.  And check
4729e39c5baSBill Taylor 	 * that the same dev_t that called init is the one calling erase now.
4739e39c5baSBill Taylor 	 */
4749e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
4759e39c5baSBill Taylor 	if ((state->hs_fw_flashdev != dev) ||
4769e39c5baSBill Taylor 	    (state->hs_fw_flashstarted == 0)) {
4779e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
4789e39c5baSBill Taylor 		return (EIO);
4799e39c5baSBill Taylor 	}
4809e39c5baSBill Taylor 
4819e39c5baSBill Taylor 	/* copy user struct to kernel */
4829e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
4839e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
4849e39c5baSBill Taylor 		hermon_flash_ioctl32_t info32;
4859e39c5baSBill Taylor 
4869e39c5baSBill Taylor 		if (ddi_copyin((void *)arg, &info32,
4879e39c5baSBill Taylor 		    sizeof (hermon_flash_ioctl32_t), mode) != 0) {
4889e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
4899e39c5baSBill Taylor 			return (EFAULT);
4909e39c5baSBill Taylor 		}
4919e39c5baSBill Taylor 		ioctl_info.af_type = info32.af_type;
4929e39c5baSBill Taylor 		ioctl_info.af_sector_num = info32.af_sector_num;
4939e39c5baSBill Taylor 	} else
4949e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
4959e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &ioctl_info, sizeof (hermon_flash_ioctl_t),
4969e39c5baSBill Taylor 	    mode) != 0) {
4979e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
4989e39c5baSBill Taylor 		return (EFAULT);
4999e39c5baSBill Taylor 	}
5009e39c5baSBill Taylor 
5019e39c5baSBill Taylor 	/*
5029e39c5baSBill Taylor 	 * Determine type of ERASE ioctl
5039e39c5baSBill Taylor 	 */
5049e39c5baSBill Taylor 	switch (ioctl_info.af_type) {
5059e39c5baSBill Taylor 	case HERMON_FLASH_ERASE_SECTOR:
5069e39c5baSBill Taylor 		/* Check if sector num is too large for flash device */
5079e39c5baSBill Taylor 		if (ioctl_info.af_sector_num >=
5089e39c5baSBill Taylor 		    (state->hs_fw_device_sz >> state->hs_fw_log_sector_sz)) {
5099e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
5109e39c5baSBill Taylor 			return (EFAULT);
5119e39c5baSBill Taylor 		}
5129e39c5baSBill Taylor 
5139e39c5baSBill Taylor 		/* Perform Sector Erase */
5149e39c5baSBill Taylor 		status = hermon_flash_erase_sector(state,
5159e39c5baSBill Taylor 		    ioctl_info.af_sector_num);
5169e39c5baSBill Taylor 		break;
5179e39c5baSBill Taylor 
5189e39c5baSBill Taylor 	case HERMON_FLASH_ERASE_CHIP:
5199e39c5baSBill Taylor 		/* Perform Chip Erase */
5209e39c5baSBill Taylor 		status = hermon_flash_erase_chip(state);
5219e39c5baSBill Taylor 		break;
5229e39c5baSBill Taylor 
5239e39c5baSBill Taylor 	default:
5249e39c5baSBill Taylor 		status = EINVAL;
5259e39c5baSBill Taylor 		break;
5269e39c5baSBill Taylor 	}
5279e39c5baSBill Taylor 
5289e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
5299e39c5baSBill Taylor 	return (status);
5309e39c5baSBill Taylor }
5319e39c5baSBill Taylor 
5329e39c5baSBill Taylor /*
5339e39c5baSBill Taylor  * hermon_ioctl_flash_init()
5349e39c5baSBill Taylor  */
5359e39c5baSBill Taylor static int
hermon_ioctl_flash_init(hermon_state_t * state,dev_t dev,intptr_t arg,int mode)5369e39c5baSBill Taylor hermon_ioctl_flash_init(hermon_state_t *state, dev_t dev, intptr_t arg,
5379e39c5baSBill Taylor     int mode)
5389e39c5baSBill Taylor {
5399e39c5baSBill Taylor 	hermon_flash_init_ioctl_t init_info;
5409e39c5baSBill Taylor 	int ret;
5419e39c5baSBill Taylor 	int intel_xcmd = 0;
5429e39c5baSBill Taylor 	ddi_acc_handle_t pci_hdl = hermon_get_pcihdl(state);
5439e39c5baSBill Taylor 
5449e39c5baSBill Taylor 	/* initialize the FMA retry loop */
5459e39c5baSBill Taylor 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test);
5469e39c5baSBill Taylor 
5479e39c5baSBill Taylor 	state->hs_fw_sector = NULL;
5489e39c5baSBill Taylor 
5499e39c5baSBill Taylor 	/*
5509e39c5baSBill Taylor 	 * init cannot be called more than once.  If we have already init'd the
5519e39c5baSBill Taylor 	 * flash, return directly.
5529e39c5baSBill Taylor 	 */
5539e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
5549e39c5baSBill Taylor 	if (state->hs_fw_flashstarted == 1) {
5559e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
5569e39c5baSBill Taylor 		return (EINVAL);
5579e39c5baSBill Taylor 	}
5589e39c5baSBill Taylor 
5599e39c5baSBill Taylor 	/* copyin the user struct to kernel */
5609e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &init_info,
5619e39c5baSBill Taylor 	    sizeof (hermon_flash_init_ioctl_t), mode) != 0) {
5629e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
5639e39c5baSBill Taylor 		return (EFAULT);
5649e39c5baSBill Taylor 	}
5659e39c5baSBill Taylor 
5669e39c5baSBill Taylor 	/* Init Flash */
5679e39c5baSBill Taylor 	if ((ret = hermon_flash_init(state)) != 0) {
5689e39c5baSBill Taylor 		if (ret == EIO) {
5699e39c5baSBill Taylor 			goto pio_error;
5709e39c5baSBill Taylor 		}
5719e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
5729e39c5baSBill Taylor 		return (ret);
5739e39c5baSBill Taylor 	}
5749e39c5baSBill Taylor 
5759e39c5baSBill Taylor 	/* Read CFI info */
5769e39c5baSBill Taylor 	if ((ret = hermon_flash_cfi_init(state, &init_info.af_cfi_info[0],
5779e39c5baSBill Taylor 	    &intel_xcmd)) != 0) {
5789e39c5baSBill Taylor 		if (ret == EIO) {
5799e39c5baSBill Taylor 			goto pio_error;
5809e39c5baSBill Taylor 		}
5819e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
5829e39c5baSBill Taylor 		return (ret);
5839e39c5baSBill Taylor 	}
5849e39c5baSBill Taylor 
5859e39c5baSBill Taylor 	/*
5869e39c5baSBill Taylor 	 * Return error if the command set is unknown.
5879e39c5baSBill Taylor 	 */
5889e39c5baSBill Taylor 	if (state->hs_fw_cmdset == HERMON_FLASH_UNKNOWN_CMDSET) {
5899e39c5baSBill Taylor 		if ((ret = hermon_ioctl_flash_cleanup_nolock(state)) != 0) {
5909e39c5baSBill Taylor 			if (ret == EIO) {
5919e39c5baSBill Taylor 				goto pio_error;
5929e39c5baSBill Taylor 			}
5939e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
5949e39c5baSBill Taylor 			return (ret);
5959e39c5baSBill Taylor 		}
5969e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
5979e39c5baSBill Taylor 		return (EFAULT);
5989e39c5baSBill Taylor 	}
5999e39c5baSBill Taylor 
6009e39c5baSBill Taylor 	/* the FMA retry loop starts. */
6019e39c5baSBill Taylor 	hermon_pio_start(state, pci_hdl, pio_error,
6029e39c5baSBill Taylor 	    fm_loop_cnt, fm_status, fm_test);
6039e39c5baSBill Taylor 
6049e39c5baSBill Taylor 	/* Read HWREV - least significant 8 bits is revision ID */
6059e39c5baSBill Taylor 	init_info.af_hwrev = pci_config_get32(pci_hdl,
6069e39c5baSBill Taylor 	    HERMON_HW_FLASH_CFG_HWREV) & 0xFF;
6079e39c5baSBill Taylor 
6089e39c5baSBill Taylor 	/* the FMA retry loop ends. */
6099e39c5baSBill Taylor 	hermon_pio_end(state, pci_hdl, pio_error, fm_loop_cnt,
6109e39c5baSBill Taylor 	    fm_status, fm_test);
6119e39c5baSBill Taylor 
6129e39c5baSBill Taylor 	/* Fill in the firmwate revision numbers */
6139e39c5baSBill Taylor 	init_info.af_fwrev.afi_maj	= state->hs_fw.fw_rev_major;
6149e39c5baSBill Taylor 	init_info.af_fwrev.afi_min	= state->hs_fw.fw_rev_minor;
6159e39c5baSBill Taylor 	init_info.af_fwrev.afi_sub	= state->hs_fw.fw_rev_subminor;
6169e39c5baSBill Taylor 
6179e39c5baSBill Taylor 	/* Alloc flash mem for one sector size */
6189e39c5baSBill Taylor 	state->hs_fw_sector = (uint32_t *)kmem_zalloc(1 <<
6199e39c5baSBill Taylor 	    state->hs_fw_log_sector_sz, KM_SLEEP);
6209e39c5baSBill Taylor 
6219e39c5baSBill Taylor 	/* Set HW part number and length */
6229e39c5baSBill Taylor 	init_info.af_pn_len = state->hs_hca_pn_len;
6239e39c5baSBill Taylor 	if (state->hs_hca_pn_len != 0) {
6249e39c5baSBill Taylor 		(void) memcpy(init_info.af_hwpn, state->hs_hca_pn,
6259e39c5baSBill Taylor 		    state->hs_hca_pn_len);
6269e39c5baSBill Taylor 	}
6279e39c5baSBill Taylor 
6289e39c5baSBill Taylor 	/* Copy ioctl results back to userland */
6299e39c5baSBill Taylor 	if (ddi_copyout(&init_info, (void *)arg,
6309e39c5baSBill Taylor 	    sizeof (hermon_flash_init_ioctl_t), mode) != 0) {
6319e39c5baSBill Taylor 		if ((ret = hermon_ioctl_flash_cleanup_nolock(state)) != 0) {
6329e39c5baSBill Taylor 			if (ret == EIO) {
6339e39c5baSBill Taylor 				goto pio_error;
6349e39c5baSBill Taylor 			}
6359e39c5baSBill Taylor 			mutex_exit(&state->hs_fw_flashlock);
6369e39c5baSBill Taylor 			return (ret);
6379e39c5baSBill Taylor 		}
6389e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
6399e39c5baSBill Taylor 		return (EFAULT);
6409e39c5baSBill Taylor 	}
6419e39c5baSBill Taylor 
6429e39c5baSBill Taylor 	/* Set flash state to started */
6439e39c5baSBill Taylor 	state->hs_fw_flashstarted = 1;
6449e39c5baSBill Taylor 	state->hs_fw_flashdev	  = dev;
6459e39c5baSBill Taylor 
6469e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
6479e39c5baSBill Taylor 
6489e39c5baSBill Taylor 	/*
6499e39c5baSBill Taylor 	 * If "flash init" is successful, add an "on close" callback to the
6509e39c5baSBill Taylor 	 * current dev node to ensure that "flash fini" gets called later
6519e39c5baSBill Taylor 	 * even if the userland process prematurely exits.
6529e39c5baSBill Taylor 	 */
6539e39c5baSBill Taylor 	ret = hermon_umap_db_set_onclose_cb(dev,
6559e39c5baSBill Taylor 	    (int (*)(void *))hermon_ioctl_flash_cleanup, state);
6569e39c5baSBill Taylor 	if (ret != DDI_SUCCESS) {
6579e39c5baSBill Taylor 		int status = hermon_ioctl_flash_fini(state, dev);
6589e39c5baSBill Taylor 		if (status != 0) {
6599e39c5baSBill Taylor 			if (status == EIO) {
6609e39c5baSBill Taylor 				hermon_fm_ereport(state, HCA_SYS_ERR,
6619e39c5baSBill Taylor 				    HCA_ERR_IOCTL);
6629e39c5baSBill Taylor 				return (EIO);
6639e39c5baSBill Taylor 			}
6649e39c5baSBill Taylor 			return (status);
6659e39c5baSBill Taylor 		}
6669e39c5baSBill Taylor 	}
6679e39c5baSBill Taylor 	return (0);
6689e39c5baSBill Taylor 
6699e39c5baSBill Taylor pio_error:
6709e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
6719e39c5baSBill Taylor 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_IOCTL);
6729e39c5baSBill Taylor 	return (EIO);
6739e39c5baSBill Taylor }
6749e39c5baSBill Taylor 
6759e39c5baSBill Taylor /*
6769e39c5baSBill Taylor  * hermon_ioctl_flash_fini()
6779e39c5baSBill Taylor  */
6789e39c5baSBill Taylor static int
hermon_ioctl_flash_fini(hermon_state_t * state,dev_t dev)6799e39c5baSBill Taylor hermon_ioctl_flash_fini(hermon_state_t *state, dev_t dev)
6809e39c5baSBill Taylor {
6819e39c5baSBill Taylor 	int ret;
6829e39c5baSBill Taylor 
6839e39c5baSBill Taylor 	/*
6849e39c5baSBill Taylor 	 * Check that flash init ioctl has been called first.  And check
6859e39c5baSBill Taylor 	 * that the same dev_t that called init is the one calling fini now.
6869e39c5baSBill Taylor 	 */
6879e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
6889e39c5baSBill Taylor 	if ((state->hs_fw_flashdev != dev) ||
6899e39c5baSBill Taylor 	    (state->hs_fw_flashstarted == 0)) {
6909e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
6919e39c5baSBill Taylor 		return (EINVAL);
6929e39c5baSBill Taylor 	}
6939e39c5baSBill Taylor 
6949e39c5baSBill Taylor 	if ((ret = hermon_ioctl_flash_cleanup_nolock(state)) != 0) {
6959e39c5baSBill Taylor 		mutex_exit(&state->hs_fw_flashlock);
6969e39c5baSBill Taylor 		if (ret == EIO) {
6979e39c5baSBill Taylor 			hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_IOCTL);
6989e39c5baSBill Taylor 		}
6999e39c5baSBill Taylor 		return (ret);
7009e39c5baSBill Taylor 	}
7019e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
7029e39c5baSBill Taylor 
7039e39c5baSBill Taylor 	/*
7049e39c5baSBill Taylor 	 * If "flash fini" is successful, remove the "on close" callback
7059e39c5baSBill Taylor 	 * that was setup during "flash init".
7069e39c5baSBill Taylor 	 */
7079e39c5baSBill Taylor 	ret = hermon_umap_db_clear_onclose_cb(dev,
7089e39c5baSBill Taylor 	    HERMON_ONCLOSE_FLASH_INPROGRESS);
7099e39c5baSBill Taylor 	if (ret != DDI_SUCCESS) {
7109e39c5baSBill Taylor 		return (EFAULT);
7119e39c5baSBill Taylor 	}
7129e39c5baSBill Taylor 	return (0);
7139e39c5baSBill Taylor }
7149e39c5baSBill Taylor 
7159e39c5baSBill Taylor 
7169e39c5baSBill Taylor /*
7179e39c5baSBill Taylor  * hermon_ioctl_flash_cleanup()
7189e39c5baSBill Taylor  */
7199e39c5baSBill Taylor static int
hermon_ioctl_flash_cleanup(hermon_state_t * state)7209e39c5baSBill Taylor hermon_ioctl_flash_cleanup(hermon_state_t *state)
7219e39c5baSBill Taylor {
7229e39c5baSBill Taylor 	int status;
7239e39c5baSBill Taylor 
7249e39c5baSBill Taylor 	mutex_enter(&state->hs_fw_flashlock);
7259e39c5baSBill Taylor 	status = hermon_ioctl_flash_cleanup_nolock(state);
7269e39c5baSBill Taylor 	mutex_exit(&state->hs_fw_flashlock);
7279e39c5baSBill Taylor 
7289e39c5baSBill Taylor 	return (status);
7299e39c5baSBill Taylor }
7309e39c5baSBill Taylor 
7319e39c5baSBill Taylor 
7329e39c5baSBill Taylor /*
7339e39c5baSBill Taylor  * hermon_ioctl_flash_cleanup_nolock()
7349e39c5baSBill Taylor  */
7359e39c5baSBill Taylor static int
hermon_ioctl_flash_cleanup_nolock(hermon_state_t * state)7369e39c5baSBill Taylor hermon_ioctl_flash_cleanup_nolock(hermon_state_t *state)
7379e39c5baSBill Taylor {
7389e39c5baSBill Taylor 	int status;
7399e39c5baSBill Taylor 	ASSERT(MUTEX_HELD(&state->hs_fw_flashlock));
7409e39c5baSBill Taylor 
7419e39c5baSBill Taylor 	/* free flash mem */
7429e39c5baSBill Taylor 	if (state->hs_fw_sector) {
7439e39c5baSBill Taylor 		kmem_free(state->hs_fw_sector, 1 << state->hs_fw_log_sector_sz);
7449e39c5baSBill Taylor 	}
7459e39c5baSBill Taylor 
7469e39c5baSBill Taylor 	/* Fini the Flash */
7479e39c5baSBill Taylor 	if ((status = hermon_flash_fini(state)) != 0)
7489e39c5baSBill Taylor 		return (status);
7499e39c5baSBill Taylor 
7509e39c5baSBill Taylor 	/* Set flash state to fini */
7519e39c5baSBill Taylor 	state->hs_fw_flashstarted = 0;
7529e39c5baSBill Taylor 	state->hs_fw_flashdev	  = 0;
7539e39c5baSBill Taylor 	return (0);
7549e39c5baSBill Taylor }
7559e39c5baSBill Taylor 
7569e39c5baSBill Taylor 
7579e39c5baSBill Taylor /*
7589e39c5baSBill Taylor  * hermon_ioctl_info()
7599e39c5baSBill Taylor  */
7609e39c5baSBill Taylor static int
hermon_ioctl_info(hermon_state_t * state,dev_t dev,intptr_t arg,int mode)7619e39c5baSBill Taylor hermon_ioctl_info(hermon_state_t *state, dev_t dev, intptr_t arg, int mode)
7629e39c5baSBill Taylor {
7639e39c5baSBill Taylor 	hermon_info_ioctl_t	 info;
7649e39c5baSBill Taylor 	hermon_flash_init_ioctl_t init_info;
7659e39c5baSBill Taylor 
7669e39c5baSBill Taylor 	/*
7679e39c5baSBill Taylor 	 * Access to Hemron VTS ioctls is not allowed in "maintenance mode".
7689e39c5baSBill Taylor 	 */
7699e39c5baSBill Taylor 	if (state->hs_operational_mode == HERMON_MAINTENANCE_MODE) {
7709e39c5baSBill Taylor 		return (EFAULT);
7719e39c5baSBill Taylor 	}
7729e39c5baSBill Taylor 
7739e39c5baSBill Taylor 	/* copyin the user struct to kernel */
7749e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &info, sizeof (hermon_info_ioctl_t),
7759e39c5baSBill Taylor 	    mode) != 0) {
7769e39c5baSBill Taylor 		return (EFAULT);
7779e39c5baSBill Taylor 	}
7789e39c5baSBill Taylor 
7799e39c5baSBill Taylor 	/*
7809e39c5baSBill Taylor 	 * Check ioctl revision
7819e39c5baSBill Taylor 	 */
7829e39c5baSBill Taylor 	if (info.ai_revision != HERMON_VTS_IOCTL_REVISION) {
7839e39c5baSBill Taylor 		return (EINVAL);
7849e39c5baSBill Taylor 	}
7859e39c5baSBill Taylor 
7869e39c5baSBill Taylor 	/*
7879e39c5baSBill Taylor 	 * If the 'fw_device_sz' has not been initialized yet, we initialize it
7889e39c5baSBill Taylor 	 * here.  This is done by leveraging the
7899e39c5baSBill Taylor 	 * hermon_ioctl_flash_init()/fini() calls.  We also hold our own mutex
7909e39c5baSBill Taylor 	 * around this operation in case we have multiple VTS threads in
7919e39c5baSBill Taylor 	 * process at the same time.
7929e39c5baSBill Taylor 	 */
7939e39c5baSBill Taylor 	mutex_enter(&state->hs_info_lock);
7949e39c5baSBill Taylor 	if (state->hs_fw_device_sz == 0) {
7959e39c5baSBill Taylor 		if (hermon_ioctl_flash_init(state, dev, (intptr_t)&init_info,
7969e39c5baSBill Taylor 		    (FKIOCTL | mode)) != 0) {
7979e39c5baSBill Taylor 			mutex_exit(&state->hs_info_lock);
7989e39c5baSBill Taylor 			return (EFAULT);
7999e39c5baSBill Taylor 		}
8009e39c5baSBill Taylor 		(void) hermon_ioctl_flash_fini(state, dev);
8019e39c5baSBill Taylor 	}
8029e39c5baSBill Taylor 	mutex_exit(&state->hs_info_lock);
8039e39c5baSBill Taylor 
8049e39c5baSBill Taylor 	info.ai_hw_rev		 = state->hs_revision_id;
8059e39c5baSBill Taylor 	info.ai_flash_sz	 = state->hs_fw_device_sz;
8069e39c5baSBill Taylor 	info.ai_fw_rev.afi_maj	 = state->hs_fw.fw_rev_major;
8079e39c5baSBill Taylor 	info.ai_fw_rev.afi_min	 = state->hs_fw.fw_rev_minor;
8089e39c5baSBill Taylor 	info.ai_fw_rev.afi_sub	 = state->hs_fw.fw_rev_subminor;
8099e39c5baSBill Taylor 
8109e39c5baSBill Taylor 	/* Copy ioctl results back to user struct */
8119e39c5baSBill Taylor 	if (ddi_copyout(&info, (void *)arg, sizeof (hermon_info_ioctl_t),
8129e39c5baSBill Taylor 	    mode) != 0) {
8139e39c5baSBill Taylor 		return (EFAULT);
8149e39c5baSBill Taylor 	}
8159e39c5baSBill Taylor 
8169e39c5baSBill Taylor 	return (0);
8179e39c5baSBill Taylor }
8189e39c5baSBill Taylor 
8199e39c5baSBill Taylor /*
8209e39c5baSBill Taylor  * hermon_ioctl_ports()
8219e39c5baSBill Taylor  */
8229e39c5baSBill Taylor static int
hermon_ioctl_ports(hermon_state_t * state,intptr_t arg,int mode)8239e39c5baSBill Taylor hermon_ioctl_ports(hermon_state_t *state, intptr_t arg, int mode)
8249e39c5baSBill Taylor {
8259e39c5baSBill Taylor 	hermon_ports_ioctl_t	info;
8269e39c5baSBill Taylor 	hermon_stat_port_ioctl_t	portstat;
8279e39c5baSBill Taylor 	ibt_hca_portinfo_t	pi;
8289e39c5baSBill Taylor 	uint_t			tbl_size;
8299e39c5baSBill Taylor 	ib_gid_t		*sgid_tbl;
8309e39c5baSBill Taylor 	ib_pkey_t		*pkey_tbl;
8319e39c5baSBill Taylor 	int			i;
8329e39c5baSBill Taylor 
8339e39c5baSBill Taylor 	/*
8349e39c5baSBill Taylor 	 * Access to Hemron VTS ioctls is not allowed in "maintenance mode".
8359e39c5baSBill Taylor 	 */
8369e39c5baSBill Taylor 	if (state->hs_operational_mode == HERMON_MAINTENANCE_MODE) {
8379e39c5baSBill Taylor 		return (EFAULT);
8389e39c5baSBill Taylor 	}
8399e39c5baSBill Taylor 
8409e39c5baSBill Taylor 	/* copyin the user struct to kernel */
8419e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
8429e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
8439e39c5baSBill Taylor 		hermon_ports_ioctl32_t info32;
8449e39c5baSBill Taylor 
8459e39c5baSBill Taylor 		if (ddi_copyin((void *)arg, &info32,
8469e39c5baSBill Taylor 		    sizeof (hermon_ports_ioctl32_t), mode) != 0) {
8479e39c5baSBill Taylor 			return (EFAULT);
8489e39c5baSBill Taylor 		}
8499e39c5baSBill Taylor 		info.ap_revision  = info32.ap_revision;
8509e39c5baSBill Taylor 		info.ap_ports	  =
8519e39c5baSBill Taylor 		    (hermon_stat_port_ioctl_t *)(uintptr_t)info32.ap_ports;
8529e39c5baSBill Taylor 		info.ap_num_ports = info32.ap_num_ports;
8539e39c5baSBill Taylor 
8549e39c5baSBill Taylor 	} else
8559e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
8569e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &info, sizeof (hermon_ports_ioctl_t),
8579e39c5baSBill Taylor 	    mode) != 0) {
8589e39c5baSBill Taylor 		return (EFAULT);
8599e39c5baSBill Taylor 	}
8609e39c5baSBill Taylor 
8619e39c5baSBill Taylor 	/*
8629e39c5baSBill Taylor 	 * Check ioctl revision
8639e39c5baSBill Taylor 	 */
8649e39c5baSBill Taylor 	if (info.ap_revision != HERMON_VTS_IOCTL_REVISION) {
8659e39c5baSBill Taylor 		return (EINVAL);
8669e39c5baSBill Taylor 	}
8679e39c5baSBill Taylor 
8689e39c5baSBill Taylor 	/* Allocate space for temporary GID table/PKey table */
8699e39c5baSBill Taylor 	tbl_size = (1 << state->hs_cfg_profile->cp_log_max_gidtbl);
8709e39c5baSBill Taylor 	sgid_tbl = (ib_gid_t *)kmem_zalloc(tbl_size * sizeof (ib_gid_t),
8719e39c5baSBill Taylor 	    KM_SLEEP);
8729e39c5baSBill Taylor 	tbl_size = (1 << state->hs_cfg_profile->cp_log_max_pkeytbl);
8739e39c5baSBill Taylor 	pkey_tbl = (ib_pkey_t *)kmem_zalloc(tbl_size * sizeof (ib_pkey_t),
8749e39c5baSBill Taylor 	    KM_SLEEP);
8759e39c5baSBill Taylor 
8769e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*sgid_tbl, *pkey_tbl))
8779e39c5baSBill Taylor 
8789e39c5baSBill Taylor 	/*
8799e39c5baSBill Taylor 	 * Setup the number of ports, then loop through all ports and
8809e39c5baSBill Taylor 	 * query properties of each.
8819e39c5baSBill Taylor 	 */
8829e39c5baSBill Taylor 	info.ap_num_ports = (uint8_t)state->hs_cfg_profile->cp_num_ports;
8839e39c5baSBill Taylor 	for (i = 0; i < info.ap_num_ports; i++) {
8849e39c5baSBill Taylor 		/*
8859e39c5baSBill Taylor 		 * Get portstate information from the device.  If
8869e39c5baSBill Taylor 		 * hermon_port_query() fails, leave zeroes in user
8879e39c5baSBill Taylor 		 * struct port entry and continue.
8889e39c5baSBill Taylor 		 */
8899e39c5baSBill Taylor 		bzero(&pi, sizeof (ibt_hca_portinfo_t));
8909e39c5baSBill Taylor 		pi.p_sgid_tbl = sgid_tbl;
8919e39c5baSBill Taylor 		pi.p_pkey_tbl = pkey_tbl;
8929e39c5baSBill Taylor 		(void) hermon_port_query(state, i + 1, &pi);
8939e39c5baSBill Taylor 
8949e39c5baSBill Taylor 		portstat.asp_port_num	= pi.p_port_num;
8959e39c5baSBill Taylor 		portstat.asp_state	= pi.p_linkstate;
8969e39c5baSBill Taylor 		portstat.asp_guid	= pi.p_sgid_tbl[0].gid_guid;
8979e39c5baSBill Taylor 
8989e39c5baSBill Taylor 		/*
8999e39c5baSBill Taylor 		 * Copy queried port results back to user struct.  If
9009e39c5baSBill Taylor 		 * this fails, then break out of loop, attempt to copy
9019e39c5baSBill Taylor 		 * out remaining info to user struct, and return (without
9029e39c5baSBill Taylor 		 * error).
9039e39c5baSBill Taylor 		 */
9049e39c5baSBill Taylor 		if (ddi_copyout(&portstat,
9059e39c5baSBill Taylor 		    &(((hermon_stat_port_ioctl_t *)info.ap_ports)[i]),
9069e39c5baSBill Taylor 		    sizeof (hermon_stat_port_ioctl_t), mode) != 0) {
9079e39c5baSBill Taylor 			break;
9089e39c5baSBill Taylor 		}
9099e39c5baSBill Taylor 	}
9109e39c5baSBill Taylor 
9119e39c5baSBill Taylor 	/* Free the temporary space used for GID table/PKey table */
9129e39c5baSBill Taylor 	tbl_size = (1 << state->hs_cfg_profile->cp_log_max_gidtbl);
9139e39c5baSBill Taylor 	kmem_free(sgid_tbl, tbl_size * sizeof (ib_gid_t));
9149e39c5baSBill Taylor 	tbl_size = (1 << state->hs_cfg_profile->cp_log_max_pkeytbl);
9159e39c5baSBill Taylor 	kmem_free(pkey_tbl, tbl_size * sizeof (ib_pkey_t));
9169e39c5baSBill Taylor 
9179e39c5baSBill Taylor 	/* Copy ioctl results back to user struct */
9189e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
9199e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
9209e39c5baSBill Taylor 		hermon_ports_ioctl32_t info32;
9219e39c5baSBill Taylor 
9229e39c5baSBill Taylor 		info32.ap_revision  = info.ap_revision;
9239e39c5baSBill Taylor 		info32.ap_ports	    = (caddr32_t)(uintptr_t)info.ap_ports;
9249e39c5baSBill Taylor 		info32.ap_num_ports = info.ap_num_ports;
9259e39c5baSBill Taylor 
9269e39c5baSBill Taylor 		if (ddi_copyout(&info32, (void *)arg,
9279e39c5baSBill Taylor 		    sizeof (hermon_ports_ioctl32_t), mode) != 0) {
9289e39c5baSBill Taylor 			return (EFAULT);
9299e39c5baSBill Taylor 		}
9309e39c5baSBill Taylor 	} else
9319e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
9329e39c5baSBill Taylor 	if (ddi_copyout(&info, (void *)arg, sizeof (hermon_ports_ioctl_t),
9339e39c5baSBill Taylor 	    mode) != 0) {
9349e39c5baSBill Taylor 		return (EFAULT);
9359e39c5baSBill Taylor 	}
9369e39c5baSBill Taylor 
9379e39c5baSBill Taylor 	return (0);
9389e39c5baSBill Taylor }
9399e39c5baSBill Taylor 
9409e39c5baSBill Taylor /*
9419e39c5baSBill Taylor  * hermon_ioctl_loopback()
9429e39c5baSBill Taylor  */
9439e39c5baSBill Taylor static int
hermon_ioctl_loopback(hermon_state_t * state,intptr_t arg,int mode)9449e39c5baSBill Taylor hermon_ioctl_loopback(hermon_state_t *state, intptr_t arg, int mode)
9459e39c5baSBill Taylor {
9469e39c5baSBill Taylor 	hermon_loopback_ioctl_t	lb;
9479e39c5baSBill Taylor 	hermon_loopback_state_t	lstate;
9489e39c5baSBill Taylor 	ibt_hca_portinfo_t 	pi;
9499e39c5baSBill Taylor 	uint_t			tbl_size, loopmax, max_usec;
9509e39c5baSBill Taylor 	ib_gid_t		*sgid_tbl;
9519e39c5baSBill Taylor 	ib_pkey_t		*pkey_tbl;
9529e39c5baSBill Taylor 	int			j, iter, ret;
9539e39c5baSBill Taylor 
9549e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(lstate))
9559e39c5baSBill Taylor 
9569e39c5baSBill Taylor 	/*
9579e39c5baSBill Taylor 	 * Access to Hemron VTS ioctls is not allowed in "maintenance mode".
9589e39c5baSBill Taylor 	 */
9599e39c5baSBill Taylor 	if (state->hs_operational_mode == HERMON_MAINTENANCE_MODE) {
9609e39c5baSBill Taylor 		return (EFAULT);
9619e39c5baSBill Taylor 	}
9629e39c5baSBill Taylor 
9639e39c5baSBill Taylor 	/* copyin the user struct to kernel */
9649e39c5baSBill Taylor #ifdef _MULTI_DATAMODEL
9659e39c5baSBill Taylor 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
9669e39c5baSBill Taylor 		hermon_loopback_ioctl32_t lb32;
9679e39c5baSBill Taylor 
9689e39c5baSBill Taylor 		if (ddi_copyin((void *)arg, &lb32,
9699e39c5baSBill Taylor 		    sizeof (hermon_loopback_ioctl32_t), mode) != 0) {
9709e39c5baSBill Taylor 			return (EFAULT);
9719e39c5baSBill Taylor 		}
9729e39c5baSBill Taylor 		lb.alb_revision	    = lb32.alb_revision;
9739e39c5baSBill Taylor 		lb.alb_send_buf	    = (caddr_t)(uintptr_t)lb32.alb_send_buf;
9749e39c5baSBill Taylor 		lb.alb_fail_buf	    = (caddr_t)(uintptr_t)lb32.alb_fail_buf;
9759e39c5baSBill Taylor 		lb.alb_buf_sz	    = lb32.alb_buf_sz;
9769e39c5baSBill Taylor 		lb.alb_num_iter	    = lb32.alb_num_iter;
9779e39c5baSBill Taylor 		lb.alb_pass_done    = lb32.alb_pass_done;
9789e39c5baSBill Taylor 		lb.alb_timeout	    = lb32.alb_timeout;
9799e39c5baSBill Taylor 		lb.alb_error_type   = lb32.alb_error_type;
9809e39c5baSBill Taylor 		lb.alb_port_num	    = lb32.alb_port_num;
9819e39c5baSBill Taylor 		lb.alb_num_retry    = lb32.alb_num_retry;
9829e39c5baSBill Taylor 	} else
9839e39c5baSBill Taylor #endif /* _MULTI_DATAMODEL */
9849e39c5baSBill Taylor 	if (ddi_copyin((void *)arg, &lb, sizeof (hermon_loopback_ioctl_t),
9859e39c5baSBill Taylor 	    mode) != 0) {
9869e39c5baSBill Taylor 		return (EFAULT);
9879e39c5baSBill Taylor 	}
9889e39c5baSBill Taylor 
9899e39c5baSBill Taylor 	/* Initialize the internal loopback test state structure */
9909e39c5baSBill Taylor 	bzero(&lstate, sizeof (hermon_loopback_state_t));
9919e39c5baSBill Taylor 
9929e39c5baSBill Taylor 	/*
9939e39c5baSBill Taylor 	 * Check ioctl revision
9949e39c5baSBill Taylor 	 */
9959e39c5baSBill Taylor 	if (lb.alb_revision != HERMON_VTS_IOCTL_REVISION) {
9969e39c5baSBill Taylor 		lb.alb_error_type = HERMON_LOOPBACK_INVALID_REVISION;
9979e39c5baSBill Taylor 		(void) hermon_loopback_copyout(&lb, arg, mode);
9989e39c5baSBill Taylor 		return (EINVAL);
9999e39c5baSBill Taylor 	}
10009e39c5baSBill Taylor 
10019e39c5baSBill Taylor 	/* Validate that specified port number is legal */