xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/smartpqi/smartpqi_sis.c (revision c3b397890d6e53844f306787c3e343c8ef35293d)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2018 Nexenta Systems, Inc.
14  */
15 
16 /*
17  * A few simple method to access controller when it's in the base mode.
18  */
19 #include <smartpqi.h>
20 
21 /* ---- legacy SIS interface commands ---- */
22 #define	SIS_CMD_GET_ADAPTER_PROPERTIES	0x19
23 #define	SIS_CMD_INIT_BASE_STRUCT_ADDRESS	0x1b
24 #define	SIS_CMD_GET_PQI_CAPABILITIES		0x3000
25 
26 /* ---- used with SIS_CMD_GET_ADAPTER_PROPERTIES command ---- */
27 #define	SIS_EXTENDED_PROPERTIES_SUPPORTED	0x800000
28 #define	SIS_SMARTARRAY_FEATURES_SUPPORTED	0x2
29 #define	SIS_PQI_MODE_SUPPORTED			0x4
30 #define	SIS_REQUIRED_EXTENDED_PROPERTIES	\
31 	(SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED)
32 
33 /* used for passing command parameters/results when issuing SIS commands */
34 typedef struct sis_sync_cmd_params {
35 	uint32_t	mailbox[6];	/* mailboxes 0-5 */
36 } __packed sis_sync_cmd_params_t;
37 
38 #define	SIS_BASE_STRUCT_REVISION	9
39 
40 typedef struct sis_base_struct {
41 	uint32_t	sb_revision;
42 	uint32_t	sb_flags;
43 	uint32_t	sb_error_buffer_paddr_low;
44 	uint32_t	sb_error_buffer_paddr_high;
45 	uint32_t	sb_error_elements_len;
46 	uint32_t	sb_error_elements_num;
47 } __packed sis_base_struct_t;
48 
49 /* ---- Forward declaration for support functions ---- */
50 static boolean_t sis_send_sync_cmd(pqi_state_t *s, uint32_t cmd,
51     sis_sync_cmd_params_t *params);
52 
53 uint32_t
54 sis_read_scratch(pqi_state_t *s)
55 {
56 	return (G32(s, sis_driver_scratch));
57 }
58 
59 void
60 sis_write_scratch(pqi_state_t *s, int mode)
61 {
62 	S32(s, sis_driver_scratch, mode);
63 }
64 
65 boolean_t
66 sis_reenable_mode(pqi_state_t *s)
67 {
68 	int		loop_count;
69 	uint32_t	doorbell;
70 
71 	S32(s, sis_host_to_ctrl_doorbell, SIS_REENABLE_SIS_MODE);
72 
73 	for (loop_count = 0; loop_count < 1000; loop_count++) {
74 		doorbell = G32(s, sis_ctrl_to_host_doorbell);
75 		if ((doorbell & SIS_REENABLE_SIS_MODE) == 0) {
76 			return (B_TRUE);
77 		}
78 		drv_usecwait(MICROSEC / MILLISEC); /* ---- Wait 1ms ---- */
79 	}
80 	return (B_FALSE);
81 }
82 
83 boolean_t
84 sis_wait_for_ctrl_ready(pqi_state_t *s)
85 {
86 	int		loop_count;
87 	uint32_t	status;
88 
89 	for (loop_count = 0; loop_count < 1000; loop_count++) {
90 		status = G32(s, sis_firmware_status);
91 		if (status & SIS_CTRL_KERNEL_PANIC)
92 			return (B_FALSE);
93 		if (status & SIS_CTRL_KERNEL_UP)
94 			return (B_TRUE);
95 		drv_usecwait(MICROSEC / MILLISEC); /* ---- Wait 1ms ---- */
96 	}
97 	return (B_FALSE);
98 }
99 
100 /*
101  * sis_get_ctrl_props -- Verify we're talking to controller that speaks PQI
102  */
103 boolean_t
104 sis_get_ctrl_props(pqi_state_t *s)
105 {
106 	sis_sync_cmd_params_t	p;
107 	uint32_t		property;
108 	uint32_t		extended_property;
109 
110 	(void) memset(&p, 0, sizeof (p));
111 	if (sis_send_sync_cmd(s, SIS_CMD_GET_ADAPTER_PROPERTIES, &p) == B_FALSE)
112 		return (B_FALSE);
113 
114 	property = p.mailbox[1];
115 	if (!(property & SIS_EXTENDED_PROPERTIES_SUPPORTED))
116 		return (B_FALSE);
117 
118 	extended_property = p.mailbox[4];
119 	if ((extended_property & SIS_REQUIRED_EXTENDED_PROPERTIES) !=
120 	    SIS_REQUIRED_EXTENDED_PROPERTIES)
121 		return (B_FALSE);
122 
123 	return (B_TRUE);
124 }
125 
126 boolean_t
127 sis_get_pqi_capabilities(pqi_state_t *s)
128 {
129 	sis_sync_cmd_params_t	p;
130 
131 	(void) memset(&p, 0, sizeof (p));
132 	if (sis_send_sync_cmd(s, SIS_CMD_GET_PQI_CAPABILITIES, &p) == B_FALSE)
133 		return (B_FALSE);
134 
135 	s->s_max_sg_entries = p.mailbox[1];
136 	s->s_max_xfer_size = p.mailbox[2];
137 	s->s_max_outstanding_requests = p.mailbox[3];
138 	s->s_config_table_offset = p.mailbox[4];
139 	s->s_config_table_len = p.mailbox[5];
140 	return (B_TRUE);
141 }
142 
143 boolean_t
144 sis_init_base_struct_addr(pqi_state_t *s)
145 {
146 	sis_base_struct_t	*base;
147 	pqi_dma_overhead_t	*o;
148 	sis_sync_cmd_params_t	params;
149 	boolean_t		rc;
150 	void			*dma_addr;
151 
152 	o = pqi_alloc_single(s, sizeof (*base) + SIS_BASE_STRUCT_ALIGNMENT);
153 	if (o == NULL)
154 		return (B_FALSE);
155 
156 	base = PQIALIGN_TYPED(o->alloc_memory, SIS_BASE_STRUCT_ALIGNMENT,
157 	    sis_base_struct_t *);
158 	base->sb_revision = SIS_BASE_STRUCT_REVISION;
159 	base->sb_error_buffer_paddr_low = (uint32_t)s->s_error_dma->dma_addr;
160 	base->sb_error_buffer_paddr_high =
161 	    (uint32_t)(s->s_error_dma->dma_addr >> 32);
162 	base->sb_error_elements_len = PQI_ERROR_BUFFER_ELEMENT_LENGTH;
163 	base->sb_error_elements_num = s->s_max_outstanding_requests;
164 
165 	dma_addr = PQIALIGN_TYPED(o->dma_addr, SIS_BASE_STRUCT_ALIGNMENT,
166 	    void *);
167 	(void) memset(&params, 0, sizeof (params));
168 	params.mailbox[1] = (uint32_t)(uintptr_t)dma_addr;
169 	params.mailbox[2] = (uint32_t)((uint64_t)((uintptr_t)dma_addr) >> 32);
170 	params.mailbox[3] = sizeof (*base);
171 	(void) ddi_dma_sync(o->handle, 0, 0, DDI_DMA_SYNC_FORDEV);
172 	rc = sis_send_sync_cmd(s, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, &params);
173 
174 	pqi_free_single(s, o);
175 
176 	return (rc);
177 }
178 
179 /*
180  * Support functions for the visible legacy functions
181  */
182 static boolean_t
183 sis_send_sync_cmd(pqi_state_t *s, uint32_t cmd,
184     sis_sync_cmd_params_t *params)
185 {
186 	uint32_t	i;
187 	uint32_t	doorbell;
188 	uint32_t	cmd_status;
189 
190 	/* Write the command to mailbox 0. */
191 	S32(s, sis_mailbox[0], cmd);
192 
193 	/*
194 	 * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used
195 	 * when sending a command to the controller).
196 	 */
197 	for (i = 1; i <= 4; i++)
198 		S32(s, sis_mailbox[i], params->mailbox[i]);
199 
200 	/* Clear the command doorbell. */
201 	S32(s, sis_ctrl_to_host_doorbell_clear,
202 	    SIS_CLEAR_CTRL_TO_HOST_DOORBELL);
203 
204 	/* Disable doorbell interrupts by masking all interrupts. */
205 	S32(s, sis_interrupt_mask, ~0);
206 
207 	/*
208 	 * Force the completion of the interrupt mask register write before
209 	 * submitting the command.
210 	 */
211 	(void) G32(s, sis_interrupt_mask);
212 
213 	/* Submit the command to the controller. */
214 	S32(s, sis_host_to_ctrl_doorbell, SIS_CMD_READY);
215 
216 	/*
217 	 * Poll for command completion.  Note that the call to msleep() is at
218 	 * the top of the loop in order to give the controller time to start
219 	 * processing the command before we start polling.
220 	 */
221 	for (i = 0; i < 10000; i++) {
222 		drv_usecwait(MICROSEC / MILLISEC);
223 		doorbell = G32(s, sis_ctrl_to_host_doorbell);
224 		if (doorbell & SIS_CMD_COMPLETE)
225 			break;
226 	}
227 	if (i == 10000)
228 		return (B_FALSE);
229 
230 	/* Read the command status from mailbox 0. */
231 	cmd_status = G32(s, sis_mailbox[0]);
232 	if (cmd_status != SIS_CMD_STATUS_SUCCESS)
233 		return (B_FALSE);
234 
235 	/*
236 	 * The command completed successfully, so save the command status and
237 	 * read the values returned in mailboxes 1-5.
238 	 */
239 	params->mailbox[0] = cmd_status;
240 	for (i = 1; i < ARRAY_SIZE(params->mailbox); i++)
241 		params->mailbox[i] = G32(s, sis_mailbox[i]);
242 
243 	return (B_TRUE);
244 }
245