18226594fSRick McNeal /*
28226594fSRick McNeal  * This file and its contents are supplied under the terms of the
38226594fSRick McNeal  * Common Development and Distribution License ("CDDL"), version 1.0.
48226594fSRick McNeal  * You may only use this file in accordance with the terms of version
58226594fSRick McNeal  * 1.0 of the CDDL.
68226594fSRick McNeal  *
78226594fSRick McNeal  * A full copy of the text of the CDDL should have accompanied this
88226594fSRick McNeal  * source.  A copy of the CDDL is also available via the Internet at
98226594fSRick McNeal  * http://www.illumos.org/license/CDDL.
108226594fSRick McNeal  */
118226594fSRick McNeal 
128226594fSRick McNeal /*
138226594fSRick McNeal  * Copyright 2023 Tintri by DDN, Inc. All rights reserved.
148226594fSRick McNeal  * Copyright 2021 Racktop Systems.
158226594fSRick McNeal  */
168226594fSRick McNeal 
178226594fSRick McNeal /*
188226594fSRick McNeal  * This file contains the start up code to initialize the HBA for use
198226594fSRick McNeal  * with the PQI interface.
208226594fSRick McNeal  */
218226594fSRick McNeal #include <smartpqi.h>
228226594fSRick McNeal 
238226594fSRick McNeal #define	PQI_DEVICE_SIGNATURE			"PQI DREG"
248226594fSRick McNeal #define	PQI_STATUS_IDLE				0x0
258226594fSRick McNeal #define	PQI_DEVICE_STATE_ALL_REGISTERS_READY	0x2
268226594fSRick McNeal 
278226594fSRick McNeal typedef struct _func_list_ {
288226594fSRick McNeal 	char		*func_name;
298226594fSRick McNeal 	boolean_t	(*func)(pqi_state_t *);
308226594fSRick McNeal } func_list_t;
318226594fSRick McNeal 
328226594fSRick McNeal static boolean_t pqi_reset_prep(pqi_state_t *);
338226594fSRick McNeal static boolean_t pqi_ctlr_ready(pqi_state_t *);
348226594fSRick McNeal static boolean_t revert_to_sis(pqi_state_t *);
358226594fSRick McNeal static boolean_t pqi_calculate_io_resources(pqi_state_t *);
368226594fSRick McNeal static boolean_t pqi_check_alloc(pqi_state_t *);
378226594fSRick McNeal static boolean_t pqi_wait_for_mode_ready(pqi_state_t *);
388226594fSRick McNeal static boolean_t save_ctrl_mode_pqi(pqi_state_t *);
398226594fSRick McNeal static boolean_t pqi_process_config_table(pqi_state_t *);
408226594fSRick McNeal static boolean_t pqi_alloc_admin_queue(pqi_state_t *);
418226594fSRick McNeal static boolean_t pqi_create_admin_queues(pqi_state_t *);
428226594fSRick McNeal static boolean_t pqi_report_device_capability(pqi_state_t *);
438226594fSRick McNeal static boolean_t pqi_valid_device_capability(pqi_state_t *);
448226594fSRick McNeal static boolean_t pqi_calculate_queue_resources(pqi_state_t *);
458226594fSRick McNeal static boolean_t pqi_alloc_io_resource(pqi_state_t *);
468226594fSRick McNeal static boolean_t pqi_alloc_operation_queues(pqi_state_t *);
478226594fSRick McNeal static boolean_t pqi_init_operational_queues(pqi_state_t *);
488226594fSRick McNeal static boolean_t pqi_init_operational_locks(pqi_state_t *);
498226594fSRick McNeal static boolean_t pqi_create_queues(pqi_state_t *);
508226594fSRick McNeal static boolean_t pqi_change_irq_mode(pqi_state_t *);
518226594fSRick McNeal static boolean_t pqi_start_heartbeat_timer(pqi_state_t *);
528226594fSRick McNeal static boolean_t pqi_enable_events(pqi_state_t *);
538226594fSRick McNeal static boolean_t pqi_get_hba_version(pqi_state_t *);
548226594fSRick McNeal static boolean_t pqi_version_to_hba(pqi_state_t *);
558226594fSRick McNeal static boolean_t pqi_schedule_update_time_worker(pqi_state_t *);
568226594fSRick McNeal static boolean_t pqi_scan_scsi_devices(pqi_state_t *);
578226594fSRick McNeal 
588226594fSRick McNeal func_list_t startup_funcs[] =
598226594fSRick McNeal {
608226594fSRick McNeal 	{ "sis_wait_for_ctrl_ready", sis_wait_for_ctrl_ready },
618226594fSRick McNeal 	{ "sis_get_ctrl_props", sis_get_ctrl_props },
628226594fSRick McNeal 	{ "sis_get_pqi_capabilities", sis_get_pqi_capabilities },
638226594fSRick McNeal 	{ "pqi_calculate_io_resources", pqi_calculate_io_resources },
648226594fSRick McNeal 	{ "pqi_check_alloc", pqi_check_alloc },
658226594fSRick McNeal 	{ "sis_init_base_struct_addr", sis_init_base_struct_addr },
668226594fSRick McNeal 	{ "pqi_wait_for_mode_ready", pqi_wait_for_mode_ready },
678226594fSRick McNeal 	{ "save_ctrl_mode_pqi", save_ctrl_mode_pqi },
688226594fSRick McNeal 	{ "pqi_process_config_table", pqi_process_config_table },
698226594fSRick McNeal 	{ "pqi_alloc_admin_queue", pqi_alloc_admin_queue },
708226594fSRick McNeal 	{ "pqi_create_admin_queues", pqi_create_admin_queues },
718226594fSRick McNeal 	{ "pqi_report_device_capability", pqi_report_device_capability },
728226594fSRick McNeal 	{ "pqi_valid_device_capability", pqi_valid_device_capability },
738226594fSRick McNeal 	{ "pqi_calculate_queue_resources", pqi_calculate_queue_resources },
748226594fSRick McNeal 	{ "pqi_alloc_io_resource", pqi_alloc_io_resource },
758226594fSRick McNeal 	{ "pqi_alloc_operation_queues", pqi_alloc_operation_queues },
768226594fSRick McNeal 	{ "pqi_init_operational_queues", pqi_init_operational_queues },
778226594fSRick McNeal 	{ "pqi_init_operational_locks", pqi_init_operational_locks },
788226594fSRick McNeal 	{ "pqi_create_queues", pqi_create_queues },
798226594fSRick McNeal 	{ "pqi_change_irq_mode", pqi_change_irq_mode },
808226594fSRick McNeal 	{ "pqi_start_heartbeat_timer", pqi_start_heartbeat_timer },
818226594fSRick McNeal 	{ "pqi_enable_events", pqi_enable_events },
828226594fSRick McNeal 	{ "pqi_get_hba_version", pqi_get_hba_version },
838226594fSRick McNeal 	{ "pqi_version_to_hba", pqi_version_to_hba },
848226594fSRick McNeal 	{ "pqi_schedule_update_time_worker", pqi_schedule_update_time_worker },
858226594fSRick McNeal 	{ "pqi_scan_scsi_devices", pqi_scan_scsi_devices },
868226594fSRick McNeal 	{ NULL, NULL }
878226594fSRick McNeal };
888226594fSRick McNeal 
898226594fSRick McNeal func_list_t reset_funcs[] =
908226594fSRick McNeal {
918226594fSRick McNeal 	{ "pqi_reset_prep", pqi_reset_prep },
928226594fSRick McNeal 	{ "revert_to_sis", revert_to_sis },
938226594fSRick McNeal 	{ "pqi_check_firmware", pqi_check_firmware },
948226594fSRick McNeal 	{ "sis_wait_for_ctrl_ready", sis_wait_for_ctrl_ready },
958226594fSRick McNeal 	{ "sis_get_ctrl_props", sis_get_ctrl_props },
968226594fSRick McNeal 	{ "sis_get_pqi_capabilities", sis_get_pqi_capabilities },
978226594fSRick McNeal 	{ "pqi_calculate_io_resources", pqi_calculate_io_resources },
988226594fSRick McNeal 	{ "pqi_check_alloc", pqi_check_alloc },
998226594fSRick McNeal 	{ "sis_init_base_struct_addr", sis_init_base_struct_addr },
1008226594fSRick McNeal 	{ "pqi_wait_for_mode_ready", pqi_wait_for_mode_ready },
1018226594fSRick McNeal 	{ "save_ctrl_mode_pqi", save_ctrl_mode_pqi },
1028226594fSRick McNeal 	{ "pqi_process_config_table", pqi_process_config_table },
1038226594fSRick McNeal 	{ "pqi_alloc_admin_queue", pqi_alloc_admin_queue },
1048226594fSRick McNeal 	{ "pqi_create_admin_queues", pqi_create_admin_queues },
1058226594fSRick McNeal 	{ "pqi_report_device_capability", pqi_report_device_capability },
1068226594fSRick McNeal 	{ "pqi_valid_device_capability", pqi_valid_device_capability },
1078226594fSRick McNeal 	{ "pqi_calculate_queue_resources", pqi_calculate_queue_resources },
1088226594fSRick McNeal 	{ "pqi_alloc_io_resource", pqi_alloc_io_resource },
1098226594fSRick McNeal 	{ "pqi_alloc_operation_queues", pqi_alloc_operation_queues },
1108226594fSRick McNeal 	{ "pqi_init_operational_queues", pqi_init_operational_queues },
1118226594fSRick McNeal 	{ "pqi_create_queues", pqi_create_queues },
1128226594fSRick McNeal 	{ "pqi_change_irq_mode", pqi_change_irq_mode },
1138226594fSRick McNeal 	{ "pqi_ctlr_ready", pqi_ctlr_ready },
1148226594fSRick McNeal 	{ "pqi_start_heartbeat_timer", pqi_start_heartbeat_timer },
1158226594fSRick McNeal 	{ "pqi_enable_events", pqi_enable_events },
1168226594fSRick McNeal 	{ "pqi_get_hba_version", pqi_get_hba_version },
1178226594fSRick McNeal 	{ "pqi_version_to_hba", pqi_version_to_hba },
1188226594fSRick McNeal 	{ "pqi_schedule_update_time_worker", pqi_schedule_update_time_worker },
1198226594fSRick McNeal 	{ NULL, NULL }
1208226594fSRick McNeal };
1218226594fSRick McNeal 
1228226594fSRick McNeal /* ---- Forward declarations for utility functions ---- */
1238226594fSRick McNeal static void bcopy_fromregs(pqi_state_t *s, uint8_t *iomem, uint8_t *dst,
1248226594fSRick McNeal     uint32_t len);
1258226594fSRick McNeal static boolean_t submit_admin_rqst_sync(pqi_state_t *s,
1268226594fSRick McNeal     pqi_general_admin_request_t *rqst, pqi_general_admin_response_t *rsp);
1278226594fSRick McNeal static boolean_t create_event_queue(pqi_state_t *s);
1288226594fSRick McNeal static boolean_t create_queue_group(pqi_state_t *s, int idx);
1298226594fSRick McNeal static boolean_t submit_raid_rqst_sync(pqi_state_t *s, pqi_iu_header_t *rqst,
1308226594fSRick McNeal     pqi_raid_error_info_t e_info);
1318226594fSRick McNeal static boolean_t identify_controller(pqi_state_t *s,
1328226594fSRick McNeal     bmic_identify_controller_t *ident);
1338226594fSRick McNeal static boolean_t write_host_wellness(pqi_state_t *s, void *buf, size_t len);
1348226594fSRick McNeal static boolean_t get_device_list(pqi_state_t *s,
135*4091c922SYuri Pankov     report_phys_lun_extended_t **pl, size_t *plen,
136*4091c922SYuri Pankov     report_log_lun_extended_t **ll, size_t *llen);
1378226594fSRick McNeal static boolean_t build_raid_path_request(pqi_raid_path_request_t *rqst, int cmd,
1388226594fSRick McNeal     caddr_t lun, uint32_t len, int vpd_page);
1398226594fSRick McNeal static boolean_t identify_physical_device(pqi_state_t *s, pqi_device_t *devp,
1408226594fSRick McNeal     bmic_identify_physical_device_t *buf);
1418226594fSRick McNeal static pqi_device_t *create_phys_dev(pqi_state_t *s,
1428226594fSRick McNeal     report_phys_lun_extended_entry_t *e);
1438226594fSRick McNeal static pqi_device_t *create_logical_dev(pqi_state_t *s,
1448226594fSRick McNeal     report_log_lun_extended_entry_t *e);
1458226594fSRick McNeal static boolean_t is_new_dev(pqi_state_t *s, pqi_device_t *new_dev);
1468226594fSRick McNeal static boolean_t revert_to_sis(pqi_state_t *s);
1478226594fSRick McNeal static void save_ctrl_mode(pqi_state_t *s, int mode);
1488226594fSRick McNeal static boolean_t scsi_common(pqi_state_t *s, pqi_raid_path_request_t *rqst,
1498226594fSRick McNeal     caddr_t buf, int len);
1508226594fSRick McNeal static void update_time(void *v);
1518226594fSRick McNeal 
1528226594fSRick McNeal static int reset_devices = 1;
1538226594fSRick McNeal 
1548226594fSRick McNeal int pqi_max_io_slots = PQI_MAX_IO_SLOTS;
1558226594fSRick McNeal 
1568226594fSRick McNeal static boolean_t
pqi_reset_prep(pqi_state_t * s)1578226594fSRick McNeal pqi_reset_prep(pqi_state_t *s)
1588226594fSRick McNeal {
1598226594fSRick McNeal 	s->s_intr_ready = B_FALSE;
1608226594fSRick McNeal 	(void) untimeout(s->s_time_of_day);
1618226594fSRick McNeal 	(void) untimeout(s->s_watchdog);
1628226594fSRick McNeal 	pqi_free_single(s, s->s_error_dma);
1638226594fSRick McNeal 	s->s_error_dma = NULL;
1648226594fSRick McNeal 
1658226594fSRick McNeal 	pqi_free_single(s, s->s_adminq_dma);
1668226594fSRick McNeal 	s->s_adminq_dma = NULL;
1678226594fSRick McNeal 
1688226594fSRick McNeal 	mutex_enter(&s->s_io_mutex);
1698226594fSRick McNeal 	pqi_free_io_resource(s);
1708226594fSRick McNeal 	mutex_exit(&s->s_io_mutex);
1718226594fSRick McNeal 	return (B_TRUE);
1728226594fSRick McNeal }
1738226594fSRick McNeal 
1748226594fSRick McNeal static boolean_t
pqi_ctlr_ready(pqi_state_t * s)1758226594fSRick McNeal pqi_ctlr_ready(pqi_state_t *s)
1768226594fSRick McNeal {
1778226594fSRick McNeal 	s->s_offline = B_FALSE;
1788226594fSRick McNeal 	return (B_TRUE);
1798226594fSRick McNeal }
1808226594fSRick McNeal 
1818226594fSRick McNeal boolean_t
pqi_check_firmware(pqi_state_t * s)1828226594fSRick McNeal pqi_check_firmware(pqi_state_t *s)
1838226594fSRick McNeal {
1848226594fSRick McNeal 	uint32_t	status;
1858226594fSRick McNeal 
1868226594fSRick McNeal 	status = G32(s, sis_firmware_status);
1878226594fSRick McNeal 	if (status & SIS_CTRL_KERNEL_PANIC)
1888226594fSRick McNeal 		return (B_FALSE);
1898226594fSRick McNeal 
1908226594fSRick McNeal 	if (sis_read_scratch(s) == SIS_MODE)
1918226594fSRick McNeal 		return (B_TRUE);
1928226594fSRick McNeal 
1938226594fSRick McNeal 	if (status & SIS_CTRL_KERNEL_UP) {
1948226594fSRick McNeal 		sis_write_scratch(s, SIS_MODE);
1958226594fSRick McNeal 		return (B_TRUE);
1968226594fSRick McNeal 	} else {
1978226594fSRick McNeal 		return (revert_to_sis(s));
1988226594fSRick McNeal 	}
1998226594fSRick McNeal }
2008226594fSRick McNeal 
2018226594fSRick McNeal boolean_t
pqi_prep_full(pqi_state_t * s)2028226594fSRick McNeal pqi_prep_full(pqi_state_t *s)
2038226594fSRick McNeal {
2048226594fSRick McNeal 	func_list_t	*f;
2058226594fSRick McNeal 
2068226594fSRick McNeal 	for (f = startup_funcs; f->func_name != NULL; f++)
2078226594fSRick McNeal 		if (f->func(s) == B_FALSE) {
2088226594fSRick McNeal 			cmn_err(CE_WARN, "Init failed on %s", f->func_name);
2098226594fSRick McNeal 			return (B_FALSE);
2108226594fSRick McNeal 		}
2118226594fSRick McNeal 
2128226594fSRick McNeal 	return (B_TRUE);
2138226594fSRick McNeal }
2148226594fSRick McNeal 
2158226594fSRick McNeal boolean_t
pqi_reset_ctl(pqi_state_t * s)2168226594fSRick McNeal pqi_reset_ctl(pqi_state_t *s)
2178226594fSRick McNeal {
2188226594fSRick McNeal 	func_list_t	*f;
2198226594fSRick McNeal 
2208226594fSRick McNeal 	for (f = reset_funcs; f->func_name != NULL; f++)
2218226594fSRick McNeal 		if (f->func(s) == B_FALSE) {
2228226594fSRick McNeal 			cmn_err(CE_WARN, "Reset failed on %s", f->func_name);
2238226594fSRick McNeal 			return (B_FALSE);
2248226594fSRick McNeal 		}
2258226594fSRick McNeal 
2268226594fSRick McNeal 	return (B_TRUE);
2278226594fSRick McNeal }
2288226594fSRick McNeal /*
2298226594fSRick McNeal  * []----------------------------------------------------------[]
2308226594fSRick McNeal  * | Startup functions called in sequence to initialize HBA.	|
2318226594fSRick McNeal  * []----------------------------------------------------------[]
2328226594fSRick McNeal  */
2338226594fSRick McNeal 
2348226594fSRick McNeal static boolean_t
pqi_calculate_io_resources(pqi_state_t * s)2358226594fSRick McNeal pqi_calculate_io_resources(pqi_state_t *s)
2368226594fSRick McNeal {
2378226594fSRick McNeal 	uint32_t	max_xfer_size;
2388226594fSRick McNeal 	uint32_t	max_sg_entries;
2398226594fSRick McNeal 
2408226594fSRick McNeal 	s->s_max_io_slots = s->s_max_outstanding_requests;
2418226594fSRick McNeal 
2428226594fSRick McNeal 	max_xfer_size = min(s->s_max_xfer_size, PQI_MAX_TRANSFER_SIZE);
2438226594fSRick McNeal 
2448226594fSRick McNeal 	/* ---- add 1 when buf is not page aligned ---- */
2458226594fSRick McNeal 	max_sg_entries = max_xfer_size / PAGESIZE + 1;
2468226594fSRick McNeal 	max_sg_entries = min(max_sg_entries, s->s_max_sg_entries);
2478226594fSRick McNeal 	max_xfer_size = (max_sg_entries - 1) * PAGESIZE;
2488226594fSRick McNeal 
2498226594fSRick McNeal 	s->s_sg_chain_buf_length = (max_sg_entries * sizeof (pqi_sg_entry_t)) +
2508226594fSRick McNeal 	    PQI_EXTRA_SGL_MEMORY;
2518226594fSRick McNeal 
2528226594fSRick McNeal 	s->s_max_sectors = max_xfer_size / 512;
2538226594fSRick McNeal 
2548226594fSRick McNeal 	return (B_TRUE);
2558226594fSRick McNeal }
2568226594fSRick McNeal 
2578226594fSRick McNeal static boolean_t
pqi_check_alloc(pqi_state_t * s)2588226594fSRick McNeal pqi_check_alloc(pqi_state_t *s)
2598226594fSRick McNeal {
2608226594fSRick McNeal 	/*
2618226594fSRick McNeal 	 * Note that we need to pass a generation cnt as part of a i/o
2628226594fSRick McNeal 	 * request id.  The id is limited to 16 bits and we reserve 4 bits
2638226594fSRick McNeal 	 * for a generation no.  This means we must limit s_max_io_slots
2648226594fSRick McNeal 	 * to max 12 bits worth of slot indexes.
2658226594fSRick McNeal 	 */
2668226594fSRick McNeal 	if (pqi_max_io_slots != 0 && pqi_max_io_slots < s->s_max_io_slots) {
2678226594fSRick McNeal 		s->s_max_io_slots = pqi_max_io_slots;
2688226594fSRick McNeal 	}
2698226594fSRick McNeal 
2708226594fSRick McNeal 	s->s_error_dma = pqi_alloc_single(s, (s->s_max_io_slots *
2718226594fSRick McNeal 	    PQI_ERROR_BUFFER_ELEMENT_LENGTH) + SIS_BASE_STRUCT_ALIGNMENT);
2728226594fSRick McNeal 	if (s->s_error_dma == NULL)
2738226594fSRick McNeal 		return (B_FALSE);
2748226594fSRick McNeal 
2758226594fSRick McNeal 	return (B_TRUE);
2768226594fSRick McNeal }
2778226594fSRick McNeal 
2788226594fSRick McNeal #define	WAIT_FOR_FIRMWARE_IN_MSECS (5 * MILLISEC)
2798226594fSRick McNeal 
2808226594fSRick McNeal static boolean_t
pqi_wait_for_mode_ready(pqi_state_t * s)2818226594fSRick McNeal pqi_wait_for_mode_ready(pqi_state_t *s)
2828226594fSRick McNeal {
2838226594fSRick McNeal 	uint64_t	signature;
2848226594fSRick McNeal 	int32_t		count = WAIT_FOR_FIRMWARE_IN_MSECS;
2858226594fSRick McNeal 
2868226594fSRick McNeal 	for (;;) {
2878226594fSRick McNeal 		signature = G64(s, pqi_registers.signature);
2888226594fSRick McNeal 		if (memcmp(&signature, PQI_DEVICE_SIGNATURE,
2898226594fSRick McNeal 		    sizeof (signature)) == 0)
2908226594fSRick McNeal 			break;
2918226594fSRick McNeal 		if (count-- == 0)
2928226594fSRick McNeal 			return (B_FALSE);
2938226594fSRick McNeal 		drv_usecwait(MICROSEC / MILLISEC);
2948226594fSRick McNeal 	}
2958226594fSRick McNeal 
2968226594fSRick McNeal 	count = WAIT_FOR_FIRMWARE_IN_MSECS;
2978226594fSRick McNeal 	for (;;) {
2988226594fSRick McNeal 		if (G64(s, pqi_registers.function_and_status_code) ==
2998226594fSRick McNeal 		    PQI_STATUS_IDLE)
3008226594fSRick McNeal 			break;
3018226594fSRick McNeal 		if (count-- == 0)
3028226594fSRick McNeal 			return (B_FALSE);
3038226594fSRick McNeal 		drv_usecwait(MICROSEC / MILLISEC);
3048226594fSRick McNeal 	}
3058226594fSRick McNeal 
3068226594fSRick McNeal 	count = WAIT_FOR_FIRMWARE_IN_MSECS;
3078226594fSRick McNeal 	for (;;) {
3088226594fSRick McNeal 		if (G32(s, pqi_registers.device_status) ==
3098226594fSRick McNeal 		    PQI_DEVICE_STATE_ALL_REGISTERS_READY)
3108226594fSRick McNeal 			break;
3118226594fSRick McNeal 		if (count-- == 0)
3128226594fSRick McNeal 			return (B_FALSE);
3138226594fSRick McNeal 		drv_usecwait(MICROSEC / MILLISEC);
3148226594fSRick McNeal 	}
3158226594fSRick McNeal 
3168226594fSRick McNeal 	return (B_TRUE);
3178226594fSRick McNeal }
3188226594fSRick McNeal 
3198226594fSRick McNeal static boolean_t
save_ctrl_mode_pqi(pqi_state_t * s)3208226594fSRick McNeal save_ctrl_mode_pqi(pqi_state_t *s)
3218226594fSRick McNeal {
3228226594fSRick McNeal 	save_ctrl_mode(s, PQI_MODE);
3238226594fSRick McNeal 	return (B_TRUE);
3248226594fSRick McNeal }
3258226594fSRick McNeal 
3268226594fSRick McNeal static boolean_t
pqi_process_config_table(pqi_state_t * s)3278226594fSRick McNeal pqi_process_config_table(pqi_state_t *s)
3288226594fSRick McNeal {
3298226594fSRick McNeal 	pqi_config_table_t			*c_table;
3308226594fSRick McNeal 	pqi_config_table_section_header_t	*section;
3318226594fSRick McNeal 	uint32_t				section_offset;
3328226594fSRick McNeal 
3338226594fSRick McNeal 	c_table = kmem_zalloc(s->s_config_table_len, KM_SLEEP);
3348226594fSRick McNeal 	bcopy_fromregs(s, (uint8_t *)s->s_reg + s->s_config_table_offset,
3358226594fSRick McNeal 	    (uint8_t *)c_table, s->s_config_table_len);
3368226594fSRick McNeal 
3378226594fSRick McNeal 	section_offset = c_table->first_section_offset;
3388226594fSRick McNeal 	while (section_offset) {
3398226594fSRick McNeal 		section = (pqi_config_table_section_header_t *)
3408226594fSRick McNeal 		    ((caddr_t)c_table + section_offset);
3418226594fSRick McNeal 		switch (section->section_id) {
3428226594fSRick McNeal 		case PQI_CONFIG_TABLE_SECTION_HEARTBEAT:
3438226594fSRick McNeal 			/* LINTED E_BAD_PTR_CAST_ALIGN */
3448226594fSRick McNeal 			s->s_heartbeat_counter = (uint32_t *)
3458226594fSRick McNeal 			    ((caddr_t)s->s_reg +
3468226594fSRick McNeal 			    s->s_config_table_offset + section_offset +
3478226594fSRick McNeal 			    offsetof(struct pqi_config_table_heartbeat,
3488226594fSRick McNeal 			    heartbeat_counter));
3498226594fSRick McNeal 			break;
3508226594fSRick McNeal 		}
3518226594fSRick McNeal 		section_offset = section->next_section_offset;
3528226594fSRick McNeal 	}
3538226594fSRick McNeal 	kmem_free(c_table, s->s_config_table_len);
3548226594fSRick McNeal 	return (B_TRUE);
3558226594fSRick McNeal }
3568226594fSRick McNeal 
3578226594fSRick McNeal static boolean_t
pqi_alloc_admin_queue(pqi_state_t * s)3588226594fSRick McNeal pqi_alloc_admin_queue(pqi_state_t *s)
3598226594fSRick McNeal {
3608226594fSRick McNeal 	pqi_admin_queues_t		*aq;
3618226594fSRick McNeal 	pqi_admin_queues_aligned_t	*aq_aligned;
3628226594fSRick McNeal 	int				len;
3638226594fSRick McNeal 
3648226594fSRick McNeal 	len = sizeof (*aq_aligned) + PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT;
3658226594fSRick McNeal 	if ((s->s_adminq_dma = pqi_alloc_single(s, len)) == NULL)
3668226594fSRick McNeal 		return (B_FALSE);
3678226594fSRick McNeal 	(void) memset(s->s_adminq_dma->alloc_memory, 0,
3688226594fSRick McNeal 	    s->s_adminq_dma->len_to_alloc);
3698226594fSRick McNeal 	(void) ddi_dma_sync(s->s_adminq_dma->handle, 0,
3708226594fSRick McNeal 	    s->s_adminq_dma->len_to_alloc, DDI_DMA_SYNC_FORDEV);
3718226594fSRick McNeal 
3728226594fSRick McNeal 	aq = &s->s_admin_queues;
3738226594fSRick McNeal 	aq_aligned = PQIALIGN_TYPED(s->s_adminq_dma->alloc_memory,
3748226594fSRick McNeal 	    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, pqi_admin_queues_aligned_t *);
3758226594fSRick McNeal 	aq->iq_element_array = (caddr_t)&aq_aligned->iq_element_array;
3768226594fSRick McNeal 	aq->oq_element_array = (caddr_t)&aq_aligned->oq_element_array;
3778226594fSRick McNeal 	aq->iq_ci = &aq_aligned->iq_ci;
3788226594fSRick McNeal 	aq->oq_pi = &aq_aligned->oq_pi;
3798226594fSRick McNeal 
3808226594fSRick McNeal 	aq->iq_element_array_bus_addr = s->s_adminq_dma->dma_addr +
3818226594fSRick McNeal 	    ((uintptr_t)aq->iq_element_array -
3828226594fSRick McNeal 	    (uintptr_t)s->s_adminq_dma->alloc_memory);
3838226594fSRick McNeal 	aq->oq_element_array_bus_addr = s->s_adminq_dma->dma_addr +
3848226594fSRick McNeal 	    ((uintptr_t)aq->oq_element_array -
3858226594fSRick McNeal 	    (uintptr_t)s->s_adminq_dma->alloc_memory);
3868226594fSRick McNeal 
3878226594fSRick McNeal 	aq->iq_ci_bus_addr = s->s_adminq_dma->dma_addr +
3888226594fSRick McNeal 	    ((uintptr_t)aq->iq_ci - (uintptr_t)s->s_adminq_dma->alloc_memory);
3898226594fSRick McNeal 	aq->oq_pi_bus_addr = s->s_adminq_dma->dma_addr +
3908226594fSRick McNeal 	    ((uintptr_t)aq->oq_pi - (uintptr_t)s->s_adminq_dma->alloc_memory);
3918226594fSRick McNeal 	return (B_TRUE);
3928226594fSRick McNeal }
3938226594fSRick McNeal 
3948226594fSRick McNeal static boolean_t
pqi_create_admin_queues(pqi_state_t * s)3958226594fSRick McNeal pqi_create_admin_queues(pqi_state_t *s)
3968226594fSRick McNeal {
3978226594fSRick McNeal 	pqi_admin_queues_t *aq = &s->s_admin_queues;
3988226594fSRick McNeal 	int			val;
3998226594fSRick McNeal 	int			status;
4008226594fSRick McNeal 	int			countdown = 1000;
4018226594fSRick McNeal 
4028226594fSRick McNeal 
4038226594fSRick McNeal 	aq->iq_pi_copy = 0;
4048226594fSRick McNeal 	aq->oq_ci_copy = 0;
4058226594fSRick McNeal 
4068226594fSRick McNeal 	S64(s, pqi_registers.admin_iq_element_array_addr,
4078226594fSRick McNeal 	    aq->iq_element_array_bus_addr);
4088226594fSRick McNeal 	S64(s, pqi_registers.admin_oq_element_array_addr,
4098226594fSRick McNeal 	    aq->oq_element_array_bus_addr);
4108226594fSRick McNeal 	S64(s, pqi_registers.admin_iq_ci_addr,
4118226594fSRick McNeal 	    aq->iq_ci_bus_addr);
4128226594fSRick McNeal 	S64(s, pqi_registers.admin_oq_pi_addr,
4138226594fSRick McNeal 	    aq->oq_pi_bus_addr);
4148226594fSRick McNeal 
4158226594fSRick McNeal 	val = PQI_ADMIN_IQ_NUM_ELEMENTS | PQI_ADMIN_OQ_NUM_ELEMENTS << 8 |
4168226594fSRick McNeal 	    aq->int_msg_num << 16;
4178226594fSRick McNeal 	S32(s, pqi_registers.admin_queue_params, val);
4188226594fSRick McNeal 	S64(s, pqi_registers.function_and_status_code,
4198226594fSRick McNeal 	    PQI_CREATE_ADMIN_QUEUE_PAIR);
4208226594fSRick McNeal 
4218226594fSRick McNeal 	while (countdown-- > 0) {
4228226594fSRick McNeal 		status = G64(s, pqi_registers.function_and_status_code);
4238226594fSRick McNeal 		if (status == PQI_STATUS_IDLE)
4248226594fSRick McNeal 			break;
4258226594fSRick McNeal 		drv_usecwait(1000);	/* ---- Wait 1ms ---- */
4268226594fSRick McNeal 	}
4278226594fSRick McNeal 	if (countdown == 0)
4288226594fSRick McNeal 		return (B_FALSE);
4298226594fSRick McNeal 
4308226594fSRick McNeal 	/*
4318226594fSRick McNeal 	 * The offset registers are not initialized to the correct
4328226594fSRick McNeal 	 * offsets until *after* the create admin queue pair command
4338226594fSRick McNeal 	 * completes successfully.
4348226594fSRick McNeal 	 */
4358226594fSRick McNeal 	aq->iq_pi = (void *)(intptr_t)((intptr_t)s->s_reg +
4368226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
4378226594fSRick McNeal 	    G64(s, pqi_registers.admin_iq_pi_offset));
4388226594fSRick McNeal 	ASSERT((G64(s, pqi_registers.admin_iq_pi_offset) +
4398226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET) < 0x8000);
4408226594fSRick McNeal 
4418226594fSRick McNeal 	aq->oq_ci = (void *)(intptr_t)((intptr_t)s->s_reg +
4428226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
4438226594fSRick McNeal 	    G64(s, pqi_registers.admin_oq_ci_offset));
4448226594fSRick McNeal 	ASSERT((G64(s, pqi_registers.admin_oq_ci_offset) +
4458226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET) < 0x8000);
4468226594fSRick McNeal 
4478226594fSRick McNeal 	return (B_TRUE);
4488226594fSRick McNeal }
4498226594fSRick McNeal 
4508226594fSRick McNeal static boolean_t
pqi_report_device_capability(pqi_state_t * s)4518226594fSRick McNeal pqi_report_device_capability(pqi_state_t *s)
4528226594fSRick McNeal {
4538226594fSRick McNeal 	pqi_general_admin_request_t	rqst;
4548226594fSRick McNeal 	pqi_general_admin_response_t	rsp;
4558226594fSRick McNeal 	pqi_device_capability_t		*cap;
4568226594fSRick McNeal 	pqi_iu_layer_descriptor_t	*iu_layer;
4578226594fSRick McNeal 	pqi_dma_overhead_t		*dma;
4588226594fSRick McNeal 	boolean_t			rval;
4598226594fSRick McNeal 	pqi_sg_entry_t			*sg;
4608226594fSRick McNeal 
4618226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
4628226594fSRick McNeal 
4638226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
4648226594fSRick McNeal 	rqst.header.iu_length = PQI_GENERAL_ADMIN_IU_LENGTH;
4658226594fSRick McNeal 	rqst.function_code =
4668226594fSRick McNeal 	    PQI_GENERAL_ADMIN_FUNCTION_REPORT_DEVICE_CAPABILITY;
4678226594fSRick McNeal 	rqst.data.report_device_capability.buffer_length =
4688226594fSRick McNeal 	    sizeof (*cap);
4698226594fSRick McNeal 
4708226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, sizeof (*cap))) == NULL)
4718226594fSRick McNeal 		return (B_FALSE);
4728226594fSRick McNeal 
4738226594fSRick McNeal 	sg = &rqst.data.report_device_capability.sg_descriptor;
4748226594fSRick McNeal 	sg->sg_addr = dma->dma_addr;
4758226594fSRick McNeal 	sg->sg_len = dma->len_to_alloc;
4768226594fSRick McNeal 	sg->sg_flags = CISS_SG_LAST;
4778226594fSRick McNeal 
4788226594fSRick McNeal 	rval = submit_admin_rqst_sync(s, &rqst, &rsp);
4798226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
4808226594fSRick McNeal 	cap = (pqi_device_capability_t *)dma->alloc_memory;
4818226594fSRick McNeal 
4828226594fSRick McNeal 	s->s_max_inbound_queues = cap->max_inbound_queues;
4838226594fSRick McNeal 	s->s_max_elements_per_iq = cap->max_elements_per_iq;
4848226594fSRick McNeal 	s->s_max_iq_element_length = cap->max_iq_element_length * 16;
4858226594fSRick McNeal 	s->s_max_outbound_queues = cap->max_outbound_queues;
4868226594fSRick McNeal 	s->s_max_elements_per_oq = cap->max_elements_per_oq;
4878226594fSRick McNeal 	s->s_max_oq_element_length = cap->max_oq_element_length * 16;
4888226594fSRick McNeal 
4898226594fSRick McNeal 	iu_layer = &cap->iu_layer_descriptors[PQI_PROTOCOL_SOP];
4908226594fSRick McNeal 	s->s_max_inbound_iu_length_per_firmware =
4918226594fSRick McNeal 	    iu_layer->max_inbound_iu_length;
4928226594fSRick McNeal 	s->s_inbound_spanning_supported = iu_layer->inbound_spanning_supported;
4938226594fSRick McNeal 	s->s_outbound_spanning_supported =
4948226594fSRick McNeal 	    iu_layer->outbound_spanning_supported;
4958226594fSRick McNeal 
4968226594fSRick McNeal 	pqi_free_single(s, dma);
4978226594fSRick McNeal 	return (rval);
4988226594fSRick McNeal }
4998226594fSRick McNeal 
5008226594fSRick McNeal static boolean_t
pqi_valid_device_capability(pqi_state_t * s)5018226594fSRick McNeal pqi_valid_device_capability(pqi_state_t *s)
5028226594fSRick McNeal {
5038226594fSRick McNeal 	if (s->s_max_iq_element_length < PQI_OPERATIONAL_IQ_ELEMENT_LENGTH)
5048226594fSRick McNeal 		return (B_FALSE);
5058226594fSRick McNeal 	if (s->s_max_oq_element_length < PQI_OPERATIONAL_OQ_ELEMENT_LENGTH)
5068226594fSRick McNeal 		return (B_FALSE);
5078226594fSRick McNeal 	if (s->s_max_inbound_iu_length_per_firmware <
5088226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH)
5098226594fSRick McNeal 		return (B_FALSE);
5108226594fSRick McNeal 	/* ---- Controller doesn't support spanning but we need it ---- */
5118226594fSRick McNeal 	if (!s->s_inbound_spanning_supported)
5128226594fSRick McNeal 		return (B_FALSE);
5138226594fSRick McNeal 	/* ---- Controller wants outbound spanning, the driver doesn't ---- */
5148226594fSRick McNeal 	if (s->s_outbound_spanning_supported)
5158226594fSRick McNeal 		return (B_FALSE);
5168226594fSRick McNeal 
5178226594fSRick McNeal 	return (B_TRUE);
5188226594fSRick McNeal }
5198226594fSRick McNeal 
5208226594fSRick McNeal static boolean_t
pqi_calculate_queue_resources(pqi_state_t * s)5218226594fSRick McNeal pqi_calculate_queue_resources(pqi_state_t *s)
5228226594fSRick McNeal {
5238226594fSRick McNeal 	int	max_queue_groups;
5248226594fSRick McNeal 	int	num_queue_groups;
5258226594fSRick McNeal 	int	num_elements_per_iq;
5268226594fSRick McNeal 	int	num_elements_per_oq;
5278226594fSRick McNeal 
5288226594fSRick McNeal 	if (reset_devices) {
5298226594fSRick McNeal 		num_queue_groups = 1;
5308226594fSRick McNeal 	} else {
5318226594fSRick McNeal 		max_queue_groups = min(s->s_max_inbound_queues / 2,
5328226594fSRick McNeal 		    s->s_max_outbound_queues - 1);
5338226594fSRick McNeal 		max_queue_groups = min(max_queue_groups, PQI_MAX_QUEUE_GROUPS);
5348226594fSRick McNeal 
5358226594fSRick McNeal 		num_queue_groups = min(ncpus, s->s_intr_cnt);
5368226594fSRick McNeal 		num_queue_groups = min(num_queue_groups, max_queue_groups);
5378226594fSRick McNeal 	}
5388226594fSRick McNeal 	s->s_num_queue_groups = num_queue_groups;
5398226594fSRick McNeal 
5408226594fSRick McNeal 	s->s_max_inbound_iu_length =
5418226594fSRick McNeal 	    (s->s_max_inbound_iu_length_per_firmware /
5428226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) *
5438226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH;
5448226594fSRick McNeal 
5458226594fSRick McNeal 	num_elements_per_iq = s->s_max_inbound_iu_length /
5468226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH;
5478226594fSRick McNeal 	/* ---- add one because one element in each queue is unusable ---- */
5488226594fSRick McNeal 	num_elements_per_iq++;
5498226594fSRick McNeal 
5508226594fSRick McNeal 	num_elements_per_iq = min(num_elements_per_iq,
5518226594fSRick McNeal 	    s->s_max_elements_per_iq);
5528226594fSRick McNeal 
5538226594fSRick McNeal 	num_elements_per_oq = ((num_elements_per_iq - 1) * 2) + 1;
5548226594fSRick McNeal 	num_elements_per_oq = min(num_elements_per_oq,
5558226594fSRick McNeal 	    s->s_max_elements_per_oq);
5568226594fSRick McNeal 
5578226594fSRick McNeal 	s->s_num_elements_per_iq = num_elements_per_iq;
5588226594fSRick McNeal 	s->s_num_elements_per_oq = num_elements_per_oq;
5598226594fSRick McNeal 
5608226594fSRick McNeal 	s->s_max_sg_per_iu = ((s->s_max_inbound_iu_length -
5618226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH) /
5628226594fSRick McNeal 	    sizeof (struct pqi_sg_entry)) +
5638226594fSRick McNeal 	    PQI_MAX_EMBEDDED_SG_DESCRIPTORS;
5648226594fSRick McNeal 	return (B_TRUE);
5658226594fSRick McNeal }
5668226594fSRick McNeal 
5678226594fSRick McNeal static boolean_t
pqi_alloc_io_resource(pqi_state_t * s)5688226594fSRick McNeal pqi_alloc_io_resource(pqi_state_t *s)
5698226594fSRick McNeal {
5708226594fSRick McNeal 	pqi_io_request_t	*io;
5718226594fSRick McNeal 	size_t			sg_chain_len;
5728226594fSRick McNeal 	int			i;
5738226594fSRick McNeal 
5748226594fSRick McNeal 	s->s_io_rqst_pool = kmem_zalloc(s->s_max_io_slots * sizeof (*io),
5758226594fSRick McNeal 	    KM_SLEEP);
5768226594fSRick McNeal 
5778226594fSRick McNeal 	sg_chain_len = s->s_sg_chain_buf_length;
5788226594fSRick McNeal 	io = s->s_io_rqst_pool;
5798226594fSRick McNeal 	for (i = 0; i < s->s_max_io_slots; i++) {
5808226594fSRick McNeal 		io->io_iu = kmem_zalloc(s->s_max_inbound_iu_length, KM_SLEEP);
5818226594fSRick McNeal 
5828226594fSRick McNeal 		/*
5838226594fSRick McNeal 		 * TODO: Don't allocate dma space here. Move this to
5848226594fSRick McNeal 		 * init_pkt when it's clear the data being transferred
5858226594fSRick McNeal 		 * will not fit in the four SG slots provided by each
5868226594fSRick McNeal 		 * command.
5878226594fSRick McNeal 		 */
5888226594fSRick McNeal 		io->io_sg_chain_dma = pqi_alloc_single(s, sg_chain_len);
5898226594fSRick McNeal 		if (io->io_sg_chain_dma == NULL)
5908226594fSRick McNeal 			goto error_out;
5918226594fSRick McNeal 
5928226594fSRick McNeal 		mutex_init(&io->io_lock, NULL, MUTEX_DRIVER, NULL);
5938226594fSRick McNeal 		io->io_gen = 1;
5948226594fSRick McNeal 		list_link_init(&io->io_list_node);
5958226594fSRick McNeal 		io->io_index = (uint16_t)i;
5968226594fSRick McNeal 
5978226594fSRick McNeal 		io->io_softc = s;
5988226594fSRick McNeal 		io++;
5998226594fSRick McNeal 	}
6008226594fSRick McNeal 
6018226594fSRick McNeal 	return (B_TRUE);
6028226594fSRick McNeal 
6038226594fSRick McNeal error_out:
6048226594fSRick McNeal 	for (i = 0; i < s->s_max_io_slots; i++) {
6058226594fSRick McNeal 		if (io->io_iu != NULL) {
6068226594fSRick McNeal 			kmem_free(io->io_iu, s->s_max_inbound_iu_length);
6078226594fSRick McNeal 			io->io_iu = NULL;
6088226594fSRick McNeal 		}
6098226594fSRick McNeal 		if (io->io_sg_chain_dma != NULL) {
6108226594fSRick McNeal 			pqi_free_single(s, io->io_sg_chain_dma);
6118226594fSRick McNeal 			io->io_sg_chain_dma = NULL;
6128226594fSRick McNeal 		}
6138226594fSRick McNeal 	}
6148226594fSRick McNeal 	kmem_free(s->s_io_rqst_pool, s->s_max_io_slots * sizeof (*io));
6158226594fSRick McNeal 	s->s_io_rqst_pool = NULL;
6168226594fSRick McNeal 
6178226594fSRick McNeal 	return (B_FALSE);
6188226594fSRick McNeal }
6198226594fSRick McNeal 
6208226594fSRick McNeal static boolean_t
pqi_alloc_operation_queues(pqi_state_t * s)6218226594fSRick McNeal pqi_alloc_operation_queues(pqi_state_t *s)
6228226594fSRick McNeal {
6238226594fSRick McNeal 	uint32_t	niq = s->s_num_queue_groups * 2;
6248226594fSRick McNeal 	uint32_t	noq = s->s_num_queue_groups;
6258226594fSRick McNeal 	uint32_t	queue_idx = (s->s_num_queue_groups * 3) + 1;
6268226594fSRick McNeal 	uint32_t	i;
6278226594fSRick McNeal 	size_t		array_len_iq;
6288226594fSRick McNeal 	size_t		array_len_oq;
6298226594fSRick McNeal 	size_t		alloc_len;
6308226594fSRick McNeal 	caddr_t		aligned_pointer = NULL;
6318226594fSRick McNeal 	pqi_queue_group_t	*qg;
6328226594fSRick McNeal 
6338226594fSRick McNeal 	array_len_iq = PQI_OPERATIONAL_IQ_ELEMENT_LENGTH *
6348226594fSRick McNeal 	    s->s_num_elements_per_iq;
6358226594fSRick McNeal 	array_len_oq = PQI_OPERATIONAL_OQ_ELEMENT_LENGTH *
6368226594fSRick McNeal 	    s->s_num_elements_per_oq;
6378226594fSRick McNeal 
6388226594fSRick McNeal 	for (i = 0; i < niq; i++) {
6398226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6408226594fSRick McNeal 		    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6418226594fSRick McNeal 		aligned_pointer += array_len_iq;
6428226594fSRick McNeal 	}
6438226594fSRick McNeal 
6448226594fSRick McNeal 	for (i = 0; i < noq; i++) {
6458226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6468226594fSRick McNeal 		    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6478226594fSRick McNeal 		aligned_pointer += array_len_oq;
6488226594fSRick McNeal 	}
6498226594fSRick McNeal 
6508226594fSRick McNeal 	aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6518226594fSRick McNeal 	    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6528226594fSRick McNeal 	aligned_pointer += PQI_NUM_EVENT_QUEUE_ELEMENTS *
6538226594fSRick McNeal 	    PQI_EVENT_OQ_ELEMENT_LENGTH;
6548226594fSRick McNeal 
6558226594fSRick McNeal 	for (i = 0; i < queue_idx; i++) {
6568226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6578226594fSRick McNeal 		    PQI_OPERATIONAL_INDEX_ALIGNMENT, caddr_t);
6588226594fSRick McNeal 		aligned_pointer += sizeof (pqi_index_t);
6598226594fSRick McNeal 	}
6608226594fSRick McNeal 
6618226594fSRick McNeal 	alloc_len = (size_t)aligned_pointer +
6628226594fSRick McNeal 	    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT + PQI_EXTRA_SGL_MEMORY;
6638226594fSRick McNeal 	if ((s->s_queue_dma = pqi_alloc_single(s, alloc_len)) == NULL)
6648226594fSRick McNeal 		return (B_FALSE);
6658226594fSRick McNeal 
6668226594fSRick McNeal 	aligned_pointer = PQIALIGN_TYPED(s->s_queue_dma->alloc_memory,
6678226594fSRick McNeal 	    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6688226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
6698226594fSRick McNeal 		qg = &s->s_queue_groups[i];
6708226594fSRick McNeal 
6718226594fSRick McNeal 		qg->iq_pi_copy[0] = 0;
6728226594fSRick McNeal 		qg->iq_pi_copy[1] = 0;
6738226594fSRick McNeal 		qg->oq_ci_copy = 0;
6748226594fSRick McNeal 		qg->iq_element_array[RAID_PATH] = aligned_pointer;
6758226594fSRick McNeal 		qg->iq_element_array_bus_addr[RAID_PATH] =
6768226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
6778226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
6788226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
6798226594fSRick McNeal 
6808226594fSRick McNeal 		aligned_pointer += array_len_iq;
6818226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6828226594fSRick McNeal 		    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6838226594fSRick McNeal 
6848226594fSRick McNeal 		qg->iq_element_array[AIO_PATH] = aligned_pointer;
6858226594fSRick McNeal 		qg->iq_element_array_bus_addr[AIO_PATH] =
6868226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
6878226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
6888226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
6898226594fSRick McNeal 
6908226594fSRick McNeal 		aligned_pointer += array_len_iq;
6918226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
6928226594fSRick McNeal 		    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
6938226594fSRick McNeal 	}
6948226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
6958226594fSRick McNeal 		qg = &s->s_queue_groups[i];
6968226594fSRick McNeal 
6978226594fSRick McNeal 		qg->oq_element_array = aligned_pointer;
6988226594fSRick McNeal 		qg->oq_element_array_bus_addr =
6998226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
7008226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
7018226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
7028226594fSRick McNeal 
7038226594fSRick McNeal 		aligned_pointer += array_len_oq;
7048226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
7058226594fSRick McNeal 		    PQI_QUEUE_ELEMENT_ARRAY_ALIGNMENT, caddr_t);
7068226594fSRick McNeal 	}
7078226594fSRick McNeal 
7088226594fSRick McNeal 	s->s_event_queue.oq_element_array = aligned_pointer;
7098226594fSRick McNeal 	s->s_event_queue.oq_element_array_bus_addr =
7108226594fSRick McNeal 	    s->s_queue_dma->dma_addr +
7118226594fSRick McNeal 	    ((uintptr_t)aligned_pointer -
7128226594fSRick McNeal 	    (uintptr_t)s->s_queue_dma->alloc_memory);
7138226594fSRick McNeal 	aligned_pointer += PQI_NUM_EVENT_QUEUE_ELEMENTS *
7148226594fSRick McNeal 	    PQI_EVENT_OQ_ELEMENT_LENGTH;
7158226594fSRick McNeal 
7168226594fSRick McNeal 	aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
7178226594fSRick McNeal 	    PQI_OPERATIONAL_INDEX_ALIGNMENT, caddr_t);
7188226594fSRick McNeal 
7198226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
7208226594fSRick McNeal 		qg = &s->s_queue_groups[i];
7218226594fSRick McNeal 
7228226594fSRick McNeal 		/* LINTED E_BAD_PTR_CAST_ALIGN */
7238226594fSRick McNeal 		qg->iq_ci[RAID_PATH] = (pqi_index_t *)aligned_pointer;
7248226594fSRick McNeal 		qg->iq_ci_bus_addr[RAID_PATH] =
7258226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
7268226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
7278226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
7288226594fSRick McNeal 
7298226594fSRick McNeal 		aligned_pointer += sizeof (pqi_index_t);
7308226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
7318226594fSRick McNeal 		    PQI_OPERATIONAL_INDEX_ALIGNMENT, caddr_t);
7328226594fSRick McNeal 
7338226594fSRick McNeal 		/* LINTED E_BAD_PTR_CAST_ALIGN */
7348226594fSRick McNeal 		qg->iq_ci[AIO_PATH] = (pqi_index_t *)aligned_pointer;
7358226594fSRick McNeal 		qg->iq_ci_bus_addr[AIO_PATH] =
7368226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
7378226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
7388226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
7398226594fSRick McNeal 
7408226594fSRick McNeal 		aligned_pointer += sizeof (pqi_index_t);
7418226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
7428226594fSRick McNeal 		    PQI_OPERATIONAL_INDEX_ALIGNMENT, caddr_t);
7438226594fSRick McNeal 
7448226594fSRick McNeal 		/* LINTED E_BAD_PTR_CAST_ALIGN */
7458226594fSRick McNeal 		qg->oq_pi = (pqi_index_t *)aligned_pointer;
7468226594fSRick McNeal 		qg->oq_pi_bus_addr =
7478226594fSRick McNeal 		    s->s_queue_dma->dma_addr +
7488226594fSRick McNeal 		    ((uintptr_t)aligned_pointer -
7498226594fSRick McNeal 		    (uintptr_t)s->s_queue_dma->alloc_memory);
7508226594fSRick McNeal 
7518226594fSRick McNeal 		aligned_pointer += sizeof (pqi_index_t);
7528226594fSRick McNeal 		aligned_pointer = PQIALIGN_TYPED(aligned_pointer,
7538226594fSRick McNeal 		    PQI_OPERATIONAL_INDEX_ALIGNMENT, caddr_t);
7548226594fSRick McNeal 	}
7558226594fSRick McNeal 
7568226594fSRick McNeal 	/* LINTED E_BAD_PTR_CAST_ALIGN */
7578226594fSRick McNeal 	s->s_event_queue.oq_pi = (pqi_index_t *)aligned_pointer;
7588226594fSRick McNeal 	s->s_event_queue.oq_pi_bus_addr =
7598226594fSRick McNeal 	    s->s_queue_dma->dma_addr +
7608226594fSRick McNeal 	    ((uintptr_t)aligned_pointer -
7618226594fSRick McNeal 	    (uintptr_t)s->s_queue_dma->alloc_memory);
7628226594fSRick McNeal 	ASSERT((uintptr_t)aligned_pointer -
7638226594fSRick McNeal 	    (uintptr_t)s->s_queue_dma->alloc_memory +
7648226594fSRick McNeal 	    sizeof (pqi_index_t) <= s->s_queue_dma->len_to_alloc);
7658226594fSRick McNeal 
7668226594fSRick McNeal 	return (B_TRUE);
7678226594fSRick McNeal }
7688226594fSRick McNeal 
7698226594fSRick McNeal static boolean_t
pqi_init_operational_queues(pqi_state_t * s)7708226594fSRick McNeal pqi_init_operational_queues(pqi_state_t *s)
7718226594fSRick McNeal {
7728226594fSRick McNeal 	int		i;
7738226594fSRick McNeal 	uint16_t	iq_id = PQI_MIN_OPERATIONAL_QUEUE_ID;
7748226594fSRick McNeal 	uint16_t	oq_id = PQI_MIN_OPERATIONAL_QUEUE_ID;
7758226594fSRick McNeal 
7768226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
7778226594fSRick McNeal 		s->s_queue_groups[i].qg_softc = s;
7788226594fSRick McNeal 	}
7798226594fSRick McNeal 	s->s_event_queue.oq_id = oq_id++;
7808226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
7818226594fSRick McNeal 		s->s_queue_groups[i].iq_id[RAID_PATH] = iq_id++;
7828226594fSRick McNeal 		s->s_queue_groups[i].iq_id[AIO_PATH] = iq_id++;
7838226594fSRick McNeal 		s->s_queue_groups[i].oq_id = oq_id++;
7848226594fSRick McNeal 		s->s_queue_groups[i].qg_active = B_TRUE;
7858226594fSRick McNeal 	}
7868226594fSRick McNeal 	s->s_event_queue.int_msg_num = 0;
7878226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++)
7888226594fSRick McNeal 		s->s_queue_groups[i].int_msg_num = (uint16_t)i;
7898226594fSRick McNeal 
7908226594fSRick McNeal 	return (B_TRUE);
7918226594fSRick McNeal }
7928226594fSRick McNeal 
7938226594fSRick McNeal static boolean_t
pqi_init_operational_locks(pqi_state_t * s)7948226594fSRick McNeal pqi_init_operational_locks(pqi_state_t *s)
7958226594fSRick McNeal {
7968226594fSRick McNeal 	int	i;
7978226594fSRick McNeal 
7988226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
7998226594fSRick McNeal 		mutex_init(&s->s_queue_groups[i].submit_lock[0], NULL,
8008226594fSRick McNeal 		    MUTEX_DRIVER, NULL);
8018226594fSRick McNeal 		mutex_init(&s->s_queue_groups[i].submit_lock[1], NULL,
8028226594fSRick McNeal 		    MUTEX_DRIVER, NULL);
8038226594fSRick McNeal 		list_create(&s->s_queue_groups[i].request_list[RAID_PATH],
8048226594fSRick McNeal 		    sizeof (pqi_io_request_t),
8058226594fSRick McNeal 		    offsetof(struct pqi_io_request, io_list_node));
8068226594fSRick McNeal 		list_create(&s->s_queue_groups[i].request_list[AIO_PATH],
8078226594fSRick McNeal 		    sizeof (pqi_io_request_t),
8088226594fSRick McNeal 		    offsetof(struct pqi_io_request, io_list_node));
8098226594fSRick McNeal 	}
8108226594fSRick McNeal 	return (B_TRUE);
8118226594fSRick McNeal }
8128226594fSRick McNeal 
8138226594fSRick McNeal static boolean_t
pqi_create_queues(pqi_state_t * s)8148226594fSRick McNeal pqi_create_queues(pqi_state_t *s)
8158226594fSRick McNeal {
8168226594fSRick McNeal 	int	i;
8178226594fSRick McNeal 
8188226594fSRick McNeal 	if (create_event_queue(s) == B_FALSE)
8198226594fSRick McNeal 		return (B_FALSE);
8208226594fSRick McNeal 
8218226594fSRick McNeal 	for (i = 0; i < s->s_num_queue_groups; i++) {
8228226594fSRick McNeal 		if (create_queue_group(s, i) == B_FALSE) {
8238226594fSRick McNeal 			return (B_FALSE);
8248226594fSRick McNeal 		}
8258226594fSRick McNeal 	}
8268226594fSRick McNeal 
8278226594fSRick McNeal 	return (B_TRUE);
8288226594fSRick McNeal }
8298226594fSRick McNeal 
8308226594fSRick McNeal static boolean_t
pqi_change_irq_mode(pqi_state_t * s)8318226594fSRick McNeal pqi_change_irq_mode(pqi_state_t *s)
8328226594fSRick McNeal {
8338226594fSRick McNeal 	/* ---- Device already is in MSIX mode ---- */
8348226594fSRick McNeal 	s->s_intr_ready = B_TRUE;
8358226594fSRick McNeal 	return (B_TRUE);
8368226594fSRick McNeal }
8378226594fSRick McNeal 
8388226594fSRick McNeal static boolean_t
pqi_start_heartbeat_timer(pqi_state_t * s)8398226594fSRick McNeal pqi_start_heartbeat_timer(pqi_state_t *s)
8408226594fSRick McNeal {
8418226594fSRick McNeal 	s->s_last_heartbeat_count = 0;
8428226594fSRick McNeal 	s->s_last_intr_count = 0;
8438226594fSRick McNeal 
8448226594fSRick McNeal 	s->s_watchdog = timeout(pqi_watchdog, s, drv_usectohz(WATCHDOG));
8458226594fSRick McNeal 	return (B_TRUE);
8468226594fSRick McNeal }
8478226594fSRick McNeal 
8488226594fSRick McNeal #define	PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH \
8498226594fSRick McNeal 	(offsetof(struct pqi_event_config, descriptors) + \
8508226594fSRick McNeal 	(PQI_MAX_EVENT_DESCRIPTORS * sizeof (pqi_event_descriptor_t)))
8518226594fSRick McNeal 
8528226594fSRick McNeal static boolean_t
pqi_enable_events(pqi_state_t * s)8538226594fSRick McNeal pqi_enable_events(pqi_state_t *s)
8548226594fSRick McNeal {
8558226594fSRick McNeal 	int			i;
8568226594fSRick McNeal 	pqi_event_config_t	*ec;
8578226594fSRick McNeal 	pqi_event_descriptor_t	*desc;
8588226594fSRick McNeal 	pqi_general_mgmt_rqst_t	rqst;
8598226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
8608226594fSRick McNeal 	pqi_sg_entry_t		*sg;
8618226594fSRick McNeal 	boolean_t		rval = B_FALSE;
8628226594fSRick McNeal 
8638226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
8648226594fSRick McNeal 	dma = pqi_alloc_single(s, PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH);
8658226594fSRick McNeal 	if (dma == NULL)
8668226594fSRick McNeal 		return (B_FALSE);
8678226594fSRick McNeal 
8688226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_REPORT_VENDOR_EVENT_CONFIG;
8698226594fSRick McNeal 	rqst.header.iu_length = offsetof(struct pqi_general_management_request,
8708226594fSRick McNeal 	    data.report_event_configuration.sg_descriptors[1]) -
8718226594fSRick McNeal 	    PQI_REQUEST_HEADER_LENGTH;
8728226594fSRick McNeal 	rqst.data.report_event_configuration.buffer_length =
8738226594fSRick McNeal 	    PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH;
8748226594fSRick McNeal 	sg = &rqst.data.report_event_configuration.sg_descriptors[0];
8758226594fSRick McNeal 	sg->sg_addr = dma->dma_addr;
8768226594fSRick McNeal 	sg->sg_len = dma->len_to_alloc;
8778226594fSRick McNeal 	sg->sg_flags = CISS_SG_LAST;
8788226594fSRick McNeal 
8798226594fSRick McNeal 	if (submit_raid_rqst_sync(s, &rqst.header, NULL) == B_FALSE)
8808226594fSRick McNeal 		goto error_out;
8818226594fSRick McNeal 
8828226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
8838226594fSRick McNeal 	ec = (pqi_event_config_t *)dma->alloc_memory;
8848226594fSRick McNeal 	for (i = 0; i < ec->num_event_descriptors; i++) {
8858226594fSRick McNeal 		desc = &ec->descriptors[i];
8868226594fSRick McNeal 		if (pqi_supported_event(desc->event_type) == B_TRUE)
8878226594fSRick McNeal 			desc->oq_id = s->s_event_queue.oq_id;
8888226594fSRick McNeal 		else
8898226594fSRick McNeal 			desc->oq_id = 0;
8908226594fSRick McNeal 	}
8918226594fSRick McNeal 
8928226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_SET_VENDOR_EVENT_CONFIG;
8938226594fSRick McNeal 	rqst.header.iu_length = offsetof(struct pqi_general_management_request,
8948226594fSRick McNeal 	    data.report_event_configuration.sg_descriptors[1]) -
8958226594fSRick McNeal 	    PQI_REQUEST_HEADER_LENGTH;
8968226594fSRick McNeal 	rqst.data.report_event_configuration.buffer_length =
8978226594fSRick McNeal 	    PQI_REPORT_EVENT_CONFIG_BUFFER_LENGTH;
8988226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORDEV);
8998226594fSRick McNeal 
9008226594fSRick McNeal 	rval = submit_raid_rqst_sync(s, &rqst.header, NULL);
9018226594fSRick McNeal 
9028226594fSRick McNeal error_out:
9038226594fSRick McNeal 	pqi_free_single(s, dma);
9048226594fSRick McNeal 	return (rval);
9058226594fSRick McNeal }
9068226594fSRick McNeal 
9078226594fSRick McNeal /*
9088226594fSRick McNeal  * pqi_get_hba_version -- find HBA's version number
9098226594fSRick McNeal  */
9108226594fSRick McNeal static boolean_t
pqi_get_hba_version(pqi_state_t * s)9118226594fSRick McNeal pqi_get_hba_version(pqi_state_t *s)
9128226594fSRick McNeal {
9138226594fSRick McNeal 	bmic_identify_controller_t	*ident;
9148226594fSRick McNeal 	boolean_t			rval = B_FALSE;
9158226594fSRick McNeal 
9168226594fSRick McNeal 	ident = kmem_zalloc(sizeof (*ident), KM_SLEEP);
9178226594fSRick McNeal 	if (identify_controller(s, ident) == B_FALSE)
9188226594fSRick McNeal 		goto out;
9198226594fSRick McNeal 	(void) memcpy(s->s_firmware_version, ident->firmware_version,
9208226594fSRick McNeal 	    sizeof (ident->firmware_version));
9218226594fSRick McNeal 	s->s_firmware_version[sizeof (ident->firmware_version)] = '\0';
9228226594fSRick McNeal 	(void) snprintf(s->s_firmware_version + strlen(s->s_firmware_version),
9238226594fSRick McNeal 	    sizeof (s->s_firmware_version) - strlen(s->s_firmware_version),
9248226594fSRick McNeal 	    "-%u", ident->firmware_build_number);
9258226594fSRick McNeal 	rval = B_TRUE;
9268226594fSRick McNeal 	cmn_err(CE_NOTE, "!smartpqi%d - firmware version: %s",
9278226594fSRick McNeal 	    s->s_instance, s->s_firmware_version);
9288226594fSRick McNeal out:
9298226594fSRick McNeal 	kmem_free(ident, sizeof (*ident));
9308226594fSRick McNeal 	return (rval);
9318226594fSRick McNeal }
9328226594fSRick McNeal 
9338226594fSRick McNeal /*
9348226594fSRick McNeal  * pqi_version_to_hba -- send driver version to HBA
9358226594fSRick McNeal  */
9368226594fSRick McNeal static boolean_t
pqi_version_to_hba(pqi_state_t * s)9378226594fSRick McNeal pqi_version_to_hba(pqi_state_t *s)
9388226594fSRick McNeal {
9398226594fSRick McNeal 	bmic_host_wellness_driver_version_t	*b;
9408226594fSRick McNeal 	boolean_t				rval = B_FALSE;
9418226594fSRick McNeal 
9428226594fSRick McNeal 	b = kmem_zalloc(sizeof (*b), KM_SLEEP);
9438226594fSRick McNeal 	b->start_tag[0] = '<';
9448226594fSRick McNeal 	b->start_tag[1] = 'H';
9458226594fSRick McNeal 	b->start_tag[2] = 'W';
9468226594fSRick McNeal 	b->start_tag[3] = '>';
9478226594fSRick McNeal 	b->drv_tag[0] = 'D';
9488226594fSRick McNeal 	b->drv_tag[1] = 'V';
9498226594fSRick McNeal 	b->driver_version_length = sizeof (b->driver_version);
9508226594fSRick McNeal 	(void) snprintf(b->driver_version, sizeof (b->driver_version),
9518226594fSRick McNeal 	    "Illumos 1.0");
9528226594fSRick McNeal 	b->end_tag[0] = 'Z';
9538226594fSRick McNeal 	b->end_tag[1] = 'Z';
9548226594fSRick McNeal 
9558226594fSRick McNeal 	rval = write_host_wellness(s, b, sizeof (*b));
9568226594fSRick McNeal 	kmem_free(b, sizeof (*b));
9578226594fSRick McNeal 
9588226594fSRick McNeal 	return (rval);
9598226594fSRick McNeal }
9608226594fSRick McNeal 
9618226594fSRick McNeal 
9628226594fSRick McNeal static boolean_t
pqi_schedule_update_time_worker(pqi_state_t * s)9638226594fSRick McNeal pqi_schedule_update_time_worker(pqi_state_t *s)
9648226594fSRick McNeal {
9658226594fSRick McNeal 	update_time(s);
9668226594fSRick McNeal 	return (B_TRUE);
9678226594fSRick McNeal }
9688226594fSRick McNeal 
9698226594fSRick McNeal static boolean_t
pqi_scan_scsi_devices(pqi_state_t * s)9708226594fSRick McNeal pqi_scan_scsi_devices(pqi_state_t *s)
9718226594fSRick McNeal {
9728226594fSRick McNeal 	report_phys_lun_extended_t	*phys_list	= NULL;
9738226594fSRick McNeal 	report_log_lun_extended_t	*logical_list	= NULL;
974*4091c922SYuri Pankov 	size_t plen;
975*4091c922SYuri Pankov 	size_t llen;
9768226594fSRick McNeal 	boolean_t			rval		= B_FALSE;
9778226594fSRick McNeal 	int				num_phys	= 0;
9788226594fSRick McNeal 	int				num_logical	= 0;
9798226594fSRick McNeal 	int				i;
9808226594fSRick McNeal 	pqi_device_t			*dev;
9818226594fSRick McNeal 
982*4091c922SYuri Pankov 	if (get_device_list(s, &phys_list, &plen,
983*4091c922SYuri Pankov 	    &logical_list, &llen) == B_FALSE)
9848226594fSRick McNeal 		goto error_out;
9858226594fSRick McNeal 
9868226594fSRick McNeal 	if (phys_list) {
9878226594fSRick McNeal 		num_phys = ntohl(phys_list->header.list_length) /
9888226594fSRick McNeal 		    sizeof (phys_list->lun_entries[0]);
9898226594fSRick McNeal 	}
9908226594fSRick McNeal 
9918226594fSRick McNeal 	if (logical_list) {
9928226594fSRick McNeal 		num_logical = ntohl(logical_list->header.list_length) /
9938226594fSRick McNeal 		    sizeof (logical_list->lun_entries[0]);
9948226594fSRick McNeal 	}
9958226594fSRick McNeal 
9968226594fSRick McNeal 	/*
9978226594fSRick McNeal 	 * Need to look for devices that are no longer available. The call
9988226594fSRick McNeal 	 * below to is_new_dev() will mark either the new device just created
9998226594fSRick McNeal 	 * as having been scanned or if is_new_dev() finds an existing
10008226594fSRick McNeal 	 * device in the list that one will be marked as scanned.
10018226594fSRick McNeal 	 */
10028226594fSRick McNeal 	mutex_enter(&s->s_mutex);
10038226594fSRick McNeal 	for (dev = list_head(&s->s_devnodes); dev != NULL;
10048226594fSRick McNeal 	    dev = list_next(&s->s_devnodes, dev)) {
10058226594fSRick McNeal 		dev->pd_scanned = 0;
10068226594fSRick McNeal 	}
10078226594fSRick McNeal 	mutex_exit(&s->s_mutex);
10088226594fSRick McNeal 
10098226594fSRick McNeal 	for (i = 0; i < (num_phys + num_logical); i++) {
10108226594fSRick McNeal 		if (i < num_phys) {
10118226594fSRick McNeal 			dev = create_phys_dev(s, &phys_list->lun_entries[i]);
10128226594fSRick McNeal 		} else {
10138226594fSRick McNeal 			dev = create_logical_dev(s,
10148226594fSRick McNeal 			    &logical_list->lun_entries[i - num_phys]);
10158226594fSRick McNeal 		}
10168226594fSRick McNeal 		if (dev != NULL) {
10178226594fSRick McNeal 			if (is_new_dev(s, dev) == B_TRUE) {
10188226594fSRick McNeal 				list_create(&dev->pd_cmd_list,
10198226594fSRick McNeal 				    sizeof (struct pqi_cmd),
10208226594fSRick McNeal 				    offsetof(struct pqi_cmd, pc_list));
10218226594fSRick McNeal 				mutex_init(&dev->pd_mutex, NULL, MUTEX_DRIVER,
10228226594fSRick McNeal 				    NULL);
10238226594fSRick McNeal 
10248226594fSRick McNeal 				mutex_enter(&s->s_mutex);
10258226594fSRick McNeal 				list_insert_tail(&s->s_devnodes, dev);
10268226594fSRick McNeal 				mutex_exit(&s->s_mutex);
10278226594fSRick McNeal 			} else {
10288226594fSRick McNeal 				ddi_devid_free_guid(dev->pd_guid);
10298226594fSRick McNeal 				kmem_free(dev, sizeof (*dev));
10308226594fSRick McNeal 			}
10318226594fSRick McNeal 		}
10328226594fSRick McNeal 	}
10338226594fSRick McNeal 
10348226594fSRick McNeal 	/*
10358226594fSRick McNeal 	 * Now look through the list for devices which have disappeared.
10368226594fSRick McNeal 	 * Mark them as being offline. During the call to config_one, which
10378226594fSRick McNeal 	 * will come next during a hotplug event, those devices will be
10388226594fSRick McNeal 	 * offlined to the SCSI subsystem.
10398226594fSRick McNeal 	 */
10408226594fSRick McNeal 	mutex_enter(&s->s_mutex);
10418226594fSRick McNeal 	for (dev = list_head(&s->s_devnodes); dev != NULL;
10428226594fSRick McNeal 	    dev = list_next(&s->s_devnodes, dev)) {
10438226594fSRick McNeal 		if (dev->pd_scanned)
10448226594fSRick McNeal 			dev->pd_online = 1;
10458226594fSRick McNeal 		else
10468226594fSRick McNeal 			dev->pd_online = 0;
10478226594fSRick McNeal 	}
10488226594fSRick McNeal 
10498226594fSRick McNeal 	mutex_exit(&s->s_mutex);
10508226594fSRick McNeal 
10518226594fSRick McNeal 	rval = B_TRUE;
10528226594fSRick McNeal 
10538226594fSRick McNeal error_out:
10548226594fSRick McNeal 	if (phys_list != NULL)
1055*4091c922SYuri Pankov 		kmem_free(phys_list, plen);
10568226594fSRick McNeal 	if (logical_list != NULL)
1057*4091c922SYuri Pankov 		kmem_free(logical_list, llen);
10588226594fSRick McNeal 	return (rval);
10598226594fSRick McNeal }
10608226594fSRick McNeal 
10618226594fSRick McNeal /*
10628226594fSRick McNeal  * []----------------------------------------------------------[]
10638226594fSRick McNeal  * | Entry points used by other funtions found in other files	|
10648226594fSRick McNeal  * []----------------------------------------------------------[]
10658226594fSRick McNeal  */
10668226594fSRick McNeal void
pqi_rescan_devices(pqi_state_t * s)10678226594fSRick McNeal pqi_rescan_devices(pqi_state_t *s)
10688226594fSRick McNeal {
10698226594fSRick McNeal 	(void) pqi_scan_scsi_devices(s);
10708226594fSRick McNeal }
10718226594fSRick McNeal 
10728226594fSRick McNeal boolean_t
pqi_scsi_inquiry(pqi_state_t * s,pqi_device_t * dev,int vpd,struct scsi_inquiry * inq,int len)10738226594fSRick McNeal pqi_scsi_inquiry(pqi_state_t *s, pqi_device_t *dev, int vpd,
10748226594fSRick McNeal     struct scsi_inquiry *inq, int len)
10758226594fSRick McNeal {
10768226594fSRick McNeal 	pqi_raid_path_request_t rqst;
10778226594fSRick McNeal 
10788226594fSRick McNeal 	if (build_raid_path_request(&rqst, SCMD_INQUIRY,
10798226594fSRick McNeal 	    dev->pd_scsi3addr, len, vpd) == B_FALSE)
10808226594fSRick McNeal 		return (B_FALSE);
10818226594fSRick McNeal 
10828226594fSRick McNeal 	return (scsi_common(s, &rqst, (caddr_t)inq, len));
10838226594fSRick McNeal }
10848226594fSRick McNeal 
10858226594fSRick McNeal void
pqi_free_io_resource(pqi_state_t * s)10868226594fSRick McNeal pqi_free_io_resource(pqi_state_t *s)
10878226594fSRick McNeal {
10888226594fSRick McNeal 	pqi_io_request_t	*io = s->s_io_rqst_pool;
10898226594fSRick McNeal 	int			i;
10908226594fSRick McNeal 
10918226594fSRick McNeal 	if (io == NULL)
10928226594fSRick McNeal 		return;
10938226594fSRick McNeal 
10948226594fSRick McNeal 	for (i = 0; i < s->s_max_io_slots; i++) {
10958226594fSRick McNeal 		if (io->io_iu == NULL)
10968226594fSRick McNeal 			break;
10978226594fSRick McNeal 		kmem_free(io->io_iu, s->s_max_inbound_iu_length);
10988226594fSRick McNeal 		io->io_iu = NULL;
10998226594fSRick McNeal 		pqi_free_single(s, io->io_sg_chain_dma);
11008226594fSRick McNeal 		io->io_sg_chain_dma = NULL;
11018226594fSRick McNeal 	}
11028226594fSRick McNeal 
11038226594fSRick McNeal 	kmem_free(s->s_io_rqst_pool, s->s_max_io_slots * sizeof (*io));
11048226594fSRick McNeal 	s->s_io_rqst_pool = NULL;
11058226594fSRick McNeal }
11068226594fSRick McNeal 
11078226594fSRick McNeal /*
11088226594fSRick McNeal  * []----------------------------------------------------------[]
11098226594fSRick McNeal  * | Utility functions for startup code.			|
11108226594fSRick McNeal  * []----------------------------------------------------------[]
11118226594fSRick McNeal  */
11128226594fSRick McNeal 
11138226594fSRick McNeal static boolean_t
scsi_common(pqi_state_t * s,pqi_raid_path_request_t * rqst,caddr_t buf,int len)11148226594fSRick McNeal scsi_common(pqi_state_t *s, pqi_raid_path_request_t *rqst, caddr_t buf, int len)
11158226594fSRick McNeal {
11168226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
11178226594fSRick McNeal 	pqi_sg_entry_t		*sg;
11188226594fSRick McNeal 	boolean_t		rval = B_FALSE;
11198226594fSRick McNeal 
11208226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, len)) == NULL)
11218226594fSRick McNeal 		return (B_FALSE);
11228226594fSRick McNeal 
11238226594fSRick McNeal 	sg = &rqst->rp_sglist[0];
11248226594fSRick McNeal 	sg->sg_addr = dma->dma_addr;
11258226594fSRick McNeal 	sg->sg_len = dma->len_to_alloc;
11268226594fSRick McNeal 	sg->sg_flags = CISS_SG_LAST;
11278226594fSRick McNeal 
11288226594fSRick McNeal 	if (submit_raid_rqst_sync(s, &rqst->header, NULL) == B_FALSE)
11298226594fSRick McNeal 		goto out;
11308226594fSRick McNeal 
11318226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
11328226594fSRick McNeal 	(void) memcpy(buf, dma->alloc_memory, len);
11338226594fSRick McNeal 	rval = B_TRUE;
11348226594fSRick McNeal out:
11358226594fSRick McNeal 	pqi_free_single(s, dma);
11368226594fSRick McNeal 	return (rval);
11378226594fSRick McNeal }
11388226594fSRick McNeal 
11398226594fSRick McNeal static void
bcopy_fromregs(pqi_state_t * s,uint8_t * iomem,uint8_t * dst,uint32_t len)11408226594fSRick McNeal bcopy_fromregs(pqi_state_t *s, uint8_t *iomem, uint8_t *dst, uint32_t len)
11418226594fSRick McNeal {
11428226594fSRick McNeal 	int	i;
11438226594fSRick McNeal 
11448226594fSRick McNeal 	for (i = 0; i < len; i++) {
11458226594fSRick McNeal 		*dst++ = ddi_get8(s->s_datap, iomem + i);
11468226594fSRick McNeal 	}
11478226594fSRick McNeal }
11488226594fSRick McNeal 
11498226594fSRick McNeal static void
submit_admin_request(pqi_state_t * s,pqi_general_admin_request_t * r)11508226594fSRick McNeal submit_admin_request(pqi_state_t *s, pqi_general_admin_request_t *r)
11518226594fSRick McNeal {
11528226594fSRick McNeal 	pqi_admin_queues_t	*aq;
11538226594fSRick McNeal 	pqi_index_t		iq_pi;
11548226594fSRick McNeal 	caddr_t			next_element;
11558226594fSRick McNeal 
11568226594fSRick McNeal 	aq = &s->s_admin_queues;
11578226594fSRick McNeal 	iq_pi = aq->iq_pi_copy;
11588226594fSRick McNeal 	next_element = aq->iq_element_array + (iq_pi *
11598226594fSRick McNeal 	    PQI_ADMIN_IQ_ELEMENT_LENGTH);
11608226594fSRick McNeal 	(void) memcpy(next_element, r, sizeof (*r));
11618226594fSRick McNeal 	(void) ddi_dma_sync(s->s_adminq_dma->handle,
11628226594fSRick McNeal 	    iq_pi * PQI_ADMIN_IQ_ELEMENT_LENGTH, sizeof (*r),
11638226594fSRick McNeal 	    DDI_DMA_SYNC_FORDEV);
11648226594fSRick McNeal 	iq_pi = (iq_pi + 1) % PQI_ADMIN_IQ_NUM_ELEMENTS;
11658226594fSRick McNeal 	aq->iq_pi_copy = iq_pi;
11668226594fSRick McNeal 
11678226594fSRick McNeal 	ddi_put32(s->s_datap, aq->iq_pi, iq_pi);
11688226594fSRick McNeal }
11698226594fSRick McNeal 
11708226594fSRick McNeal static boolean_t
poll_for_admin_response(pqi_state_t * s,pqi_general_admin_response_t * r)11718226594fSRick McNeal poll_for_admin_response(pqi_state_t *s, pqi_general_admin_response_t *r)
11728226594fSRick McNeal {
11738226594fSRick McNeal 	pqi_admin_queues_t	*aq;
11748226594fSRick McNeal 	pqi_index_t		oq_pi;
11758226594fSRick McNeal 	pqi_index_t		oq_ci;
11768226594fSRick McNeal 	int			countdown = 10 * MICROSEC;	/* 10 seconds */
11778226594fSRick McNeal 	int			pause_time = 10 * MILLISEC;	/* 10ms */
11788226594fSRick McNeal 
11798226594fSRick McNeal 	countdown /= pause_time;
11808226594fSRick McNeal 	aq = &s->s_admin_queues;
11818226594fSRick McNeal 	oq_ci = aq->oq_ci_copy;
11828226594fSRick McNeal 
11838226594fSRick McNeal 	while (--countdown) {
11848226594fSRick McNeal 		oq_pi = ddi_get32(s->s_adminq_dma->acc, aq->oq_pi);
11858226594fSRick McNeal 		if (oq_pi != oq_ci)
11868226594fSRick McNeal 			break;
11878226594fSRick McNeal 		drv_usecwait(pause_time);
11888226594fSRick McNeal 	}
11898226594fSRick McNeal 	if (countdown == 0)
11908226594fSRick McNeal 		return (B_FALSE);
11918226594fSRick McNeal 
11928226594fSRick McNeal 	(void) ddi_dma_sync(s->s_adminq_dma->handle,
11938226594fSRick McNeal 	    oq_ci * PQI_ADMIN_OQ_ELEMENT_LENGTH, sizeof (*r),
11948226594fSRick McNeal 	    DDI_DMA_SYNC_FORCPU);
11958226594fSRick McNeal 	(void) memcpy(r, aq->oq_element_array +
11968226594fSRick McNeal 	    (oq_ci * PQI_ADMIN_OQ_ELEMENT_LENGTH), sizeof (*r));
11978226594fSRick McNeal 
11988226594fSRick McNeal 	aq->oq_ci_copy = (oq_ci + 1) % PQI_ADMIN_OQ_NUM_ELEMENTS;
11998226594fSRick McNeal 	ddi_put32(s->s_datap, aq->oq_ci, aq->oq_ci_copy);
12008226594fSRick McNeal 
12018226594fSRick McNeal 	return (B_TRUE);
12028226594fSRick McNeal }
12038226594fSRick McNeal 
12048226594fSRick McNeal static boolean_t
validate_admin_response(pqi_general_admin_response_t * r,uint8_t code)12058226594fSRick McNeal validate_admin_response(pqi_general_admin_response_t *r, uint8_t code)
12068226594fSRick McNeal {
12078226594fSRick McNeal 	if (r->header.iu_type != PQI_RESPONSE_IU_GENERAL_ADMIN)
12088226594fSRick McNeal 		return (B_FALSE);
12098226594fSRick McNeal 
12108226594fSRick McNeal 	if (r->header.iu_length != PQI_GENERAL_ADMIN_IU_LENGTH)
12118226594fSRick McNeal 		return (B_FALSE);
12128226594fSRick McNeal 
12138226594fSRick McNeal 	if (r->function_code != code)
12148226594fSRick McNeal 		return (B_FALSE);
12158226594fSRick McNeal 
12168226594fSRick McNeal 	if (r->status != PQI_GENERAL_ADMIN_STATUS_SUCCESS)
12178226594fSRick McNeal 		return (B_FALSE);
12188226594fSRick McNeal 
12198226594fSRick McNeal 	return (B_TRUE);
12208226594fSRick McNeal }
12218226594fSRick McNeal 
12228226594fSRick McNeal static boolean_t
submit_admin_rqst_sync(pqi_state_t * s,pqi_general_admin_request_t * rqst,pqi_general_admin_response_t * rsp)12238226594fSRick McNeal submit_admin_rqst_sync(pqi_state_t *s,
12248226594fSRick McNeal     pqi_general_admin_request_t *rqst, pqi_general_admin_response_t *rsp)
12258226594fSRick McNeal {
12268226594fSRick McNeal 	boolean_t	rval;
12278226594fSRick McNeal 
12288226594fSRick McNeal 	submit_admin_request(s, rqst);
12298226594fSRick McNeal 	rval = poll_for_admin_response(s, rsp);
12308226594fSRick McNeal 	if (rval == B_TRUE) {
12318226594fSRick McNeal 		rval = validate_admin_response(rsp, rqst->function_code);
12328226594fSRick McNeal 		if (rval == B_FALSE) {
12338226594fSRick McNeal 			pqi_show_dev_state(s);
12348226594fSRick McNeal 		}
12358226594fSRick McNeal 	}
12368226594fSRick McNeal 	return (rval);
12378226594fSRick McNeal }
12388226594fSRick McNeal 
12398226594fSRick McNeal static boolean_t
create_event_queue(pqi_state_t * s)12408226594fSRick McNeal create_event_queue(pqi_state_t *s)
12418226594fSRick McNeal {
12428226594fSRick McNeal 	pqi_event_queue_t		*eq;
12438226594fSRick McNeal 	pqi_general_admin_request_t	request;
12448226594fSRick McNeal 	pqi_general_admin_response_t	response;
12458226594fSRick McNeal 
12468226594fSRick McNeal 	eq = &s->s_event_queue;
12478226594fSRick McNeal 
12488226594fSRick McNeal 	/*
12498226594fSRick McNeal 	 * Create OQ (Outbound Queue - device to host queue) to dedicate
12508226594fSRick McNeal 	 * to events.
12518226594fSRick McNeal 	 */
12528226594fSRick McNeal 	(void) memset(&request, 0, sizeof (request));
12538226594fSRick McNeal 	request.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
12548226594fSRick McNeal 	request.header.iu_length = PQI_GENERAL_ADMIN_IU_LENGTH;
12558226594fSRick McNeal 	request.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ;
12568226594fSRick McNeal 	request.data.create_operational_oq.queue_id = eq->oq_id;
12578226594fSRick McNeal 	request.data.create_operational_oq.element_array_addr =
12588226594fSRick McNeal 	    eq->oq_element_array_bus_addr;
12598226594fSRick McNeal 	request.data.create_operational_oq.pi_addr = eq->oq_pi_bus_addr;
12608226594fSRick McNeal 	request.data.create_operational_oq.num_elements =
12618226594fSRick McNeal 	    PQI_NUM_EVENT_QUEUE_ELEMENTS;
12628226594fSRick McNeal 	request.data.create_operational_oq.element_length =
12638226594fSRick McNeal 	    PQI_EVENT_OQ_ELEMENT_LENGTH / 16;
12648226594fSRick McNeal 	request.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP;
12658226594fSRick McNeal 	request.data.create_operational_oq.int_msg_num = eq->int_msg_num;
12668226594fSRick McNeal 
12678226594fSRick McNeal 	if (submit_admin_rqst_sync(s, &request, &response) == B_FALSE)
12688226594fSRick McNeal 		return (B_FALSE);
12698226594fSRick McNeal 
12708226594fSRick McNeal 	eq->oq_ci = (uint32_t *)(intptr_t)((uint64_t)(intptr_t)s->s_reg +
12718226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
12728226594fSRick McNeal 	    response.data.create_operational_oq.oq_ci_offset);
12738226594fSRick McNeal 
12748226594fSRick McNeal 	return (B_TRUE);
12758226594fSRick McNeal }
12768226594fSRick McNeal 
12778226594fSRick McNeal static boolean_t
create_queue_group(pqi_state_t * s,int idx)12788226594fSRick McNeal create_queue_group(pqi_state_t *s, int idx)
12798226594fSRick McNeal {
12808226594fSRick McNeal 	pqi_queue_group_t		*qg;
12818226594fSRick McNeal 	pqi_general_admin_request_t	rqst;
12828226594fSRick McNeal 	pqi_general_admin_response_t	rsp;
12838226594fSRick McNeal 
12848226594fSRick McNeal 	qg = &s->s_queue_groups[idx];
12858226594fSRick McNeal 
12868226594fSRick McNeal 	/* ---- Create inbound queue for RAID path (host to device) ---- */
12878226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
12888226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
12898226594fSRick McNeal 	rqst.header.iu_length = PQI_GENERAL_ADMIN_IU_LENGTH;
12908226594fSRick McNeal 	rqst.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ;
12918226594fSRick McNeal 	rqst.data.create_operational_iq.queue_id = qg->iq_id[RAID_PATH];
12928226594fSRick McNeal 	rqst.data.create_operational_iq.element_array_addr =
12938226594fSRick McNeal 	    qg->iq_element_array_bus_addr[RAID_PATH];
12948226594fSRick McNeal 	rqst.data.create_operational_iq.ci_addr =
12958226594fSRick McNeal 	    qg->iq_ci_bus_addr[RAID_PATH];
12968226594fSRick McNeal 	rqst.data.create_operational_iq.num_elements =
12978226594fSRick McNeal 	    s->s_num_elements_per_iq;
12988226594fSRick McNeal 	rqst.data.create_operational_iq.element_length =
12998226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16;
13008226594fSRick McNeal 	rqst.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP;
13018226594fSRick McNeal 
13028226594fSRick McNeal 	if (submit_admin_rqst_sync(s, &rqst, &rsp) == B_FALSE)
13038226594fSRick McNeal 		return (B_FALSE);
13048226594fSRick McNeal 	qg->iq_pi[RAID_PATH] =
13058226594fSRick McNeal 	    (uint32_t *)(intptr_t)((uint64_t)(intptr_t)s->s_reg +
13068226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
13078226594fSRick McNeal 	    rsp.data.create_operational_iq.iq_pi_offset);
13088226594fSRick McNeal 
13098226594fSRick McNeal 	/* ---- Create inbound queue for Advanced I/O path. ---- */
13108226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
13118226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
13128226594fSRick McNeal 	rqst.header.iu_length = PQI_GENERAL_ADMIN_IU_LENGTH;
13138226594fSRick McNeal 	rqst.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_IQ;
13148226594fSRick McNeal 	rqst.data.create_operational_iq.queue_id =
13158226594fSRick McNeal 	    qg->iq_id[AIO_PATH];
13168226594fSRick McNeal 	rqst.data.create_operational_iq.element_array_addr =
13178226594fSRick McNeal 	    qg->iq_element_array_bus_addr[AIO_PATH];
13188226594fSRick McNeal 	rqst.data.create_operational_iq.ci_addr =
13198226594fSRick McNeal 	    qg->iq_ci_bus_addr[AIO_PATH];
13208226594fSRick McNeal 	rqst.data.create_operational_iq.num_elements =
13218226594fSRick McNeal 	    s->s_num_elements_per_iq;
13228226594fSRick McNeal 	rqst.data.create_operational_iq.element_length =
13238226594fSRick McNeal 	    PQI_OPERATIONAL_IQ_ELEMENT_LENGTH / 16;
13248226594fSRick McNeal 	rqst.data.create_operational_iq.queue_protocol = PQI_PROTOCOL_SOP;
13258226594fSRick McNeal 
13268226594fSRick McNeal 	if (submit_admin_rqst_sync(s, &rqst, &rsp) == B_FALSE)
13278226594fSRick McNeal 		return (B_FALSE);
13288226594fSRick McNeal 
13298226594fSRick McNeal 	qg->iq_pi[AIO_PATH] =
13308226594fSRick McNeal 	    (uint32_t *)(intptr_t)((uint64_t)(intptr_t)s->s_reg +
13318226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
13328226594fSRick McNeal 	    rsp.data.create_operational_iq.iq_pi_offset);
13338226594fSRick McNeal 
13348226594fSRick McNeal 	/* ---- Change second queue to be AIO ---- */
13358226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
13368226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
13378226594fSRick McNeal 	rqst.header.iu_length =	PQI_GENERAL_ADMIN_IU_LENGTH;
13388226594fSRick McNeal 	rqst.function_code = PQI_GENERAL_ADMIN_FUNCTION_CHANGE_IQ_PROPERTY;
13398226594fSRick McNeal 	rqst.data.change_operational_iq_properties.queue_id =
13408226594fSRick McNeal 	    qg->iq_id[AIO_PATH];
13418226594fSRick McNeal 	rqst.data.change_operational_iq_properties.queue_id =
13428226594fSRick McNeal 	    PQI_IQ_PROPERTY_IS_AIO_QUEUE;
13438226594fSRick McNeal 
13448226594fSRick McNeal 	if (submit_admin_rqst_sync(s, &rqst, &rsp) == B_FALSE)
13458226594fSRick McNeal 		return (B_FALSE);
13468226594fSRick McNeal 
13478226594fSRick McNeal 	/* ---- Create outbound queue (device to host) ---- */
13488226594fSRick McNeal 	(void) memset(&rqst, 0, sizeof (rqst));
13498226594fSRick McNeal 	rqst.header.iu_type = PQI_REQUEST_IU_GENERAL_ADMIN;
13508226594fSRick McNeal 	rqst.header.iu_length = PQI_GENERAL_ADMIN_IU_LENGTH;
13518226594fSRick McNeal 	rqst.function_code = PQI_GENERAL_ADMIN_FUNCTION_CREATE_OQ;
13528226594fSRick McNeal 	rqst.data.create_operational_oq.queue_id = qg->oq_id;
13538226594fSRick McNeal 	rqst.data.create_operational_oq.element_array_addr =
13548226594fSRick McNeal 	    qg->oq_element_array_bus_addr;
13558226594fSRick McNeal 	rqst.data.create_operational_oq.pi_addr = qg->oq_pi_bus_addr;
13568226594fSRick McNeal 	rqst.data.create_operational_oq.num_elements =
13578226594fSRick McNeal 	    s->s_num_elements_per_oq;
13588226594fSRick McNeal 	rqst.data.create_operational_oq.element_length =
13598226594fSRick McNeal 	    PQI_OPERATIONAL_OQ_ELEMENT_LENGTH / 16;
13608226594fSRick McNeal 	rqst.data.create_operational_oq.queue_protocol = PQI_PROTOCOL_SOP;
13618226594fSRick McNeal 	rqst.data.create_operational_oq.int_msg_num = qg->int_msg_num;
13628226594fSRick McNeal 
13638226594fSRick McNeal 	if (submit_admin_rqst_sync(s, &rqst, &rsp) == B_FALSE)
13648226594fSRick McNeal 		return (B_FALSE);
13658226594fSRick McNeal 	qg->oq_ci = (uint32_t *)(intptr_t)((uint64_t)(intptr_t)s->s_reg +
13668226594fSRick McNeal 	    PQI_DEVICE_REGISTERS_OFFSET +
13678226594fSRick McNeal 	    rsp.data.create_operational_oq.oq_ci_offset);
13688226594fSRick McNeal 
13698226594fSRick McNeal 	return (B_TRUE);
13708226594fSRick McNeal }
13718226594fSRick McNeal 
13728226594fSRick McNeal static void
raid_sync_complete(pqi_io_request_t * io __unused,void * ctx)13738226594fSRick McNeal raid_sync_complete(pqi_io_request_t *io __unused, void *ctx)
13748226594fSRick McNeal {
13758226594fSRick McNeal 	ksema_t *s = (ksema_t *)ctx;
13768226594fSRick McNeal 
13778226594fSRick McNeal 	sema_v(s);
13788226594fSRick McNeal }
13798226594fSRick McNeal 
13808226594fSRick McNeal static boolean_t
submit_raid_sync_with_io(pqi_state_t * s,pqi_io_request_t * io)13818226594fSRick McNeal submit_raid_sync_with_io(pqi_state_t *s, pqi_io_request_t *io)
13828226594fSRick McNeal {
13838226594fSRick McNeal 	ksema_t	sema;
13848226594fSRick McNeal 
13858226594fSRick McNeal 	sema_init(&sema, 0, NULL, SEMA_DRIVER, NULL);
13868226594fSRick McNeal 
13878226594fSRick McNeal 	io->io_cb = raid_sync_complete;
13888226594fSRick McNeal 	io->io_context = &sema;
13898226594fSRick McNeal 
13908226594fSRick McNeal 	pqi_start_io(s, &s->s_queue_groups[PQI_DEFAULT_QUEUE_GROUP],
13918226594fSRick McNeal 	    RAID_PATH, io);
13928226594fSRick McNeal 	sema_p(&sema);
13938226594fSRick McNeal 
13948226594fSRick McNeal 	switch (io->io_status) {
13958226594fSRick McNeal 		case PQI_DATA_IN_OUT_GOOD:
13968226594fSRick McNeal 		case PQI_DATA_IN_OUT_UNDERFLOW:
13978226594fSRick McNeal 			return (B_TRUE);
13988226594fSRick McNeal 		default:
13998226594fSRick McNeal 			return (B_FALSE);
14008226594fSRick McNeal 	}
14018226594fSRick McNeal }
14028226594fSRick McNeal 
14038226594fSRick McNeal static boolean_t
submit_raid_rqst_sync(pqi_state_t * s,pqi_iu_header_t * rqst,pqi_raid_error_info_t e_info __unused)14048226594fSRick McNeal submit_raid_rqst_sync(pqi_state_t *s, pqi_iu_header_t *rqst,
14058226594fSRick McNeal     pqi_raid_error_info_t e_info __unused)
14068226594fSRick McNeal {
14078226594fSRick McNeal 	pqi_io_request_t	*io;
14088226594fSRick McNeal 	size_t			len;
14098226594fSRick McNeal 	boolean_t		rval = B_FALSE; // default to error case
14108226594fSRick McNeal 	struct pqi_cmd		*c;
14118226594fSRick McNeal 
14128226594fSRick McNeal 	if ((io = pqi_alloc_io(s)) == NULL)
14138226594fSRick McNeal 		return (B_FALSE);
14148226594fSRick McNeal 
14158226594fSRick McNeal 	c = kmem_zalloc(sizeof (*c), KM_SLEEP);
14168226594fSRick McNeal 
14178226594fSRick McNeal 	mutex_init(&c->pc_mutex, NULL, MUTEX_DRIVER, NULL);
14188226594fSRick McNeal 	c->pc_io_rqst = io;
14198226594fSRick McNeal 	c->pc_device = &s->s_special_device;
14208226594fSRick McNeal 	c->pc_softc = s;
14218226594fSRick McNeal 	io->io_cmd = c;
14228226594fSRick McNeal 	(void) pqi_cmd_action(c, PQI_CMD_QUEUE);
14238226594fSRick McNeal 
14248226594fSRick McNeal 	((pqi_raid_path_request_t *)rqst)->rp_id = PQI_MAKE_REQID(io->io_index,
14258226594fSRick McNeal 	    io->io_gen);
14268226594fSRick McNeal 	if (rqst->iu_type == PQI_REQUEST_IU_RAID_PATH_IO)
14278226594fSRick McNeal 		((pqi_raid_path_request_t *)rqst)->rp_error_index =
14288226594fSRick McNeal 		    io->io_index;
14298226594fSRick McNeal 	len = rqst->iu_length + PQI_REQUEST_HEADER_LENGTH;
14308226594fSRick McNeal 	(void) memcpy(io->io_iu, rqst, len);
14318226594fSRick McNeal 
14328226594fSRick McNeal 	if (submit_raid_sync_with_io(s, io) == B_TRUE)
14338226594fSRick McNeal 		rval = B_TRUE;
14348226594fSRick McNeal 
14358226594fSRick McNeal 	(void) pqi_cmd_action(c, PQI_CMD_CMPLT);
14368226594fSRick McNeal 	mutex_destroy(&c->pc_mutex);
14378226594fSRick McNeal 	kmem_free(c, sizeof (*c));
14388226594fSRick McNeal 
14398226594fSRick McNeal 	return (rval);
14408226594fSRick McNeal }
14418226594fSRick McNeal 
14428226594fSRick McNeal static boolean_t
build_raid_path_request(pqi_raid_path_request_t * rqst,int cmd,caddr_t lun,uint32_t len,int vpd_page)14438226594fSRick McNeal build_raid_path_request(pqi_raid_path_request_t *rqst,
14448226594fSRick McNeal     int cmd, caddr_t lun, uint32_t len, int vpd_page)
14458226594fSRick McNeal {
14468226594fSRick McNeal 	uint8_t		*cdb;
14478226594fSRick McNeal 
14488226594fSRick McNeal 	(void) memset(rqst, 0, sizeof (*rqst));
14498226594fSRick McNeal 	rqst->header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
14508226594fSRick McNeal 	rqst->header.iu_length = offsetof(struct pqi_raid_path_request,
14518226594fSRick McNeal 	    rp_sglist[1]) - PQI_REQUEST_HEADER_LENGTH;
14528226594fSRick McNeal 	rqst->rp_data_len = len;
14538226594fSRick McNeal 	(void) memcpy(rqst->rp_lun, lun, sizeof (rqst->rp_lun));
14548226594fSRick McNeal 	rqst->rp_task_attr = SOP_TASK_ATTRIBUTE_SIMPLE;
14558226594fSRick McNeal 	rqst->rp_additional_cdb = SOP_ADDITIONAL_CDB_BYTES_0;
14568226594fSRick McNeal 
14578226594fSRick McNeal 	cdb = rqst->rp_cdb;
14588226594fSRick McNeal 	switch (cmd) {
14598226594fSRick McNeal 	case SCMD_READ_CAPACITY:
14608226594fSRick McNeal 		rqst->rp_data_dir = (uint8_t)SOP_READ_FLAG;
14618226594fSRick McNeal 		cdb[0] = (uint8_t)cmd;
14628226594fSRick McNeal 		break;
14638226594fSRick McNeal 
14648226594fSRick McNeal 	case SCMD_READ:
14658226594fSRick McNeal 		rqst->rp_data_dir = (uint8_t)SOP_READ_FLAG;
14668226594fSRick McNeal 		cdb[0] = (uint8_t)cmd;
14678226594fSRick McNeal 		cdb[2] = (uint8_t)(vpd_page >> 8);
14688226594fSRick McNeal 		cdb[3] = (uint8_t)vpd_page;
14698226594fSRick McNeal 		cdb[4] = len >> 9;
14708226594fSRick McNeal 		break;
14718226594fSRick McNeal 
14728226594fSRick McNeal 	case SCMD_MODE_SENSE:
14738226594fSRick McNeal 		rqst->rp_data_dir = (uint8_t)SOP_READ_FLAG;
14748226594fSRick McNeal 		cdb[0] = (uint8_t)cmd;
14758226594fSRick McNeal 		cdb[1] = 0;
14768226594fSRick McNeal 		cdb[2] = (uint8_t)vpd_page;
14778226594fSRick McNeal 		cdb[4] = (uint8_t)len;
14788226594fSRick McNeal 		break;
14798226594fSRick McNeal 
14808226594fSRick McNeal 	case SCMD_INQUIRY:
14818226594fSRick McNeal 		rqst->rp_data_dir = SOP_READ_FLAG;
14828226594fSRick McNeal 		cdb[0] = (uint8_t)cmd;
14838226594fSRick McNeal 		if (vpd_page & VPD_PAGE) {
14848226594fSRick McNeal 			cdb[1] = 0x1;
14858226594fSRick McNeal 			cdb[2] = (uint8_t)vpd_page;
14868226594fSRick McNeal 		}
14878226594fSRick McNeal 		cdb[4] = (uint8_t)len;
14888226594fSRick McNeal 		break;
14898226594fSRick McNeal 
14908226594fSRick McNeal 	case BMIC_IDENTIFY_PHYSICAL_DEVICE:
14918226594fSRick McNeal 	case BMIC_IDENTIFY_CONTROLLER:
14928226594fSRick McNeal 		rqst->rp_data_dir = SOP_READ_FLAG;
14938226594fSRick McNeal 		cdb[0] = BMIC_READ;
14948226594fSRick McNeal 		cdb[6] = (uint8_t)cmd;
14958226594fSRick McNeal 		cdb[7] = (uint8_t)(len >> 8);
14968226594fSRick McNeal 		cdb[8] = (uint8_t)len;
14978226594fSRick McNeal 		break;
14988226594fSRick McNeal 
14998226594fSRick McNeal 	case BMIC_WRITE_HOST_WELLNESS:
15008226594fSRick McNeal 		rqst->rp_data_dir = SOP_WRITE_FLAG;
15018226594fSRick McNeal 		cdb[0] = BMIC_WRITE;
15028226594fSRick McNeal 		cdb[6] = (uint8_t)cmd;
15038226594fSRick McNeal 		cdb[7] = (uint8_t)(len >> 8);
15048226594fSRick McNeal 		cdb[8] = (uint8_t)len;
15058226594fSRick McNeal 		break;
15068226594fSRick McNeal 
15078226594fSRick McNeal 	case CISS_REPORT_LOG:
15088226594fSRick McNeal 	case CISS_REPORT_PHYS:
15098226594fSRick McNeal 		rqst->rp_data_dir = SOP_READ_FLAG;
15108226594fSRick McNeal 		cdb[0] = (uint8_t)cmd;
15118226594fSRick McNeal 		if (cmd == CISS_REPORT_PHYS)
15128226594fSRick McNeal 			cdb[1] = CISS_REPORT_PHYS_EXTENDED;
15138226594fSRick McNeal 		else
15148226594fSRick McNeal 			cdb[1] = CISS_REPORT_LOG_EXTENDED;
15158226594fSRick McNeal 		cdb[6] = (uint8_t)(len >> 24);
15168226594fSRick McNeal 		cdb[7] = (uint8_t)(len >> 16);
15178226594fSRick McNeal 		cdb[8] = (uint8_t)(len >> 8);
15188226594fSRick McNeal 		cdb[9] = (uint8_t)len;
15198226594fSRick McNeal 		break;
15208226594fSRick McNeal 
15218226594fSRick McNeal 	default:
15228226594fSRick McNeal 		ASSERT(0);
15238226594fSRick McNeal 		break;
15248226594fSRick McNeal 	}
15258226594fSRick McNeal 
15268226594fSRick McNeal 	return (B_TRUE);
15278226594fSRick McNeal }
15288226594fSRick McNeal 
15298226594fSRick McNeal static boolean_t
identify_physical_device(pqi_state_t * s,pqi_device_t * devp,bmic_identify_physical_device_t * buf)15308226594fSRick McNeal identify_physical_device(pqi_state_t *s, pqi_device_t *devp,
15318226594fSRick McNeal     bmic_identify_physical_device_t *buf)
15328226594fSRick McNeal {
15338226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
15348226594fSRick McNeal 	pqi_raid_path_request_t	rqst;
15358226594fSRick McNeal 	boolean_t		rval = B_FALSE;
15368226594fSRick McNeal 	uint16_t		idx;
15378226594fSRick McNeal 
15388226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, sizeof (*buf))) == NULL)
15398226594fSRick McNeal 		return (B_FALSE);
15408226594fSRick McNeal 
15418226594fSRick McNeal 	if (build_raid_path_request(&rqst, BMIC_IDENTIFY_PHYSICAL_DEVICE,
15428226594fSRick McNeal 	    RAID_CTLR_LUNID, sizeof (*buf), 0) == B_FALSE)
15438226594fSRick McNeal 		goto out;
15448226594fSRick McNeal 
15458226594fSRick McNeal 	idx = CISS_GET_DRIVE_NUMBER(devp->pd_scsi3addr);
15468226594fSRick McNeal 	rqst.rp_cdb[2] = (uint8_t)idx;
15478226594fSRick McNeal 	rqst.rp_cdb[9] = (uint8_t)(idx >> 8);
15488226594fSRick McNeal 
15498226594fSRick McNeal 	rqst.rp_sglist[0].sg_addr = dma->dma_addr;
15508226594fSRick McNeal 	rqst.rp_sglist[0].sg_len = dma->len_to_alloc;
15518226594fSRick McNeal 	rqst.rp_sglist[0].sg_flags = CISS_SG_LAST;
15528226594fSRick McNeal 
15538226594fSRick McNeal 	if (submit_raid_rqst_sync(s, &rqst.header, NULL) == B_FALSE)
15548226594fSRick McNeal 		goto out;
15558226594fSRick McNeal 
15568226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
15578226594fSRick McNeal 	(void) memcpy(buf, dma->alloc_memory, sizeof (*buf));
15588226594fSRick McNeal 	rval = B_TRUE;
15598226594fSRick McNeal out:
15608226594fSRick McNeal 	pqi_free_single(s, dma);
15618226594fSRick McNeal 	return (rval);
15628226594fSRick McNeal }
15638226594fSRick McNeal 
15648226594fSRick McNeal static boolean_t
identify_controller(pqi_state_t * s,bmic_identify_controller_t * ident)15658226594fSRick McNeal identify_controller(pqi_state_t *s, bmic_identify_controller_t *ident)
15668226594fSRick McNeal {
15678226594fSRick McNeal 	pqi_raid_path_request_t	rqst;
15688226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
15698226594fSRick McNeal 	boolean_t		rval = B_FALSE;
15708226594fSRick McNeal 
15718226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, sizeof (*ident))) == NULL)
15728226594fSRick McNeal 		return (B_FALSE);
15738226594fSRick McNeal 
15748226594fSRick McNeal 	if (build_raid_path_request(&rqst, BMIC_IDENTIFY_CONTROLLER,
15758226594fSRick McNeal 	    RAID_CTLR_LUNID, sizeof (*ident), 0) == B_FALSE)
15768226594fSRick McNeal 		goto out;
15778226594fSRick McNeal 
15788226594fSRick McNeal 	rqst.rp_sglist[0].sg_addr = dma->dma_addr;
15798226594fSRick McNeal 	rqst.rp_sglist[0].sg_len = dma->len_to_alloc;
15808226594fSRick McNeal 	rqst.rp_sglist[0].sg_flags = CISS_SG_LAST;
15818226594fSRick McNeal 
15828226594fSRick McNeal 	if (submit_raid_rqst_sync(s, &rqst.header, NULL) == B_FALSE)
15838226594fSRick McNeal 		goto out;
15848226594fSRick McNeal 
15858226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
15868226594fSRick McNeal 	(void) memcpy(ident, dma->alloc_memory, sizeof (*ident));
15878226594fSRick McNeal 	rval = B_TRUE;
15888226594fSRick McNeal out:
15898226594fSRick McNeal 	pqi_free_single(s, dma);
15908226594fSRick McNeal 	return (rval);
15918226594fSRick McNeal }
15928226594fSRick McNeal 
15938226594fSRick McNeal static boolean_t
write_host_wellness(pqi_state_t * s,void * buf,size_t len)15948226594fSRick McNeal write_host_wellness(pqi_state_t *s, void *buf, size_t len)
15958226594fSRick McNeal {
15968226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
15978226594fSRick McNeal 	boolean_t		rval = B_FALSE;
15988226594fSRick McNeal 	pqi_raid_path_request_t	rqst;
15998226594fSRick McNeal 
16008226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, len)) == NULL)
16018226594fSRick McNeal 		return (B_FALSE);
16028226594fSRick McNeal 	if (build_raid_path_request(&rqst, BMIC_WRITE_HOST_WELLNESS,
16038226594fSRick McNeal 	    RAID_CTLR_LUNID, len, 0) == B_FALSE)
16048226594fSRick McNeal 		goto out;
16058226594fSRick McNeal 
16068226594fSRick McNeal 	(void) memcpy(dma->alloc_memory, buf, dma->len_to_alloc);
16078226594fSRick McNeal 	rqst.rp_sglist[0].sg_addr = dma->dma_addr;
16088226594fSRick McNeal 	rqst.rp_sglist[0].sg_len = dma->len_to_alloc;
16098226594fSRick McNeal 	rqst.rp_sglist[0].sg_flags = CISS_SG_LAST;
16108226594fSRick McNeal 
16118226594fSRick McNeal 	rval = submit_raid_rqst_sync(s, &rqst.header, NULL);
16128226594fSRick McNeal out:
16138226594fSRick McNeal 	pqi_free_single(s, dma);
16148226594fSRick McNeal 	return (rval);
16158226594fSRick McNeal }
16168226594fSRick McNeal 
16178226594fSRick McNeal static boolean_t
report_luns(pqi_state_t * s,int cmd,void * data,size_t len)16188226594fSRick McNeal report_luns(pqi_state_t *s, int cmd, void *data, size_t len)
16198226594fSRick McNeal {
16208226594fSRick McNeal 	pqi_dma_overhead_t	*dma;
16218226594fSRick McNeal 	boolean_t		rval = B_FALSE;
16228226594fSRick McNeal 	pqi_raid_path_request_t	rqst;
16238226594fSRick McNeal 
16248226594fSRick McNeal 	if ((dma = pqi_alloc_single(s, len)) == NULL)
16258226594fSRick McNeal 		return (B_FALSE);
16268226594fSRick McNeal 	if (build_raid_path_request(&rqst, cmd, RAID_CTLR_LUNID,
16278226594fSRick McNeal 	    len, 0) == B_FALSE)
16288226594fSRick McNeal 		goto error_out;
16298226594fSRick McNeal 
16308226594fSRick McNeal 	rqst.rp_sglist[0].sg_addr = dma->dma_addr;
16318226594fSRick McNeal 	rqst.rp_sglist[0].sg_len = dma->len_to_alloc;
16328226594fSRick McNeal 	rqst.rp_sglist[0].sg_flags = CISS_SG_LAST;
16338226594fSRick McNeal 
16348226594fSRick McNeal 	if (submit_raid_rqst_sync(s, &rqst.header, NULL) == B_FALSE)
16358226594fSRick McNeal 		goto error_out;
16368226594fSRick McNeal 
16378226594fSRick McNeal 	(void) ddi_dma_sync(dma->handle, 0, 0, DDI_DMA_SYNC_FORCPU);
16388226594fSRick McNeal 	(void) memcpy(data, dma->alloc_memory, len);
16398226594fSRick McNeal 	rval = B_TRUE;
16408226594fSRick McNeal 
16418226594fSRick McNeal error_out:
16428226594fSRick McNeal 	pqi_free_single(s, dma);
16438226594fSRick McNeal 	return (rval);
16448226594fSRick McNeal }
16458226594fSRick McNeal 
16468226594fSRick McNeal static boolean_t
report_luns_by_cmd(pqi_state_t * s,int cmd,void ** buf,size_t * buflen)1647*4091c922SYuri Pankov report_luns_by_cmd(pqi_state_t *s, int cmd, void **buf, size_t *buflen)
16488226594fSRick McNeal {
16498226594fSRick McNeal 	void		*data		= NULL;
16508226594fSRick McNeal 	size_t		data_len	= 0;
16518226594fSRick McNeal 	size_t		new_data_len;
16528226594fSRick McNeal 	uint32_t	new_list_len	= 0;
16538226594fSRick McNeal 	uint32_t	list_len	= 0;
16548226594fSRick McNeal 	boolean_t	rval		= B_FALSE;
16558226594fSRick McNeal 
16568226594fSRick McNeal 	new_data_len = sizeof (report_lun_header_t);
16578226594fSRick McNeal 	do {
16588226594fSRick McNeal 		if (data != NULL) {
16598226594fSRick McNeal 			kmem_free(data, data_len);
16608226594fSRick McNeal 		}
16618226594fSRick McNeal 		data_len = new_data_len;
16628226594fSRick McNeal 		data = kmem_zalloc(data_len, KM_SLEEP);
16638226594fSRick McNeal 		list_len = new_list_len;
16648226594fSRick McNeal 		if (report_luns(s, cmd, data, data_len) == B_FALSE)
16658226594fSRick McNeal 			goto error_out;
16668226594fSRick McNeal 		new_list_len =
16678226594fSRick McNeal 		    ntohl(((report_lun_header_t *)data)->list_length);
16688226594fSRick McNeal 		new_data_len = sizeof (report_lun_header_t) +
16698226594fSRick McNeal 		    new_list_len;
16708226594fSRick McNeal 	} while (new_list_len > list_len);
16718226594fSRick McNeal 	rval = B_TRUE;
16728226594fSRick McNeal 
16738226594fSRick McNeal error_out:
16748226594fSRick McNeal 	if (rval == B_FALSE) {
16758226594fSRick McNeal 		kmem_free(data, data_len);
16768226594fSRick McNeal 		data = NULL;
1677*4091c922SYuri Pankov 		data_len = 0;
16788226594fSRick McNeal 	}
16798226594fSRick McNeal 	*buf = data;
1680*4091c922SYuri Pankov 	*buflen = data_len;
16818226594fSRick McNeal 	return (rval);
16828226594fSRick McNeal }
16838226594fSRick McNeal 
16848226594fSRick McNeal static inline boolean_t
report_phys_luns(pqi_state_t * s,void ** v,size_t * vlen)1685*4091c922SYuri Pankov report_phys_luns(pqi_state_t *s, void **v, size_t *vlen)
16868226594fSRick McNeal {
1687*4091c922SYuri Pankov 	return (report_luns_by_cmd(s, CISS_REPORT_PHYS, v, vlen));
16888226594fSRick McNeal }
16898226594fSRick McNeal 
16908226594fSRick McNeal static inline boolean_t
report_logical_luns(pqi_state_t * s,void ** v,size_t * vlen)1691*4091c922SYuri Pankov report_logical_luns(pqi_state_t *s, void **v, size_t *vlen)
16928226594fSRick McNeal {
1693*4091c922SYuri Pankov 	return (report_luns_by_cmd(s, CISS_REPORT_LOG, v, vlen));
16948226594fSRick McNeal }
16958226594fSRick McNeal 
16968226594fSRick McNeal static boolean_t
get_device_list(pqi_state_t * s,report_phys_lun_extended_t ** pl,size_t * plen,report_log_lun_extended_t ** ll,size_t * llen)1697*4091c922SYuri Pankov get_device_list(pqi_state_t *s, report_phys_lun_extended_t **pl, size_t *plen,
1698*4091c922SYuri Pankov     report_log_lun_extended_t **ll, size_t *llen)
16998226594fSRick McNeal {
17008226594fSRick McNeal 	report_log_lun_extended_t	*log_data;
17018226594fSRick McNeal 	report_log_lun_extended_t	*internal_log;
17028226594fSRick McNeal 	size_t				list_len;
17038226594fSRick McNeal 	size_t				data_len;
17048226594fSRick McNeal 	report_lun_header_t		header;
17058226594fSRick McNeal 
1706*4091c922SYuri Pankov 	if (report_phys_luns(s, (void **)pl, plen) == B_FALSE)
17078226594fSRick McNeal 		return (B_FALSE);
17088226594fSRick McNeal 
1709*4091c922SYuri Pankov 	if (report_logical_luns(s, (void **)ll, llen) == B_FALSE)
17108226594fSRick McNeal 		return (B_FALSE);
17118226594fSRick McNeal 
17128226594fSRick McNeal 	log_data = *ll;
1713*4091c922SYuri Pankov 	if (log_data != NULL) {
17148226594fSRick McNeal 		list_len = ntohl(log_data->header.list_length);
17158226594fSRick McNeal 	} else {
17168226594fSRick McNeal 		(void) memset(&header, 0, sizeof (header));
17178226594fSRick McNeal 		log_data = (report_log_lun_extended_t *)&header;
17188226594fSRick McNeal 		list_len = 0;
17198226594fSRick McNeal 	}
17208226594fSRick McNeal 
17218226594fSRick McNeal 	data_len = sizeof (header) + list_len;
17228226594fSRick McNeal 	/*
17238226594fSRick McNeal 	 * Add the controller to the logical luns which is a empty device
17248226594fSRick McNeal 	 */
17258226594fSRick McNeal 	internal_log = kmem_zalloc(data_len +
17268226594fSRick McNeal 	    sizeof (report_log_lun_extended_entry_t), KM_SLEEP);
17278226594fSRick McNeal 	(void) memcpy(internal_log, log_data, data_len);
17288226594fSRick McNeal 	internal_log->header.list_length = htonl(list_len +
17298226594fSRick McNeal 	    sizeof (report_log_lun_extended_entry_t));
17308226594fSRick McNeal 
17318226594fSRick McNeal 	if (*ll != NULL)
1732*4091c922SYuri Pankov 		kmem_free(*ll, *llen);
17338226594fSRick McNeal 	*ll = internal_log;
1734*4091c922SYuri Pankov 	*llen = data_len + sizeof (report_log_lun_extended_entry_t);
17358226594fSRick McNeal 	return (B_TRUE);
17368226594fSRick McNeal }
17378226594fSRick McNeal 
17388226594fSRick McNeal static boolean_t
get_device_info(pqi_state_t * s,pqi_device_t * dev)17398226594fSRick McNeal get_device_info(pqi_state_t *s, pqi_device_t *dev)
17408226594fSRick McNeal {
17418226594fSRick McNeal 	boolean_t		rval = B_FALSE;
17428226594fSRick McNeal 	struct scsi_inquiry	*inq;
17438226594fSRick McNeal 
17448226594fSRick McNeal 	inq = kmem_zalloc(sizeof (*inq), KM_SLEEP);
17458226594fSRick McNeal 	if (pqi_scsi_inquiry(s, dev, 0, inq, sizeof (*inq)) == B_FALSE)
17468226594fSRick McNeal 		goto out;
17478226594fSRick McNeal 
17488226594fSRick McNeal 	dev->pd_devtype = inq->inq_dtype & 0x1f;
17498226594fSRick McNeal 	(void) memcpy(dev->pd_vendor, inq->inq_vid, sizeof (dev->pd_vendor));
17508226594fSRick McNeal 	(void) memcpy(dev->pd_model, inq->inq_pid, sizeof (dev->pd_model));
17518226594fSRick McNeal 
17528226594fSRick McNeal 	rval = B_TRUE;
17538226594fSRick McNeal out:
17548226594fSRick McNeal 	kmem_free(inq, sizeof (*inq));
17558226594fSRick McNeal 	return (rval);
17568226594fSRick McNeal }
17578226594fSRick McNeal 
17588226594fSRick McNeal static boolean_t
is_supported_dev(pqi_state_t * s,pqi_device_t * dev)17598226594fSRick McNeal is_supported_dev(pqi_state_t *s, pqi_device_t *dev)
17608226594fSRick McNeal {
17618226594fSRick McNeal 	boolean_t	rval = B_FALSE;
17628226594fSRick McNeal 
17638226594fSRick McNeal 	switch (dev->pd_devtype) {
17648226594fSRick McNeal 	case DTYPE_DIRECT:
17658226594fSRick McNeal 	case TYPE_ZBC:
17668226594fSRick McNeal 	case DTYPE_SEQUENTIAL:
17678226594fSRick McNeal 	case DTYPE_ESI:
17688226594fSRick McNeal 		rval = B_TRUE;
17698226594fSRick McNeal 		break;
17708226594fSRick McNeal 	case DTYPE_ARRAY_CTRL:
17718226594fSRick McNeal 		if (strncmp(dev->pd_scsi3addr, RAID_CTLR_LUNID,
17728226594fSRick McNeal 		    sizeof (dev->pd_scsi3addr)) == 0)
17738226594fSRick McNeal 			rval = B_TRUE;
17748226594fSRick McNeal 		break;
17758226594fSRick McNeal 	default:
17768226594fSRick McNeal 		dev_err(s->s_dip, CE_WARN, "%s is not a supported device",
17778226594fSRick McNeal 		    scsi_dname(dev->pd_devtype));
17788226594fSRick McNeal 		break;
17798226594fSRick McNeal 	}
17808226594fSRick McNeal 	return (rval);
17818226594fSRick McNeal }
17828226594fSRick McNeal 
17838226594fSRick McNeal static void
get_phys_disk_info(pqi_state_t * s __unused,pqi_device_t * dev,bmic_identify_physical_device_t * id)17848226594fSRick McNeal get_phys_disk_info(pqi_state_t *s __unused, pqi_device_t *dev,
17858226594fSRick McNeal     bmic_identify_physical_device_t *id)
17868226594fSRick McNeal {
17878226594fSRick McNeal 	dev->pd_lun = id->scsi_lun;
17888226594fSRick McNeal 	(void) snprintf(dev->pd_unit_address, sizeof (dev->pd_unit_address),
17898226594fSRick McNeal 	    "w%016lx,%d", dev->pd_wwid, id->scsi_lun);
17908226594fSRick McNeal }
17918226594fSRick McNeal 
17928226594fSRick McNeal static int
is_external_raid_addr(char * addr)17938226594fSRick McNeal is_external_raid_addr(char *addr)
17948226594fSRick McNeal {
17958226594fSRick McNeal 	return (addr[2] != 0);
17968226594fSRick McNeal }
17978226594fSRick McNeal 
17988226594fSRick McNeal static void
build_guid(pqi_state_t * s,pqi_device_t * d)17998226594fSRick McNeal build_guid(pqi_state_t *s, pqi_device_t *d)
18008226594fSRick McNeal {
18018226594fSRick McNeal 	int			len	= 0xff;
18028226594fSRick McNeal 	struct scsi_inquiry	*inq	= NULL;
18038226594fSRick McNeal 	uchar_t			*inq83	= NULL;
18048226594fSRick McNeal 	ddi_devid_t		devid;
18058226594fSRick McNeal 
18068226594fSRick McNeal 	ddi_devid_free_guid(d->pd_guid);
18078226594fSRick McNeal 	d->pd_guid = NULL;
18088226594fSRick McNeal 
18098226594fSRick McNeal 	inq = kmem_alloc(sizeof (struct scsi_inquiry), KM_SLEEP);
18108226594fSRick McNeal 	if (pqi_scsi_inquiry(s, d, 0, inq, sizeof (struct scsi_inquiry)) ==
18118226594fSRick McNeal 	    B_FALSE) {
18128226594fSRick McNeal 		goto out;
18138226594fSRick McNeal 	}
18148226594fSRick McNeal 
18158226594fSRick McNeal 	inq83 = kmem_zalloc(len, KM_SLEEP);
18168226594fSRick McNeal 	if (pqi_scsi_inquiry(s, d, VPD_PAGE | 0x83,
18178226594fSRick McNeal 	    (struct scsi_inquiry *)inq83, len) == B_FALSE) {
18188226594fSRick McNeal 		goto out;
18198226594fSRick McNeal 	}
18208226594fSRick McNeal 
18218226594fSRick McNeal 	if (ddi_devid_scsi_encode(DEVID_SCSI_ENCODE_VERSION_LATEST, NULL,
18228226594fSRick McNeal 	    (uchar_t *)inq, sizeof (struct scsi_inquiry), NULL, 0, inq83,
18238226594fSRick McNeal 	    (size_t)len, &devid) == DDI_SUCCESS) {
18248226594fSRick McNeal 		d->pd_guid = ddi_devid_to_guid(devid);
18258226594fSRick McNeal 		ddi_devid_free(devid);
18268226594fSRick McNeal 	}
18278226594fSRick McNeal out:
18288226594fSRick McNeal 	if (inq != NULL)
18298226594fSRick McNeal 		kmem_free(inq, sizeof (struct scsi_inquiry));
18308226594fSRick McNeal 	if (inq83 != NULL)
18318226594fSRick McNeal 		kmem_free(inq83, len);
18328226594fSRick McNeal }
18338226594fSRick McNeal 
18348226594fSRick McNeal static pqi_device_t *
create_phys_dev(pqi_state_t * s,report_phys_lun_extended_entry_t * e)18358226594fSRick McNeal create_phys_dev(pqi_state_t *s, report_phys_lun_extended_entry_t *e)
18368226594fSRick McNeal {
18378226594fSRick McNeal 	pqi_device_t			*dev;
18388226594fSRick McNeal 	bmic_identify_physical_device_t	*id_phys	= NULL;
18398226594fSRick McNeal 
18408226594fSRick McNeal 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
18418226594fSRick McNeal 	dev->pd_phys_dev = 1;
18428226594fSRick McNeal 	dev->pd_wwid = htonll(e->wwid);
18438226594fSRick McNeal 	(void) memcpy(dev->pd_scsi3addr, e->lunid, sizeof (dev->pd_scsi3addr));
18448226594fSRick McNeal 
18458226594fSRick McNeal 	/* Skip masked physical devices */
18468226594fSRick McNeal 	if (MASKED_DEVICE(dev->pd_scsi3addr))
18478226594fSRick McNeal 		goto out;
18488226594fSRick McNeal 
18498226594fSRick McNeal 	if (get_device_info(s, dev) == B_FALSE)
18508226594fSRick McNeal 		goto out;
18518226594fSRick McNeal 
18528226594fSRick McNeal 	if (!is_supported_dev(s, dev))
18538226594fSRick McNeal 		goto out;
18548226594fSRick McNeal 
18558226594fSRick McNeal 	switch (dev->pd_devtype) {
18568226594fSRick McNeal 	case DTYPE_ESI:
18578226594fSRick McNeal 		build_guid(s, dev);
18588226594fSRick McNeal 		/* hopefully only LUN 0... which seems to match */
18598226594fSRick McNeal 		(void) snprintf(dev->pd_unit_address, 20, "w%016lx,0",
18608226594fSRick McNeal 		    dev->pd_wwid);
18618226594fSRick McNeal 		break;
18628226594fSRick McNeal 
18638226594fSRick McNeal 	case DTYPE_DIRECT:
18648226594fSRick McNeal 	case TYPE_ZBC:
18658226594fSRick McNeal 		build_guid(s, dev);
18668226594fSRick McNeal 		id_phys = kmem_zalloc(sizeof (*id_phys), KM_SLEEP);
18678226594fSRick McNeal 		if ((e->device_flags &
18688226594fSRick McNeal 		    REPORT_PHYS_LUN_DEV_FLAG_AIO_ENABLED) &&
18698226594fSRick McNeal 		    e->aio_handle) {
18708226594fSRick McNeal 
18718226594fSRick McNeal 			/*
18728226594fSRick McNeal 			 * XXX Until I figure out what's wrong with
18738226594fSRick McNeal 			 * using AIO I'll disable this for now.
18748226594fSRick McNeal 			 */
18758226594fSRick McNeal 			dev->pd_aio_enabled = 0;
18768226594fSRick McNeal 			dev->pd_aio_handle = e->aio_handle;
18778226594fSRick McNeal 			if (identify_physical_device(s, dev,
18788226594fSRick McNeal 			    id_phys) == B_FALSE)
18798226594fSRick McNeal 				goto out;
18808226594fSRick McNeal 		}
18818226594fSRick McNeal 		get_phys_disk_info(s, dev, id_phys);
18828226594fSRick McNeal 		kmem_free(id_phys, sizeof (*id_phys));
18838226594fSRick McNeal 		break;
18848226594fSRick McNeal 	}
18858226594fSRick McNeal 
18868226594fSRick McNeal 	return (dev);
18878226594fSRick McNeal out:
18888226594fSRick McNeal 	kmem_free(dev, sizeof (*dev));
18898226594fSRick McNeal 	return (NULL);
18908226594fSRick McNeal }
18918226594fSRick McNeal 
18928226594fSRick McNeal static pqi_device_t *
create_logical_dev(pqi_state_t * s,report_log_lun_extended_entry_t * e)18938226594fSRick McNeal create_logical_dev(pqi_state_t *s, report_log_lun_extended_entry_t *e)
18948226594fSRick McNeal {
18958226594fSRick McNeal 	pqi_device_t	*dev;
18968226594fSRick McNeal 	uint16_t	target;
18978226594fSRick McNeal 	uint16_t	lun;
18988226594fSRick McNeal 
18998226594fSRick McNeal 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
19008226594fSRick McNeal 	dev->pd_phys_dev = 0;
19018226594fSRick McNeal 	(void) memcpy(dev->pd_scsi3addr, e->lunid, sizeof (dev->pd_scsi3addr));
19028226594fSRick McNeal 	dev->pd_external_raid = is_external_raid_addr(dev->pd_scsi3addr);
19038226594fSRick McNeal 
19048226594fSRick McNeal 	if (get_device_info(s, dev) == B_FALSE)
19058226594fSRick McNeal 		goto out;
19068226594fSRick McNeal 
19078226594fSRick McNeal 	if (!is_supported_dev(s, dev))
19088226594fSRick McNeal 		goto out;
19098226594fSRick McNeal 
19108226594fSRick McNeal 	if (memcmp(dev->pd_scsi3addr, RAID_CTLR_LUNID, 8) == 0) {
19118226594fSRick McNeal 		target = 0;
19128226594fSRick McNeal 		lun = 0;
19138226594fSRick McNeal 	} else if (dev->pd_external_raid) {
19148226594fSRick McNeal 		target = (LE_IN16(&dev->pd_scsi3addr[2]) & 0x3FFF) + 2;
19158226594fSRick McNeal 		lun = dev->pd_scsi3addr[0];
19168226594fSRick McNeal 	} else {
19178226594fSRick McNeal 		target = 1;
19188226594fSRick McNeal 		lun = LE_IN16(dev->pd_scsi3addr);
19198226594fSRick McNeal 	}
19208226594fSRick McNeal 	dev->pd_target = target;
19218226594fSRick McNeal 	dev->pd_lun = lun;
19228226594fSRick McNeal 	(void) snprintf(dev->pd_unit_address, sizeof (dev->pd_unit_address),
19238226594fSRick McNeal 	    "%d,%d", target, lun);
19248226594fSRick McNeal 
19258226594fSRick McNeal 	(void) memcpy(dev->pd_volume_id, e->volume_id,
19268226594fSRick McNeal 	    sizeof (dev->pd_volume_id));
19278226594fSRick McNeal 	return (dev);
19288226594fSRick McNeal 
19298226594fSRick McNeal out:
19308226594fSRick McNeal 	kmem_free(dev, sizeof (*dev));
19318226594fSRick McNeal 	return (NULL);
19328226594fSRick McNeal }
19338226594fSRick McNeal 
19348226594fSRick McNeal /*
19358226594fSRick McNeal  * is_new_dev -- look to see if new_dev is indeed new.
19368226594fSRick McNeal  *
19378226594fSRick McNeal  * NOTE: This function has two outcomes. One is to determine if the new_dev
19388226594fSRick McNeal  * is truly new. The other is to mark a new_dev as being scanned if it's
19398226594fSRick McNeal  * truly new or marking the existing device as having been scanned.
19408226594fSRick McNeal  */
19418226594fSRick McNeal static boolean_t
is_new_dev(pqi_state_t * s,pqi_device_t * new_dev)19428226594fSRick McNeal is_new_dev(pqi_state_t *s, pqi_device_t *new_dev)
19438226594fSRick McNeal {
19448226594fSRick McNeal 	pqi_device_t	*dev;
19458226594fSRick McNeal 
19468226594fSRick McNeal 	for (dev = list_head(&s->s_devnodes); dev != NULL;
19478226594fSRick McNeal 	    dev = list_next(&s->s_devnodes, dev)) {
19488226594fSRick McNeal 		if (new_dev->pd_phys_dev != dev->pd_phys_dev) {
19498226594fSRick McNeal 			continue;
19508226594fSRick McNeal 		}
19518226594fSRick McNeal 		if (dev->pd_phys_dev) {
19528226594fSRick McNeal 			if (dev->pd_wwid == new_dev->pd_wwid) {
19538226594fSRick McNeal 				dev->pd_scanned = 1;
19548226594fSRick McNeal 				return (B_FALSE);
19558226594fSRick McNeal 			}
19568226594fSRick McNeal 		} else {
19578226594fSRick McNeal 			if (memcmp(dev->pd_volume_id, new_dev->pd_volume_id,
19588226594fSRick McNeal 			    16) == 0) {
19598226594fSRick McNeal 				dev->pd_scanned = 1;
19608226594fSRick McNeal 				return (B_FALSE);
19618226594fSRick McNeal 			}
19628226594fSRick McNeal 		}
19638226594fSRick McNeal 	}
19648226594fSRick McNeal 
19658226594fSRick McNeal 	new_dev->pd_scanned = 1;
19668226594fSRick McNeal 	return (B_TRUE);
19678226594fSRick McNeal }
19688226594fSRick McNeal 
19698226594fSRick McNeal enum pqi_reset_action {
19708226594fSRick McNeal 	PQI_RESET_ACTION_RESET = 0x1,
19718226594fSRick McNeal 	PQI_RESET_ACTION_COMPLETE = 0x2
19728226594fSRick McNeal };
19738226594fSRick McNeal 
19748226594fSRick McNeal enum pqi_reset_type {
19758226594fSRick McNeal 	PQI_RESET_TYPE_NO_RESET =	0x0,
19768226594fSRick McNeal 	PQI_RESET_TYPE_SOFT_RESET =	0x1,
19778226594fSRick McNeal 	PQI_RESET_TYPE_FIRM_RESET =	0x2,
19788226594fSRick McNeal 	PQI_RESET_TYPE_HARD_RESET =	0x3
19798226594fSRick McNeal };
19808226594fSRick McNeal 
19818226594fSRick McNeal boolean_t
pqi_hba_reset(pqi_state_t * s)19828226594fSRick McNeal pqi_hba_reset(pqi_state_t *s)
19838226594fSRick McNeal {
19848226594fSRick McNeal 	uint32_t	val;
19858226594fSRick McNeal 	int		max_count = 1000;
19868226594fSRick McNeal 
19878226594fSRick McNeal 	val = (PQI_RESET_ACTION_RESET << 5) | PQI_RESET_TYPE_HARD_RESET;
19888226594fSRick McNeal 	S32(s, pqi_registers.device_reset, val);
19898226594fSRick McNeal 
19908226594fSRick McNeal 	while (1) {
19918226594fSRick McNeal 		drv_usecwait(100 * (MICROSEC / MILLISEC));
19928226594fSRick McNeal 		val = G32(s, pqi_registers.device_reset);
19938226594fSRick McNeal 		if ((val >> 5) == PQI_RESET_ACTION_COMPLETE)
19948226594fSRick McNeal 			break;
19958226594fSRick McNeal 		if (max_count-- == 0)
19968226594fSRick McNeal 			break;
19978226594fSRick McNeal 	}
19988226594fSRick McNeal 
19998226594fSRick McNeal #ifdef DEBUG
20008226594fSRick McNeal 	cmn_err(CE_WARN, "pqi_hba_reset: reset reg=0x%x, count=%d", val,
20018226594fSRick McNeal 	    max_count);
20028226594fSRick McNeal #endif
20038226594fSRick McNeal 	return (pqi_wait_for_mode_ready(s));
20048226594fSRick McNeal }
20058226594fSRick McNeal 
20068226594fSRick McNeal static void
save_ctrl_mode(pqi_state_t * s,int mode)20078226594fSRick McNeal save_ctrl_mode(pqi_state_t *s, int mode)
20088226594fSRick McNeal {
20098226594fSRick McNeal 	sis_write_scratch(s, mode);
20108226594fSRick McNeal }
20118226594fSRick McNeal 
20128226594fSRick McNeal static boolean_t
revert_to_sis(pqi_state_t * s)20138226594fSRick McNeal revert_to_sis(pqi_state_t *s)
20148226594fSRick McNeal {
20158226594fSRick McNeal 	if (!pqi_hba_reset(s))
20168226594fSRick McNeal 		return (B_FALSE);
20178226594fSRick McNeal 	if (sis_reenable_mode(s) == B_FALSE)
20188226594fSRick McNeal 		return (B_FALSE);
20198226594fSRick McNeal 	sis_write_scratch(s, SIS_MODE);
20208226594fSRick McNeal 	return (B_TRUE);
20218226594fSRick McNeal }
20228226594fSRick McNeal 
20238226594fSRick McNeal 
20248226594fSRick McNeal #define	BIN2BCD(x)	((((x) / 10) << 4) + (x) % 10)
20258226594fSRick McNeal 
20268226594fSRick McNeal static void
update_time(void * v)20278226594fSRick McNeal update_time(void *v)
20288226594fSRick McNeal {
20298226594fSRick McNeal 	pqi_state_t			*s = v;
20308226594fSRick McNeal 	bmic_host_wellness_time_t	*ht;
20318226594fSRick McNeal 	struct timeval			curtime;
20328226594fSRick McNeal 	todinfo_t			tod;
20338226594fSRick McNeal 
20348226594fSRick McNeal 	ht = kmem_zalloc(sizeof (*ht), KM_SLEEP);
20358226594fSRick McNeal 	ht->start_tag[0] = '<';
20368226594fSRick McNeal 	ht->start_tag[1] = 'H';
20378226594fSRick McNeal 	ht->start_tag[2] = 'W';
20388226594fSRick McNeal 	ht->start_tag[3] = '>';
20398226594fSRick McNeal 	ht->time_tag[0] = 'T';
20408226594fSRick McNeal 	ht->time_tag[1] = 'D';
20418226594fSRick McNeal 	ht->time_length = sizeof (ht->time);
20428226594fSRick McNeal 
20438226594fSRick McNeal 	uniqtime(&curtime);
20448226594fSRick McNeal 	mutex_enter(&tod_lock);
20458226594fSRick McNeal 	tod = utc_to_tod(curtime.tv_sec);
20468226594fSRick McNeal 	mutex_exit(&tod_lock);
20478226594fSRick McNeal 
20488226594fSRick McNeal 	ht->time[0] = BIN2BCD(tod.tod_hour);		/* Hour */
20498226594fSRick McNeal 	ht->time[1] = BIN2BCD(tod.tod_min);		/* Minute */
20508226594fSRick McNeal 	ht->time[2] = BIN2BCD(tod.tod_sec);		/* Second */
20518226594fSRick McNeal 	ht->time[3] = 0;
20528226594fSRick McNeal 	ht->time[4] = BIN2BCD(tod.tod_month);		/* Month */
20538226594fSRick McNeal 	ht->time[5] = BIN2BCD(tod.tod_day);		/* Day */
20548226594fSRick McNeal 	ht->time[6] = BIN2BCD(20);			/* Century */
20558226594fSRick McNeal 	ht->time[7] = BIN2BCD(tod.tod_year - 70);	/* Year w/in century */
20568226594fSRick McNeal 
20578226594fSRick McNeal 	ht->dont_write_tag[0] = 'D';
20588226594fSRick McNeal 	ht->dont_write_tag[1] = 'W';
20598226594fSRick McNeal 	ht->end_tag[0] = 'Z';
20608226594fSRick McNeal 	ht->end_tag[1] = 'Z';
20618226594fSRick McNeal 
20628226594fSRick McNeal 	(void) write_host_wellness(s, ht, sizeof (*ht));
20638226594fSRick McNeal 	kmem_free(ht, sizeof (*ht));
20648226594fSRick McNeal 	s->s_time_of_day = timeout(update_time, s,
20658226594fSRick McNeal 	    DAY * drv_usectohz(MICROSEC));
20668226594fSRick McNeal }
2067