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 #pragma pack(1)
34 /* used for passing command parameters/results when issuing SIS commands */
35 typedef struct sis_sync_cmd_params {
36 uint32_t mailbox[6]; /* mailboxes 0-5 */
37 } sis_sync_cmd_params_t;
38
39 #define SIS_BASE_STRUCT_REVISION 9
40
41 typedef struct sis_base_struct {
42 uint32_t sb_revision;
43 uint32_t sb_flags;
44 uint32_t sb_error_buffer_paddr_low;
45 uint32_t sb_error_buffer_paddr_high;
46 uint32_t sb_error_elements_len;
47 uint32_t sb_error_elements_num;
48 } sis_base_struct_t;
49 #pragma pack()
50
51 /* ---- Forward declaration for support functions ---- */
52 static boolean_t sis_send_sync_cmd(pqi_state_t s, uint32_t cmd,
53 sis_sync_cmd_params_t *params);
54
55 uint32_t
sis_read_scratch(pqi_state_t s)56 sis_read_scratch(pqi_state_t s)
57 {
58 return (G32(s, sis_driver_scratch));
59 }
60
61 void
sis_write_scratch(pqi_state_t s,int mode)62 sis_write_scratch(pqi_state_t s, int mode)
63 {
64 S32(s, sis_driver_scratch, mode);
65 }
66
67 boolean_t
sis_reenable_mode(pqi_state_t s)68 sis_reenable_mode(pqi_state_t s)
69 {
70 int loop_count;
71 uint32_t doorbell;
72
73 S32(s, sis_host_to_ctrl_doorbell, SIS_REENABLE_SIS_MODE);
74
75 for (loop_count = 0; loop_count < 1000; loop_count++) {
76 doorbell = G32(s, sis_ctrl_to_host_doorbell);
77 if ((doorbell & SIS_REENABLE_SIS_MODE) == 0) {
78 return (B_TRUE);
79 }
80 drv_usecwait(1000); /* ---- Wait 1ms ---- */
81 }
82 return (B_FALSE);
83 }
84
85 boolean_t
sis_wait_for_ctrl_ready(pqi_state_t s)86 sis_wait_for_ctrl_ready(pqi_state_t s)
87 {
88 int loop_count;
89 uint32_t status;
90
91 for (loop_count = 0; loop_count < 1000; loop_count++) {
92 status = G32(s, sis_firmware_status);
93 if (status & SIS_CTRL_KERNEL_PANIC)
94 return (B_FALSE);
95 if (status & SIS_CTRL_KERNEL_UP)
96 return (B_TRUE);
97 drv_usecwait(1000); /* ---- Wait 1ms ---- */
98 }
99 return (B_FALSE);
100 }
101
102 /*
103 * sis_get_ctrl_props -- Verify we're talking to controller that speaks PQI
104 */
105 boolean_t
sis_get_ctrl_props(pqi_state_t s)106 sis_get_ctrl_props(pqi_state_t s)
107 {
108 sis_sync_cmd_params_t p;
109 uint32_t property;
110 uint32_t extended_property;
111
112 (void) memset(&p, 0, sizeof (p));
113 if (sis_send_sync_cmd(s, SIS_CMD_GET_ADAPTER_PROPERTIES, &p) == B_FALSE)
114 return (B_FALSE);
115
116 property = p.mailbox[1];
117 if (!(property & SIS_EXTENDED_PROPERTIES_SUPPORTED))
118 return (B_FALSE);
119
120 extended_property = p.mailbox[4];
121 if ((extended_property & SIS_REQUIRED_EXTENDED_PROPERTIES) !=
122 SIS_REQUIRED_EXTENDED_PROPERTIES)
123 return (B_FALSE);
124
125 return (B_TRUE);
126 }
127
128 boolean_t
sis_get_pqi_capabilities(pqi_state_t s)129 sis_get_pqi_capabilities(pqi_state_t s)
130 {
131 sis_sync_cmd_params_t p;
132
133 (void) memset(&p, 0, sizeof (p));
134 if (sis_send_sync_cmd(s, SIS_CMD_GET_PQI_CAPABILITIES, &p) == B_FALSE)
135 return (B_FALSE);
136
137 s->s_max_sg_entries = p.mailbox[1];
138 s->s_max_xfer_size = p.mailbox[2];
139 s->s_max_outstanding_requests = p.mailbox[3];
140 s->s_config_table_offset = p.mailbox[4];
141 s->s_config_table_len = p.mailbox[5];
142 return (B_TRUE);
143 }
144
145 #define SIS_ROUNDUP(a, align, type) \
146 (type)((((uintptr_t)(a) + align - 1) & ~align))
147
148 boolean_t
sis_init_base_struct_addr(pqi_state_t s)149 sis_init_base_struct_addr(pqi_state_t s)
150 {
151 sis_base_struct_t *base;
152 pqi_dma_overhead_t *o;
153 sis_sync_cmd_params_t params;
154 boolean_t rc;
155 void *dma_addr;
156
157 o = pqi_alloc_single(s, sizeof (*base) + SIS_BASE_STRUCT_ALIGNMENT);
158 if (o == NULL)
159 return (B_FALSE);
160
161 base = PQIALIGN_TYPED(o->alloc_memory, SIS_BASE_STRUCT_ALIGNMENT,
162 sis_base_struct_t *);
163 base->sb_revision = SIS_BASE_STRUCT_REVISION;
164 base->sb_error_buffer_paddr_low = (uint32_t)s->s_error_dma->dma_addr;
165 base->sb_error_buffer_paddr_high =
166 (uint32_t)(s->s_error_dma->dma_addr >> 32);
167 base->sb_error_elements_len = PQI_ERROR_BUFFER_ELEMENT_LENGTH;
168 base->sb_error_elements_num = s->s_max_outstanding_requests;
169
170 dma_addr = PQIALIGN_TYPED(o->dma_addr, SIS_BASE_STRUCT_ALIGNMENT,
171 void *);
172 (void) memset(¶ms, 0, sizeof (params));
173 params.mailbox[1] = (uint32_t)(intptr_t)dma_addr;
174 params.mailbox[2] = (uint32_t)((uint64_t)((intptr_t)dma_addr) >> 32);
175 params.mailbox[3] = sizeof (*base);
176 (void) ddi_dma_sync(o->handle, 0, 0, DDI_DMA_SYNC_FORDEV);
177 rc = sis_send_sync_cmd(s, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, ¶ms);
178
179 pqi_free_single(s, o);
180
181 return (rc);
182 }
183
184 /*
185 * []------------------------------------------------------------[]
186 * | Support functions for the visable legacy fucntions |
187 * []------------------------------------------------------------[]
188 */
189 static boolean_t
sis_send_sync_cmd(pqi_state_t s,uint32_t cmd,sis_sync_cmd_params_t * params)190 sis_send_sync_cmd(pqi_state_t s, uint32_t cmd,
191 sis_sync_cmd_params_t *params)
192 {
193 uint32_t i;
194 uint32_t doorbell;
195 uint32_t cmd_status;
196
197 /* Write the command to mailbox 0. */
198 S32(s, sis_mailbox[0], cmd);
199
200 /*
201 * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used
202 * when sending a command to the controller).
203 */
204 for (i = 1; i <= 4; i++)
205 S32(s, sis_mailbox[i], params->mailbox[i]);
206
207 /* Clear the command doorbell. */
208 S32(s, sis_ctrl_to_host_doorbell_clear,
209 SIS_CLEAR_CTRL_TO_HOST_DOORBELL);
210
211 /* Disable doorbell interrupts by masking all interrupts. */
212 S32(s, sis_interrupt_mask, ~0);
213
214 /*
215 * Force the completion of the interrupt mask register write before
216 * submitting the command.
217 */
218 (void) G32(s, sis_interrupt_mask);
219
220 /* Submit the command to the controller. */
221 S32(s, sis_host_to_ctrl_doorbell, SIS_CMD_READY);
222
223 /*
224 * Poll for command completion. Note that the call to msleep() is at
225 * the top of the loop in order to give the controller time to start
226 * processing the command before we start polling.
227 */
228 for (i = 0; i < 10000; i++) {
229 drv_usecwait(1000);
230 doorbell = G32(s, sis_ctrl_to_host_doorbell);
231 if (doorbell & SIS_CMD_COMPLETE)
232 break;
233 }
234 if (i == 10000)
235 return (B_FALSE);
236
237 /* Read the command status from mailbox 0. */
238 cmd_status = G32(s, sis_mailbox[0]);
239 if (cmd_status != SIS_CMD_STATUS_SUCCESS)
240 return (B_FALSE);
241
242 /*
243 * The command completed successfully, so save the command status and
244 * read the values returned in mailboxes 1-5.
245 */
246 params->mailbox[0] = cmd_status;
247 for (i = 1; i < ARRAY_SIZE(params->mailbox); i++)
248 params->mailbox[i] = G32(s, sis_mailbox[i]);
249
250 return (B_TRUE);
251 }
252