Commit | Line | Data |
---|---|---|
6c223761 KB |
1 | /* |
2 | * driver for Microsemi PQI-based storage controllers | |
b805dbfe | 3 | * Copyright (c) 2016-2017 Microsemi Corporation |
6c223761 KB |
4 | * Copyright (c) 2016 PMC-Sierra, Inc. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; version 2 of the License. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
13 | * NON INFRINGEMENT. See the GNU General Public License for more details. | |
14 | * | |
15 | * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/pci.h> | |
23 | #include <scsi/scsi_device.h> | |
24 | #include <asm/unaligned.h> | |
25 | #include "smartpqi.h" | |
26 | #include "smartpqi_sis.h" | |
27 | ||
28 | /* legacy SIS interface commands */ | |
29 | #define SIS_CMD_GET_ADAPTER_PROPERTIES 0x19 | |
30 | #define SIS_CMD_INIT_BASE_STRUCT_ADDRESS 0x1b | |
31 | #define SIS_CMD_GET_PQI_CAPABILITIES 0x3000 | |
32 | ||
33 | /* for submission of legacy SIS commands */ | |
34 | #define SIS_REENABLE_SIS_MODE 0x1 | |
35 | #define SIS_ENABLE_MSIX 0x40 | |
061ef06a | 36 | #define SIS_ENABLE_INTX 0x80 |
4fd22c13 | 37 | #define SIS_SOFT_RESET 0x100 |
4f078e24 | 38 | #define SIS_CMD_READY 0x200 |
5b0fba0f | 39 | #define SIS_TRIGGER_SHUTDOWN 0x800000 |
336b6819 | 40 | #define SIS_PQI_RESET_QUIESCE 0x1000000 |
4f078e24 | 41 | |
6c223761 KB |
42 | #define SIS_CMD_COMPLETE 0x1000 |
43 | #define SIS_CLEAR_CTRL_TO_HOST_DOORBELL 0x1000 | |
4f078e24 | 44 | |
6c223761 KB |
45 | #define SIS_CMD_STATUS_SUCCESS 0x1 |
46 | #define SIS_CMD_COMPLETE_TIMEOUT_SECS 30 | |
47 | #define SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS 10 | |
48 | ||
49 | /* used with SIS_CMD_GET_ADAPTER_PROPERTIES command */ | |
50 | #define SIS_EXTENDED_PROPERTIES_SUPPORTED 0x800000 | |
51 | #define SIS_SMARTARRAY_FEATURES_SUPPORTED 0x2 | |
52 | #define SIS_PQI_MODE_SUPPORTED 0x4 | |
336b6819 | 53 | #define SIS_PQI_RESET_QUIESCE_SUPPORTED 0x8 |
6c223761 KB |
54 | #define SIS_REQUIRED_EXTENDED_PROPERTIES \ |
55 | (SIS_SMARTARRAY_FEATURES_SUPPORTED | SIS_PQI_MODE_SUPPORTED) | |
56 | ||
57 | /* used with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ | |
58 | #define SIS_BASE_STRUCT_REVISION 9 | |
59 | #define SIS_BASE_STRUCT_ALIGNMENT 16 | |
60 | ||
61 | #define SIS_CTRL_KERNEL_UP 0x80 | |
62 | #define SIS_CTRL_KERNEL_PANIC 0x100 | |
65111785 | 63 | #define SIS_CTRL_READY_TIMEOUT_SECS 180 |
061ef06a | 64 | #define SIS_CTRL_READY_RESUME_TIMEOUT_SECS 90 |
6c223761 KB |
65 | #define SIS_CTRL_READY_POLL_INTERVAL_MSECS 10 |
66 | ||
67 | #pragma pack(1) | |
68 | ||
69 | /* for use with SIS_CMD_INIT_BASE_STRUCT_ADDRESS command */ | |
70 | struct sis_base_struct { | |
71 | __le32 revision; /* revision of this structure */ | |
72 | __le32 flags; /* reserved */ | |
73 | __le32 error_buffer_paddr_low; /* lower 32 bits of physical memory */ | |
74 | /* buffer for PQI error response */ | |
75 | /* data */ | |
76 | __le32 error_buffer_paddr_high; /* upper 32 bits of physical */ | |
77 | /* memory buffer for PQI */ | |
78 | /* error response data */ | |
79 | __le32 error_buffer_element_length; /* length of each PQI error */ | |
80 | /* response buffer element */ | |
81 | /* in bytes */ | |
82 | __le32 error_buffer_num_elements; /* total number of PQI error */ | |
83 | /* response buffers available */ | |
84 | }; | |
85 | ||
86 | #pragma pack() | |
87 | ||
061ef06a KB |
88 | static int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info, |
89 | unsigned int timeout_secs) | |
6c223761 KB |
90 | { |
91 | unsigned long timeout; | |
92 | u32 status; | |
93 | ||
4fd22c13 | 94 | timeout = (timeout_secs * PQI_HZ) + jiffies; |
6c223761 KB |
95 | |
96 | while (1) { | |
97 | status = readl(&ctrl_info->registers->sis_firmware_status); | |
98 | if (status != ~0) { | |
99 | if (status & SIS_CTRL_KERNEL_PANIC) { | |
100 | dev_err(&ctrl_info->pci_dev->dev, | |
101 | "controller is offline: status code 0x%x\n", | |
102 | readl( | |
103 | &ctrl_info->registers->sis_mailbox[7])); | |
104 | return -ENODEV; | |
105 | } | |
106 | if (status & SIS_CTRL_KERNEL_UP) | |
107 | break; | |
108 | } | |
8845fdfa KB |
109 | if (time_after(jiffies, timeout)) { |
110 | dev_err(&ctrl_info->pci_dev->dev, | |
d87d5474 KB |
111 | "controller not ready after %u seconds\n", |
112 | timeout_secs); | |
6c223761 | 113 | return -ETIMEDOUT; |
8845fdfa | 114 | } |
6c223761 KB |
115 | msleep(SIS_CTRL_READY_POLL_INTERVAL_MSECS); |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
061ef06a KB |
121 | int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) |
122 | { | |
123 | return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, | |
124 | SIS_CTRL_READY_TIMEOUT_SECS); | |
125 | } | |
126 | ||
127 | int sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info *ctrl_info) | |
128 | { | |
129 | return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, | |
130 | SIS_CTRL_READY_RESUME_TIMEOUT_SECS); | |
131 | } | |
132 | ||
6c223761 KB |
133 | bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) |
134 | { | |
135 | bool running; | |
136 | u32 status; | |
137 | ||
138 | status = readl(&ctrl_info->registers->sis_firmware_status); | |
139 | ||
140 | if (status & SIS_CTRL_KERNEL_PANIC) | |
141 | running = false; | |
142 | else | |
143 | running = true; | |
144 | ||
145 | if (!running) | |
146 | dev_err(&ctrl_info->pci_dev->dev, | |
147 | "controller is offline: status code 0x%x\n", | |
148 | readl(&ctrl_info->registers->sis_mailbox[7])); | |
149 | ||
150 | return running; | |
151 | } | |
152 | ||
162d7753 KB |
153 | bool sis_is_kernel_up(struct pqi_ctrl_info *ctrl_info) |
154 | { | |
155 | return readl(&ctrl_info->registers->sis_firmware_status) & | |
156 | SIS_CTRL_KERNEL_UP; | |
157 | } | |
158 | ||
6c223761 KB |
159 | /* used for passing command parameters/results when issuing SIS commands */ |
160 | struct sis_sync_cmd_params { | |
161 | u32 mailbox[6]; /* mailboxes 0-5 */ | |
162 | }; | |
163 | ||
164 | static int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info, | |
165 | u32 cmd, struct sis_sync_cmd_params *params) | |
166 | { | |
167 | struct pqi_ctrl_registers __iomem *registers; | |
168 | unsigned int i; | |
169 | unsigned long timeout; | |
170 | u32 doorbell; | |
171 | u32 cmd_status; | |
172 | ||
173 | registers = ctrl_info->registers; | |
174 | ||
175 | /* Write the command to mailbox 0. */ | |
176 | writel(cmd, ®isters->sis_mailbox[0]); | |
177 | ||
178 | /* | |
179 | * Write the command parameters to mailboxes 1-4 (mailbox 5 is not used | |
180 | * when sending a command to the controller). | |
181 | */ | |
182 | for (i = 1; i <= 4; i++) | |
183 | writel(params->mailbox[i], ®isters->sis_mailbox[i]); | |
184 | ||
185 | /* Clear the command doorbell. */ | |
186 | writel(SIS_CLEAR_CTRL_TO_HOST_DOORBELL, | |
187 | ®isters->sis_ctrl_to_host_doorbell_clear); | |
188 | ||
189 | /* Disable doorbell interrupts by masking all interrupts. */ | |
190 | writel(~0, ®isters->sis_interrupt_mask); | |
191 | ||
192 | /* | |
193 | * Force the completion of the interrupt mask register write before | |
194 | * submitting the command. | |
195 | */ | |
196 | readl(®isters->sis_interrupt_mask); | |
197 | ||
198 | /* Submit the command to the controller. */ | |
199 | writel(SIS_CMD_READY, ®isters->sis_host_to_ctrl_doorbell); | |
200 | ||
201 | /* | |
202 | * Poll for command completion. Note that the call to msleep() is at | |
203 | * the top of the loop in order to give the controller time to start | |
204 | * processing the command before we start polling. | |
205 | */ | |
4fd22c13 | 206 | timeout = (SIS_CMD_COMPLETE_TIMEOUT_SECS * PQI_HZ) + jiffies; |
6c223761 KB |
207 | while (1) { |
208 | msleep(SIS_CMD_COMPLETE_POLL_INTERVAL_MSECS); | |
209 | doorbell = readl(®isters->sis_ctrl_to_host_doorbell); | |
210 | if (doorbell & SIS_CMD_COMPLETE) | |
211 | break; | |
212 | if (time_after(jiffies, timeout)) | |
213 | return -ETIMEDOUT; | |
214 | } | |
215 | ||
216 | /* Read the command status from mailbox 0. */ | |
217 | cmd_status = readl(®isters->sis_mailbox[0]); | |
218 | if (cmd_status != SIS_CMD_STATUS_SUCCESS) { | |
219 | dev_err(&ctrl_info->pci_dev->dev, | |
220 | "SIS command failed for command 0x%x: status = 0x%x\n", | |
221 | cmd, cmd_status); | |
222 | return -EINVAL; | |
223 | } | |
224 | ||
225 | /* | |
226 | * The command completed successfully, so save the command status and | |
227 | * read the values returned in mailboxes 1-5. | |
228 | */ | |
229 | params->mailbox[0] = cmd_status; | |
230 | for (i = 1; i < ARRAY_SIZE(params->mailbox); i++) | |
231 | params->mailbox[i] = readl(®isters->sis_mailbox[i]); | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | /* | |
237 | * This function verifies that we are talking to a controller that speaks PQI. | |
238 | */ | |
239 | ||
240 | int sis_get_ctrl_properties(struct pqi_ctrl_info *ctrl_info) | |
241 | { | |
242 | int rc; | |
243 | u32 properties; | |
244 | u32 extended_properties; | |
245 | struct sis_sync_cmd_params params; | |
246 | ||
247 | memset(¶ms, 0, sizeof(params)); | |
248 | ||
249 | rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_ADAPTER_PROPERTIES, | |
250 | ¶ms); | |
251 | if (rc) | |
252 | return rc; | |
253 | ||
254 | properties = params.mailbox[1]; | |
255 | ||
256 | if (!(properties & SIS_EXTENDED_PROPERTIES_SUPPORTED)) | |
257 | return -ENODEV; | |
258 | ||
259 | extended_properties = params.mailbox[4]; | |
260 | ||
261 | if ((extended_properties & SIS_REQUIRED_EXTENDED_PROPERTIES) != | |
262 | SIS_REQUIRED_EXTENDED_PROPERTIES) | |
263 | return -ENODEV; | |
264 | ||
336b6819 KB |
265 | if (extended_properties & SIS_PQI_RESET_QUIESCE_SUPPORTED) |
266 | ctrl_info->pqi_reset_quiesce_supported = true; | |
267 | ||
6c223761 KB |
268 | return 0; |
269 | } | |
270 | ||
271 | int sis_get_pqi_capabilities(struct pqi_ctrl_info *ctrl_info) | |
272 | { | |
273 | int rc; | |
274 | struct sis_sync_cmd_params params; | |
275 | ||
276 | memset(¶ms, 0, sizeof(params)); | |
277 | ||
278 | rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_GET_PQI_CAPABILITIES, | |
279 | ¶ms); | |
280 | if (rc) | |
281 | return rc; | |
282 | ||
283 | ctrl_info->max_sg_entries = params.mailbox[1]; | |
284 | ctrl_info->max_transfer_size = params.mailbox[2]; | |
285 | ctrl_info->max_outstanding_requests = params.mailbox[3]; | |
286 | ctrl_info->config_table_offset = params.mailbox[4]; | |
287 | ctrl_info->config_table_length = params.mailbox[5]; | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | int sis_init_base_struct_addr(struct pqi_ctrl_info *ctrl_info) | |
293 | { | |
294 | int rc; | |
295 | void *base_struct_unaligned; | |
296 | struct sis_base_struct *base_struct; | |
297 | struct sis_sync_cmd_params params; | |
298 | unsigned long error_buffer_paddr; | |
299 | dma_addr_t bus_address; | |
300 | ||
301 | base_struct_unaligned = kzalloc(sizeof(*base_struct) | |
302 | + SIS_BASE_STRUCT_ALIGNMENT - 1, GFP_KERNEL); | |
303 | if (!base_struct_unaligned) | |
304 | return -ENOMEM; | |
305 | ||
306 | base_struct = PTR_ALIGN(base_struct_unaligned, | |
307 | SIS_BASE_STRUCT_ALIGNMENT); | |
308 | error_buffer_paddr = (unsigned long)ctrl_info->error_buffer_dma_handle; | |
309 | ||
310 | put_unaligned_le32(SIS_BASE_STRUCT_REVISION, &base_struct->revision); | |
311 | put_unaligned_le32(lower_32_bits(error_buffer_paddr), | |
312 | &base_struct->error_buffer_paddr_low); | |
313 | put_unaligned_le32(upper_32_bits(error_buffer_paddr), | |
314 | &base_struct->error_buffer_paddr_high); | |
315 | put_unaligned_le32(PQI_ERROR_BUFFER_ELEMENT_LENGTH, | |
316 | &base_struct->error_buffer_element_length); | |
317 | put_unaligned_le32(ctrl_info->max_io_slots, | |
318 | &base_struct->error_buffer_num_elements); | |
319 | ||
6917a9cc CH |
320 | bus_address = dma_map_single(&ctrl_info->pci_dev->dev, base_struct, |
321 | sizeof(*base_struct), DMA_TO_DEVICE); | |
322 | if (dma_mapping_error(&ctrl_info->pci_dev->dev, bus_address)) { | |
6c223761 KB |
323 | rc = -ENOMEM; |
324 | goto out; | |
325 | } | |
326 | ||
327 | memset(¶ms, 0, sizeof(params)); | |
328 | params.mailbox[1] = lower_32_bits((u64)bus_address); | |
329 | params.mailbox[2] = upper_32_bits((u64)bus_address); | |
330 | params.mailbox[3] = sizeof(*base_struct); | |
331 | ||
332 | rc = sis_send_sync_cmd(ctrl_info, SIS_CMD_INIT_BASE_STRUCT_ADDRESS, | |
333 | ¶ms); | |
334 | ||
6917a9cc CH |
335 | dma_unmap_single(&ctrl_info->pci_dev->dev, bus_address, |
336 | sizeof(*base_struct), DMA_TO_DEVICE); | |
6c223761 KB |
337 | out: |
338 | kfree(base_struct_unaligned); | |
339 | ||
340 | return rc; | |
341 | } | |
342 | ||
061ef06a KB |
343 | #define SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS 30 |
344 | ||
336b6819 | 345 | static int sis_wait_for_doorbell_bit_to_clear( |
061ef06a KB |
346 | struct pqi_ctrl_info *ctrl_info, u32 bit) |
347 | { | |
336b6819 | 348 | int rc = 0; |
061ef06a KB |
349 | u32 doorbell_register; |
350 | unsigned long timeout; | |
351 | ||
4fd22c13 | 352 | timeout = (SIS_DOORBELL_BIT_CLEAR_TIMEOUT_SECS * PQI_HZ) + jiffies; |
061ef06a KB |
353 | |
354 | while (1) { | |
355 | doorbell_register = | |
356 | readl(&ctrl_info->registers->sis_host_to_ctrl_doorbell); | |
357 | if ((doorbell_register & bit) == 0) | |
358 | break; | |
359 | if (readl(&ctrl_info->registers->sis_firmware_status) & | |
336b6819 KB |
360 | SIS_CTRL_KERNEL_PANIC) { |
361 | rc = -ENODEV; | |
061ef06a | 362 | break; |
336b6819 | 363 | } |
061ef06a KB |
364 | if (time_after(jiffies, timeout)) { |
365 | dev_err(&ctrl_info->pci_dev->dev, | |
366 | "doorbell register bit 0x%x not cleared\n", | |
367 | bit); | |
336b6819 | 368 | rc = -ETIMEDOUT; |
061ef06a KB |
369 | break; |
370 | } | |
371 | usleep_range(1000, 2000); | |
372 | } | |
336b6819 KB |
373 | |
374 | return rc; | |
061ef06a KB |
375 | } |
376 | ||
4f078e24 | 377 | static inline int sis_set_doorbell_bit(struct pqi_ctrl_info *ctrl_info, u32 bit) |
6c223761 | 378 | { |
4f078e24 | 379 | writel(bit, &ctrl_info->registers->sis_host_to_ctrl_doorbell); |
6c223761 | 380 | |
4f078e24 | 381 | return sis_wait_for_doorbell_bit_to_clear(ctrl_info, bit); |
6c223761 KB |
382 | } |
383 | ||
4f078e24 | 384 | void sis_enable_msix(struct pqi_ctrl_info *ctrl_info) |
6c223761 | 385 | { |
4f078e24 | 386 | sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_MSIX); |
6c223761 KB |
387 | } |
388 | ||
061ef06a KB |
389 | void sis_enable_intx(struct pqi_ctrl_info *ctrl_info) |
390 | { | |
4f078e24 | 391 | sis_set_doorbell_bit(ctrl_info, SIS_ENABLE_INTX); |
6c223761 KB |
392 | } |
393 | ||
5b0fba0f KB |
394 | void sis_shutdown_ctrl(struct pqi_ctrl_info *ctrl_info) |
395 | { | |
98f87667 KB |
396 | if (readl(&ctrl_info->registers->sis_firmware_status) & |
397 | SIS_CTRL_KERNEL_PANIC) | |
398 | return; | |
399 | ||
5b0fba0f KB |
400 | writel(SIS_TRIGGER_SHUTDOWN, |
401 | &ctrl_info->registers->sis_host_to_ctrl_doorbell); | |
402 | } | |
403 | ||
336b6819 KB |
404 | int sis_pqi_reset_quiesce(struct pqi_ctrl_info *ctrl_info) |
405 | { | |
4f078e24 | 406 | return sis_set_doorbell_bit(ctrl_info, SIS_PQI_RESET_QUIESCE); |
336b6819 KB |
407 | } |
408 | ||
6c223761 KB |
409 | int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) |
410 | { | |
4f078e24 | 411 | return sis_set_doorbell_bit(ctrl_info, SIS_REENABLE_SIS_MODE); |
6c223761 KB |
412 | } |
413 | ||
ff6abb73 KB |
414 | void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value) |
415 | { | |
416 | writel(value, &ctrl_info->registers->sis_driver_scratch); | |
417 | } | |
418 | ||
419 | u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info) | |
420 | { | |
421 | return readl(&ctrl_info->registers->sis_driver_scratch); | |
422 | } | |
423 | ||
4fd22c13 MR |
424 | void sis_soft_reset(struct pqi_ctrl_info *ctrl_info) |
425 | { | |
426 | writel(SIS_SOFT_RESET, | |
427 | &ctrl_info->registers->sis_host_to_ctrl_doorbell); | |
428 | } | |
429 | ||
6c223761 KB |
430 | static void __attribute__((unused)) verify_structures(void) |
431 | { | |
432 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
433 | revision) != 0x0); | |
434 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
435 | flags) != 0x4); | |
436 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
437 | error_buffer_paddr_low) != 0x8); | |
438 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
439 | error_buffer_paddr_high) != 0xc); | |
440 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
441 | error_buffer_element_length) != 0x10); | |
442 | BUILD_BUG_ON(offsetof(struct sis_base_struct, | |
443 | error_buffer_num_elements) != 0x14); | |
444 | BUILD_BUG_ON(sizeof(struct sis_base_struct) != 0x18); | |
445 | } |