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
sis_read_scratch(pqi_state_t * s)54 sis_read_scratch(pqi_state_t *s)
55 {
56 return (G32(s, sis_driver_scratch));
57 }
58
59 void
sis_write_scratch(pqi_state_t * s,int mode)60 sis_write_scratch(pqi_state_t *s, int mode)
61 {
62 S32(s, sis_driver_scratch, mode);
63 }
64
65 boolean_t
sis_reenable_mode(pqi_state_t * s)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
sis_wait_for_ctrl_ready(pqi_state_t * s)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
sis_get_ctrl_props(pqi_state_t * s)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
sis_get_pqi_capabilities(pqi_state_t * s)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
sis_init_base_struct_addr(pqi_state_t * s)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(¶ms, 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, ¶ms);
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
sis_send_sync_cmd(pqi_state_t * s,uint32_t cmd,sis_sync_cmd_params_t * params)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