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