diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_nx2.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx2.c | 3716 |
1 files changed, 3716 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c new file mode 100644 index 000000000000..8164cc9e7286 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_nx2.c | |||
@@ -0,0 +1,3716 @@ | |||
1 | /* | ||
2 | * QLogic Fibre Channel HBA Driver | ||
3 | * Copyright (c) 2003-2013 QLogic Corporation | ||
4 | * | ||
5 | * See LICENSE.qla2xxx for copyright and licensing details. | ||
6 | */ | ||
7 | |||
8 | #include <linux/vmalloc.h> | ||
9 | |||
10 | #include "qla_def.h" | ||
11 | #include "qla_gbl.h" | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | |||
15 | /* 8044 Flash Read/Write functions */ | ||
16 | uint32_t | ||
17 | qla8044_rd_reg(struct qla_hw_data *ha, ulong addr) | ||
18 | { | ||
19 | return readl((void __iomem *) (ha->nx_pcibase + addr)); | ||
20 | } | ||
21 | |||
22 | void | ||
23 | qla8044_wr_reg(struct qla_hw_data *ha, ulong addr, uint32_t val) | ||
24 | { | ||
25 | writel(val, (void __iomem *)((ha)->nx_pcibase + addr)); | ||
26 | } | ||
27 | |||
28 | int | ||
29 | qla8044_rd_direct(struct scsi_qla_host *vha, | ||
30 | const uint32_t crb_reg) | ||
31 | { | ||
32 | struct qla_hw_data *ha = vha->hw; | ||
33 | |||
34 | if (crb_reg < CRB_REG_INDEX_MAX) | ||
35 | return qla8044_rd_reg(ha, qla8044_reg_tbl[crb_reg]); | ||
36 | else | ||
37 | return QLA_FUNCTION_FAILED; | ||
38 | } | ||
39 | |||
40 | void | ||
41 | qla8044_wr_direct(struct scsi_qla_host *vha, | ||
42 | const uint32_t crb_reg, | ||
43 | const uint32_t value) | ||
44 | { | ||
45 | struct qla_hw_data *ha = vha->hw; | ||
46 | |||
47 | if (crb_reg < CRB_REG_INDEX_MAX) | ||
48 | qla8044_wr_reg(ha, qla8044_reg_tbl[crb_reg], value); | ||
49 | } | ||
50 | |||
51 | static int | ||
52 | qla8044_set_win_base(scsi_qla_host_t *vha, uint32_t addr) | ||
53 | { | ||
54 | uint32_t val; | ||
55 | int ret_val = QLA_SUCCESS; | ||
56 | struct qla_hw_data *ha = vha->hw; | ||
57 | |||
58 | qla8044_wr_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum), addr); | ||
59 | val = qla8044_rd_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum)); | ||
60 | |||
61 | if (val != addr) { | ||
62 | ql_log(ql_log_warn, vha, 0xb087, | ||
63 | "%s: Failed to set register window : " | ||
64 | "addr written 0x%x, read 0x%x!\n", | ||
65 | __func__, addr, val); | ||
66 | ret_val = QLA_FUNCTION_FAILED; | ||
67 | } | ||
68 | return ret_val; | ||
69 | } | ||
70 | |||
71 | static int | ||
72 | qla8044_rd_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data) | ||
73 | { | ||
74 | int ret_val = QLA_SUCCESS; | ||
75 | struct qla_hw_data *ha = vha->hw; | ||
76 | |||
77 | ret_val = qla8044_set_win_base(vha, addr); | ||
78 | if (!ret_val) | ||
79 | *data = qla8044_rd_reg(ha, QLA8044_WILDCARD); | ||
80 | else | ||
81 | ql_log(ql_log_warn, vha, 0xb088, | ||
82 | "%s: failed read of addr 0x%x!\n", __func__, addr); | ||
83 | return ret_val; | ||
84 | } | ||
85 | |||
86 | static int | ||
87 | qla8044_wr_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t data) | ||
88 | { | ||
89 | int ret_val = QLA_SUCCESS; | ||
90 | struct qla_hw_data *ha = vha->hw; | ||
91 | |||
92 | ret_val = qla8044_set_win_base(vha, addr); | ||
93 | if (!ret_val) | ||
94 | qla8044_wr_reg(ha, QLA8044_WILDCARD, data); | ||
95 | else | ||
96 | ql_log(ql_log_warn, vha, 0xb089, | ||
97 | "%s: failed wrt to addr 0x%x, data 0x%x\n", | ||
98 | __func__, addr, data); | ||
99 | return ret_val; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * qla8044_read_write_crb_reg - Read from raddr and write value to waddr. | ||
104 | * | ||
105 | * @ha : Pointer to adapter structure | ||
106 | * @raddr : CRB address to read from | ||
107 | * @waddr : CRB address to write to | ||
108 | * | ||
109 | */ | ||
110 | static void | ||
111 | qla8044_read_write_crb_reg(struct scsi_qla_host *vha, | ||
112 | uint32_t raddr, uint32_t waddr) | ||
113 | { | ||
114 | uint32_t value; | ||
115 | |||
116 | qla8044_rd_reg_indirect(vha, raddr, &value); | ||
117 | qla8044_wr_reg_indirect(vha, waddr, value); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * qla8044_rmw_crb_reg - Read value from raddr, AND with test_mask, | ||
122 | * Shift Left,Right/OR/XOR with values RMW header and write value to waddr. | ||
123 | * | ||
124 | * @vha : Pointer to adapter structure | ||
125 | * @raddr : CRB address to read from | ||
126 | * @waddr : CRB address to write to | ||
127 | * @p_rmw_hdr : header with shift/or/xor values. | ||
128 | * | ||
129 | */ | ||
130 | static void | ||
131 | qla8044_rmw_crb_reg(struct scsi_qla_host *vha, | ||
132 | uint32_t raddr, uint32_t waddr, struct qla8044_rmw *p_rmw_hdr) | ||
133 | { | ||
134 | uint32_t value; | ||
135 | |||
136 | if (p_rmw_hdr->index_a) | ||
137 | value = vha->reset_tmplt.array[p_rmw_hdr->index_a]; | ||
138 | else | ||
139 | qla8044_rd_reg_indirect(vha, raddr, &value); | ||
140 | value &= p_rmw_hdr->test_mask; | ||
141 | value <<= p_rmw_hdr->shl; | ||
142 | value >>= p_rmw_hdr->shr; | ||
143 | value |= p_rmw_hdr->or_value; | ||
144 | value ^= p_rmw_hdr->xor_value; | ||
145 | qla8044_wr_reg_indirect(vha, waddr, value); | ||
146 | return; | ||
147 | } | ||
148 | |||
149 | inline void | ||
150 | qla8044_set_qsnt_ready(struct scsi_qla_host *vha) | ||
151 | { | ||
152 | uint32_t qsnt_state; | ||
153 | struct qla_hw_data *ha = vha->hw; | ||
154 | |||
155 | qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
156 | qsnt_state |= (1 << ha->portnum); | ||
157 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state); | ||
158 | ql_log(ql_log_info, vha, 0xb08e, "%s(%ld): qsnt_state: 0x%08x\n", | ||
159 | __func__, vha->host_no, qsnt_state); | ||
160 | } | ||
161 | |||
162 | void | ||
163 | qla8044_clear_qsnt_ready(struct scsi_qla_host *vha) | ||
164 | { | ||
165 | uint32_t qsnt_state; | ||
166 | struct qla_hw_data *ha = vha->hw; | ||
167 | |||
168 | qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
169 | qsnt_state &= ~(1 << ha->portnum); | ||
170 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state); | ||
171 | ql_log(ql_log_info, vha, 0xb08f, "%s(%ld): qsnt_state: 0x%08x\n", | ||
172 | __func__, vha->host_no, qsnt_state); | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * | ||
177 | * qla8044_lock_recovery - Recovers the idc_lock. | ||
178 | * @ha : Pointer to adapter structure | ||
179 | * | ||
180 | * Lock Recovery Register | ||
181 | * 5-2 Lock recovery owner: Function ID of driver doing lock recovery, | ||
182 | * valid if bits 1..0 are set by driver doing lock recovery. | ||
183 | * 1-0 1 - Driver intends to force unlock the IDC lock. | ||
184 | * 2 - Driver is moving forward to unlock the IDC lock. Driver clears | ||
185 | * this field after force unlocking the IDC lock. | ||
186 | * | ||
187 | * Lock Recovery process | ||
188 | * a. Read the IDC_LOCK_RECOVERY register. If the value in bits 1..0 is | ||
189 | * greater than 0, then wait for the other driver to unlock otherwise | ||
190 | * move to the next step. | ||
191 | * b. Indicate intent to force-unlock by writing 1h to the IDC_LOCK_RECOVERY | ||
192 | * register bits 1..0 and also set the function# in bits 5..2. | ||
193 | * c. Read the IDC_LOCK_RECOVERY register again after a delay of 200ms. | ||
194 | * Wait for the other driver to perform lock recovery if the function | ||
195 | * number in bits 5..2 has changed, otherwise move to the next step. | ||
196 | * d. Write a value of 2h to the IDC_LOCK_RECOVERY register bits 1..0 | ||
197 | * leaving your function# in bits 5..2. | ||
198 | * e. Force unlock using the DRIVER_UNLOCK register and immediately clear | ||
199 | * the IDC_LOCK_RECOVERY bits 5..0 by writing 0. | ||
200 | **/ | ||
201 | static int | ||
202 | qla8044_lock_recovery(struct scsi_qla_host *vha) | ||
203 | { | ||
204 | uint32_t lock = 0, lockid; | ||
205 | struct qla_hw_data *ha = vha->hw; | ||
206 | |||
207 | lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY); | ||
208 | |||
209 | /* Check for other Recovery in progress, go wait */ | ||
210 | if ((lockid & IDC_LOCK_RECOVERY_STATE_MASK) != 0) | ||
211 | return QLA_FUNCTION_FAILED; | ||
212 | |||
213 | /* Intent to Recover */ | ||
214 | qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, | ||
215 | (ha->portnum << | ||
216 | IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) | INTENT_TO_RECOVER); | ||
217 | msleep(200); | ||
218 | |||
219 | /* Check Intent to Recover is advertised */ | ||
220 | lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY); | ||
221 | if ((lockid & IDC_LOCK_RECOVERY_OWNER_MASK) != (ha->portnum << | ||
222 | IDC_LOCK_RECOVERY_STATE_SHIFT_BITS)) | ||
223 | return QLA_FUNCTION_FAILED; | ||
224 | |||
225 | ql_dbg(ql_dbg_p3p, vha, 0xb08B, "%s:%d: IDC Lock recovery initiated\n" | ||
226 | , __func__, ha->portnum); | ||
227 | |||
228 | /* Proceed to Recover */ | ||
229 | qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, | ||
230 | (ha->portnum << IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) | | ||
231 | PROCEED_TO_RECOVER); | ||
232 | |||
233 | /* Force Unlock() */ | ||
234 | qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, 0xFF); | ||
235 | qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK); | ||
236 | |||
237 | /* Clear bits 0-5 in IDC_RECOVERY register*/ | ||
238 | qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, 0); | ||
239 | |||
240 | /* Get lock() */ | ||
241 | lock = qla8044_rd_reg(ha, QLA8044_DRV_LOCK); | ||
242 | if (lock) { | ||
243 | lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); | ||
244 | lockid = ((lockid + (1 << 8)) & ~0xFF) | ha->portnum; | ||
245 | qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lockid); | ||
246 | return QLA_SUCCESS; | ||
247 | } else | ||
248 | return QLA_FUNCTION_FAILED; | ||
249 | } | ||
250 | |||
251 | int | ||
252 | qla8044_idc_lock(struct qla_hw_data *ha) | ||
253 | { | ||
254 | uint32_t ret_val = QLA_SUCCESS, timeout = 0, status = 0; | ||
255 | uint32_t lock_id, lock_cnt, func_num, tmo_owner = 0, first_owner = 0; | ||
256 | scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); | ||
257 | |||
258 | while (status == 0) { | ||
259 | /* acquire semaphore5 from PCI HW block */ | ||
260 | status = qla8044_rd_reg(ha, QLA8044_DRV_LOCK); | ||
261 | |||
262 | if (status) { | ||
263 | /* Increment Counter (8-31) and update func_num (0-7) on | ||
264 | * getting a successful lock */ | ||
265 | lock_id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); | ||
266 | lock_id = ((lock_id + (1 << 8)) & ~0xFF) | ha->portnum; | ||
267 | qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lock_id); | ||
268 | break; | ||
269 | } | ||
270 | |||
271 | if (timeout == 0) | ||
272 | first_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); | ||
273 | |||
274 | if (++timeout >= | ||
275 | (QLA8044_DRV_LOCK_TIMEOUT / QLA8044_DRV_LOCK_MSLEEP)) { | ||
276 | tmo_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); | ||
277 | func_num = tmo_owner & 0xFF; | ||
278 | lock_cnt = tmo_owner >> 8; | ||
279 | ql_log(ql_log_warn, vha, 0xb114, | ||
280 | "%s: Lock by func %d failed after 2s, lock held " | ||
281 | "by func %d, lock count %d, first_owner %d\n", | ||
282 | __func__, ha->portnum, func_num, lock_cnt, | ||
283 | (first_owner & 0xFF)); | ||
284 | if (first_owner != tmo_owner) { | ||
285 | /* Some other driver got lock, | ||
286 | * OR same driver got lock again (counter | ||
287 | * value changed), when we were waiting for | ||
288 | * lock. Retry for another 2 sec */ | ||
289 | ql_dbg(ql_dbg_p3p, vha, 0xb115, | ||
290 | "%s: %d: IDC lock failed\n", | ||
291 | __func__, ha->portnum); | ||
292 | timeout = 0; | ||
293 | } else { | ||
294 | /* Same driver holding lock > 2sec. | ||
295 | * Force Recovery */ | ||
296 | if (qla8044_lock_recovery(vha) == QLA_SUCCESS) { | ||
297 | /* Recovered and got lock */ | ||
298 | ret_val = QLA_SUCCESS; | ||
299 | ql_dbg(ql_dbg_p3p, vha, 0xb116, | ||
300 | "%s:IDC lock Recovery by %d" | ||
301 | "successful...\n", __func__, | ||
302 | ha->portnum); | ||
303 | } | ||
304 | /* Recovery Failed, some other function | ||
305 | * has the lock, wait for 2secs | ||
306 | * and retry | ||
307 | */ | ||
308 | ql_dbg(ql_dbg_p3p, vha, 0xb08a, | ||
309 | "%s: IDC lock Recovery by %d " | ||
310 | "failed, Retrying timout\n", __func__, | ||
311 | ha->portnum); | ||
312 | timeout = 0; | ||
313 | } | ||
314 | } | ||
315 | msleep(QLA8044_DRV_LOCK_MSLEEP); | ||
316 | } | ||
317 | return ret_val; | ||
318 | } | ||
319 | |||
320 | void | ||
321 | qla8044_idc_unlock(struct qla_hw_data *ha) | ||
322 | { | ||
323 | int id; | ||
324 | scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); | ||
325 | |||
326 | id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); | ||
327 | |||
328 | if ((id & 0xFF) != ha->portnum) { | ||
329 | ql_log(ql_log_warn, vha, 0xb118, | ||
330 | "%s: IDC Unlock by %d failed, lock owner is %d!\n", | ||
331 | __func__, ha->portnum, (id & 0xFF)); | ||
332 | return; | ||
333 | } | ||
334 | |||
335 | /* Keep lock counter value, update the ha->func_num to 0xFF */ | ||
336 | qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, (id | 0xFF)); | ||
337 | qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK); | ||
338 | } | ||
339 | |||
340 | /* 8044 Flash Lock/Unlock functions */ | ||
341 | static int | ||
342 | qla8044_flash_lock(scsi_qla_host_t *vha) | ||
343 | { | ||
344 | int lock_owner; | ||
345 | int timeout = 0; | ||
346 | uint32_t lock_status = 0; | ||
347 | int ret_val = QLA_SUCCESS; | ||
348 | struct qla_hw_data *ha = vha->hw; | ||
349 | |||
350 | while (lock_status == 0) { | ||
351 | lock_status = qla8044_rd_reg(ha, QLA8044_FLASH_LOCK); | ||
352 | if (lock_status) | ||
353 | break; | ||
354 | |||
355 | if (++timeout >= QLA8044_FLASH_LOCK_TIMEOUT / 20) { | ||
356 | lock_owner = qla8044_rd_reg(ha, | ||
357 | QLA8044_FLASH_LOCK_ID); | ||
358 | ql_log(ql_log_warn, vha, 0xb113, | ||
359 | "%s: flash lock by %d failed, held by %d\n", | ||
360 | __func__, ha->portnum, lock_owner); | ||
361 | ret_val = QLA_FUNCTION_FAILED; | ||
362 | break; | ||
363 | } | ||
364 | msleep(20); | ||
365 | } | ||
366 | qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, ha->portnum); | ||
367 | return ret_val; | ||
368 | } | ||
369 | |||
370 | static void | ||
371 | qla8044_flash_unlock(scsi_qla_host_t *vha) | ||
372 | { | ||
373 | int ret_val; | ||
374 | struct qla_hw_data *ha = vha->hw; | ||
375 | |||
376 | /* Reading FLASH_UNLOCK register unlocks the Flash */ | ||
377 | qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, 0xFF); | ||
378 | ret_val = qla8044_rd_reg(ha, QLA8044_FLASH_UNLOCK); | ||
379 | } | ||
380 | |||
381 | |||
382 | static | ||
383 | void qla8044_flash_lock_recovery(struct scsi_qla_host *vha) | ||
384 | { | ||
385 | |||
386 | if (qla8044_flash_lock(vha)) { | ||
387 | /* Someone else is holding the lock. */ | ||
388 | ql_log(ql_log_warn, vha, 0xb120, "Resetting flash_lock\n"); | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * Either we got the lock, or someone | ||
393 | * else died while holding it. | ||
394 | * In either case, unlock. | ||
395 | */ | ||
396 | qla8044_flash_unlock(vha); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Address and length are byte address | ||
401 | */ | ||
402 | static int | ||
403 | qla8044_read_flash_data(scsi_qla_host_t *vha, uint8_t *p_data, | ||
404 | uint32_t flash_addr, int u32_word_count) | ||
405 | { | ||
406 | int i, ret_val = QLA_SUCCESS; | ||
407 | uint32_t u32_word; | ||
408 | |||
409 | if (qla8044_flash_lock(vha) != QLA_SUCCESS) { | ||
410 | ret_val = QLA_FUNCTION_FAILED; | ||
411 | goto exit_lock_error; | ||
412 | } | ||
413 | |||
414 | if (flash_addr & 0x03) { | ||
415 | ql_log(ql_log_warn, vha, 0xb117, | ||
416 | "%s: Illegal addr = 0x%x\n", __func__, flash_addr); | ||
417 | ret_val = QLA_FUNCTION_FAILED; | ||
418 | goto exit_flash_read; | ||
419 | } | ||
420 | |||
421 | for (i = 0; i < u32_word_count; i++) { | ||
422 | if (qla8044_wr_reg_indirect(vha, QLA8044_FLASH_DIRECT_WINDOW, | ||
423 | (flash_addr & 0xFFFF0000))) { | ||
424 | ql_log(ql_log_warn, vha, 0xb119, | ||
425 | "%s: failed to write addr 0x%x to " | ||
426 | "FLASH_DIRECT_WINDOW\n! ", | ||
427 | __func__, flash_addr); | ||
428 | ret_val = QLA_FUNCTION_FAILED; | ||
429 | goto exit_flash_read; | ||
430 | } | ||
431 | |||
432 | ret_val = qla8044_rd_reg_indirect(vha, | ||
433 | QLA8044_FLASH_DIRECT_DATA(flash_addr), | ||
434 | &u32_word); | ||
435 | if (ret_val != QLA_SUCCESS) { | ||
436 | ql_log(ql_log_warn, vha, 0xb08c, | ||
437 | "%s: failed to read addr 0x%x!\n", | ||
438 | __func__, flash_addr); | ||
439 | goto exit_flash_read; | ||
440 | } | ||
441 | |||
442 | *(uint32_t *)p_data = u32_word; | ||
443 | p_data = p_data + 4; | ||
444 | flash_addr = flash_addr + 4; | ||
445 | } | ||
446 | |||
447 | exit_flash_read: | ||
448 | qla8044_flash_unlock(vha); | ||
449 | |||
450 | exit_lock_error: | ||
451 | return ret_val; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Address and length are byte address | ||
456 | */ | ||
457 | uint8_t * | ||
458 | qla8044_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, | ||
459 | uint32_t offset, uint32_t length) | ||
460 | { | ||
461 | scsi_block_requests(vha->host); | ||
462 | if (qla8044_read_flash_data(vha, (uint8_t *)buf, offset, length / 4) | ||
463 | != QLA_SUCCESS) { | ||
464 | ql_log(ql_log_warn, vha, 0xb08d, | ||
465 | "%s: Failed to read from flash\n", | ||
466 | __func__); | ||
467 | } | ||
468 | scsi_unblock_requests(vha->host); | ||
469 | return buf; | ||
470 | } | ||
471 | |||
472 | inline int | ||
473 | qla8044_need_reset(struct scsi_qla_host *vha) | ||
474 | { | ||
475 | uint32_t drv_state, drv_active; | ||
476 | int rval; | ||
477 | struct qla_hw_data *ha = vha->hw; | ||
478 | |||
479 | drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
480 | drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
481 | |||
482 | rval = drv_state & (1 << ha->portnum); | ||
483 | |||
484 | if (ha->flags.eeh_busy && drv_active) | ||
485 | rval = 1; | ||
486 | return rval; | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * qla8044_write_list - Write the value (p_entry->arg2) to address specified | ||
491 | * by p_entry->arg1 for all entries in header with delay of p_hdr->delay between | ||
492 | * entries. | ||
493 | * | ||
494 | * @vha : Pointer to adapter structure | ||
495 | * @p_hdr : reset_entry header for WRITE_LIST opcode. | ||
496 | * | ||
497 | */ | ||
498 | static void | ||
499 | qla8044_write_list(struct scsi_qla_host *vha, | ||
500 | struct qla8044_reset_entry_hdr *p_hdr) | ||
501 | { | ||
502 | struct qla8044_entry *p_entry; | ||
503 | uint32_t i; | ||
504 | |||
505 | p_entry = (struct qla8044_entry *)((char *)p_hdr + | ||
506 | sizeof(struct qla8044_reset_entry_hdr)); | ||
507 | |||
508 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
509 | qla8044_wr_reg_indirect(vha, p_entry->arg1, p_entry->arg2); | ||
510 | if (p_hdr->delay) | ||
511 | udelay((uint32_t)(p_hdr->delay)); | ||
512 | } | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * qla8044_read_write_list - Read from address specified by p_entry->arg1, | ||
517 | * write value read to address specified by p_entry->arg2, for all entries in | ||
518 | * header with delay of p_hdr->delay between entries. | ||
519 | * | ||
520 | * @vha : Pointer to adapter structure | ||
521 | * @p_hdr : reset_entry header for READ_WRITE_LIST opcode. | ||
522 | * | ||
523 | */ | ||
524 | static void | ||
525 | qla8044_read_write_list(struct scsi_qla_host *vha, | ||
526 | struct qla8044_reset_entry_hdr *p_hdr) | ||
527 | { | ||
528 | struct qla8044_entry *p_entry; | ||
529 | uint32_t i; | ||
530 | |||
531 | p_entry = (struct qla8044_entry *)((char *)p_hdr + | ||
532 | sizeof(struct qla8044_reset_entry_hdr)); | ||
533 | |||
534 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
535 | qla8044_read_write_crb_reg(vha, p_entry->arg1, | ||
536 | p_entry->arg2); | ||
537 | if (p_hdr->delay) | ||
538 | udelay((uint32_t)(p_hdr->delay)); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | /* | ||
543 | * qla8044_poll_reg - Poll the given CRB addr for duration msecs till | ||
544 | * value read ANDed with test_mask is equal to test_result. | ||
545 | * | ||
546 | * @ha : Pointer to adapter structure | ||
547 | * @addr : CRB register address | ||
548 | * @duration : Poll for total of "duration" msecs | ||
549 | * @test_mask : Mask value read with "test_mask" | ||
550 | * @test_result : Compare (value&test_mask) with test_result. | ||
551 | * | ||
552 | * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED | ||
553 | */ | ||
554 | static int | ||
555 | qla8044_poll_reg(struct scsi_qla_host *vha, uint32_t addr, | ||
556 | int duration, uint32_t test_mask, uint32_t test_result) | ||
557 | { | ||
558 | uint32_t value; | ||
559 | int timeout_error; | ||
560 | uint8_t retries; | ||
561 | int ret_val = QLA_SUCCESS; | ||
562 | |||
563 | ret_val = qla8044_rd_reg_indirect(vha, addr, &value); | ||
564 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
565 | timeout_error = 1; | ||
566 | goto exit_poll_reg; | ||
567 | } | ||
568 | |||
569 | /* poll every 1/10 of the total duration */ | ||
570 | retries = duration/10; | ||
571 | |||
572 | do { | ||
573 | if ((value & test_mask) != test_result) { | ||
574 | timeout_error = 1; | ||
575 | msleep(duration/10); | ||
576 | ret_val = qla8044_rd_reg_indirect(vha, addr, &value); | ||
577 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
578 | timeout_error = 1; | ||
579 | goto exit_poll_reg; | ||
580 | } | ||
581 | } else { | ||
582 | timeout_error = 0; | ||
583 | break; | ||
584 | } | ||
585 | } while (retries--); | ||
586 | |||
587 | exit_poll_reg: | ||
588 | if (timeout_error) { | ||
589 | vha->reset_tmplt.seq_error++; | ||
590 | ql_log(ql_log_fatal, vha, 0xb090, | ||
591 | "%s: Poll Failed: 0x%08x 0x%08x 0x%08x\n", | ||
592 | __func__, value, test_mask, test_result); | ||
593 | } | ||
594 | |||
595 | return timeout_error; | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | * qla8044_poll_list - For all entries in the POLL_LIST header, poll read CRB | ||
600 | * register specified by p_entry->arg1 and compare (value AND test_mask) with | ||
601 | * test_result to validate it. Wait for p_hdr->delay between processing entries. | ||
602 | * | ||
603 | * @ha : Pointer to adapter structure | ||
604 | * @p_hdr : reset_entry header for POLL_LIST opcode. | ||
605 | * | ||
606 | */ | ||
607 | static void | ||
608 | qla8044_poll_list(struct scsi_qla_host *vha, | ||
609 | struct qla8044_reset_entry_hdr *p_hdr) | ||
610 | { | ||
611 | long delay; | ||
612 | struct qla8044_entry *p_entry; | ||
613 | struct qla8044_poll *p_poll; | ||
614 | uint32_t i; | ||
615 | uint32_t value; | ||
616 | |||
617 | p_poll = (struct qla8044_poll *) | ||
618 | ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr)); | ||
619 | |||
620 | /* Entries start after 8 byte qla8044_poll, poll header contains | ||
621 | * the test_mask, test_value. | ||
622 | */ | ||
623 | p_entry = (struct qla8044_entry *)((char *)p_poll + | ||
624 | sizeof(struct qla8044_poll)); | ||
625 | |||
626 | delay = (long)p_hdr->delay; | ||
627 | |||
628 | if (!delay) { | ||
629 | for (i = 0; i < p_hdr->count; i++, p_entry++) | ||
630 | qla8044_poll_reg(vha, p_entry->arg1, | ||
631 | delay, p_poll->test_mask, p_poll->test_value); | ||
632 | } else { | ||
633 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
634 | if (delay) { | ||
635 | if (qla8044_poll_reg(vha, | ||
636 | p_entry->arg1, delay, | ||
637 | p_poll->test_mask, | ||
638 | p_poll->test_value)) { | ||
639 | /*If | ||
640 | * (data_read&test_mask != test_value) | ||
641 | * read TIMEOUT_ADDR (arg1) and | ||
642 | * ADDR (arg2) registers | ||
643 | */ | ||
644 | qla8044_rd_reg_indirect(vha, | ||
645 | p_entry->arg1, &value); | ||
646 | qla8044_rd_reg_indirect(vha, | ||
647 | p_entry->arg2, &value); | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | } | ||
652 | } | ||
653 | |||
654 | /* | ||
655 | * qla8044_poll_write_list - Write dr_value, ar_value to dr_addr/ar_addr, | ||
656 | * read ar_addr, if (value& test_mask != test_mask) re-read till timeout | ||
657 | * expires. | ||
658 | * | ||
659 | * @vha : Pointer to adapter structure | ||
660 | * @p_hdr : reset entry header for POLL_WRITE_LIST opcode. | ||
661 | * | ||
662 | */ | ||
663 | static void | ||
664 | qla8044_poll_write_list(struct scsi_qla_host *vha, | ||
665 | struct qla8044_reset_entry_hdr *p_hdr) | ||
666 | { | ||
667 | long delay; | ||
668 | struct qla8044_quad_entry *p_entry; | ||
669 | struct qla8044_poll *p_poll; | ||
670 | uint32_t i; | ||
671 | |||
672 | p_poll = (struct qla8044_poll *)((char *)p_hdr + | ||
673 | sizeof(struct qla8044_reset_entry_hdr)); | ||
674 | |||
675 | p_entry = (struct qla8044_quad_entry *)((char *)p_poll + | ||
676 | sizeof(struct qla8044_poll)); | ||
677 | |||
678 | delay = (long)p_hdr->delay; | ||
679 | |||
680 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
681 | qla8044_wr_reg_indirect(vha, | ||
682 | p_entry->dr_addr, p_entry->dr_value); | ||
683 | qla8044_wr_reg_indirect(vha, | ||
684 | p_entry->ar_addr, p_entry->ar_value); | ||
685 | if (delay) { | ||
686 | if (qla8044_poll_reg(vha, | ||
687 | p_entry->ar_addr, delay, | ||
688 | p_poll->test_mask, | ||
689 | p_poll->test_value)) { | ||
690 | ql_dbg(ql_dbg_p3p, vha, 0xb091, | ||
691 | "%s: Timeout Error: poll list, ", | ||
692 | __func__); | ||
693 | ql_dbg(ql_dbg_p3p, vha, 0xb092, | ||
694 | "item_num %d, entry_num %d\n", i, | ||
695 | vha->reset_tmplt.seq_index); | ||
696 | } | ||
697 | } | ||
698 | } | ||
699 | } | ||
700 | |||
701 | /* | ||
702 | * qla8044_read_modify_write - Read value from p_entry->arg1, modify the | ||
703 | * value, write value to p_entry->arg2. Process entries with p_hdr->delay | ||
704 | * between entries. | ||
705 | * | ||
706 | * @vha : Pointer to adapter structure | ||
707 | * @p_hdr : header with shift/or/xor values. | ||
708 | * | ||
709 | */ | ||
710 | static void | ||
711 | qla8044_read_modify_write(struct scsi_qla_host *vha, | ||
712 | struct qla8044_reset_entry_hdr *p_hdr) | ||
713 | { | ||
714 | struct qla8044_entry *p_entry; | ||
715 | struct qla8044_rmw *p_rmw_hdr; | ||
716 | uint32_t i; | ||
717 | |||
718 | p_rmw_hdr = (struct qla8044_rmw *)((char *)p_hdr + | ||
719 | sizeof(struct qla8044_reset_entry_hdr)); | ||
720 | |||
721 | p_entry = (struct qla8044_entry *)((char *)p_rmw_hdr + | ||
722 | sizeof(struct qla8044_rmw)); | ||
723 | |||
724 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
725 | qla8044_rmw_crb_reg(vha, p_entry->arg1, | ||
726 | p_entry->arg2, p_rmw_hdr); | ||
727 | if (p_hdr->delay) | ||
728 | udelay((uint32_t)(p_hdr->delay)); | ||
729 | } | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * qla8044_pause - Wait for p_hdr->delay msecs, called between processing | ||
734 | * two entries of a sequence. | ||
735 | * | ||
736 | * @vha : Pointer to adapter structure | ||
737 | * @p_hdr : Common reset entry header. | ||
738 | * | ||
739 | */ | ||
740 | static | ||
741 | void qla8044_pause(struct scsi_qla_host *vha, | ||
742 | struct qla8044_reset_entry_hdr *p_hdr) | ||
743 | { | ||
744 | if (p_hdr->delay) | ||
745 | mdelay((uint32_t)((long)p_hdr->delay)); | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * qla8044_template_end - Indicates end of reset sequence processing. | ||
750 | * | ||
751 | * @vha : Pointer to adapter structure | ||
752 | * @p_hdr : Common reset entry header. | ||
753 | * | ||
754 | */ | ||
755 | static void | ||
756 | qla8044_template_end(struct scsi_qla_host *vha, | ||
757 | struct qla8044_reset_entry_hdr *p_hdr) | ||
758 | { | ||
759 | vha->reset_tmplt.template_end = 1; | ||
760 | |||
761 | if (vha->reset_tmplt.seq_error == 0) { | ||
762 | ql_dbg(ql_dbg_p3p, vha, 0xb093, | ||
763 | "%s: Reset sequence completed SUCCESSFULLY.\n", __func__); | ||
764 | } else { | ||
765 | ql_log(ql_log_fatal, vha, 0xb094, | ||
766 | "%s: Reset sequence completed with some timeout " | ||
767 | "errors.\n", __func__); | ||
768 | } | ||
769 | } | ||
770 | |||
771 | /* | ||
772 | * qla8044_poll_read_list - Write ar_value to ar_addr register, read ar_addr, | ||
773 | * if (value & test_mask != test_value) re-read till timeout value expires, | ||
774 | * read dr_addr register and assign to reset_tmplt.array. | ||
775 | * | ||
776 | * @vha : Pointer to adapter structure | ||
777 | * @p_hdr : Common reset entry header. | ||
778 | * | ||
779 | */ | ||
780 | static void | ||
781 | qla8044_poll_read_list(struct scsi_qla_host *vha, | ||
782 | struct qla8044_reset_entry_hdr *p_hdr) | ||
783 | { | ||
784 | long delay; | ||
785 | int index; | ||
786 | struct qla8044_quad_entry *p_entry; | ||
787 | struct qla8044_poll *p_poll; | ||
788 | uint32_t i; | ||
789 | uint32_t value; | ||
790 | |||
791 | p_poll = (struct qla8044_poll *) | ||
792 | ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr)); | ||
793 | |||
794 | p_entry = (struct qla8044_quad_entry *) | ||
795 | ((char *)p_poll + sizeof(struct qla8044_poll)); | ||
796 | |||
797 | delay = (long)p_hdr->delay; | ||
798 | |||
799 | for (i = 0; i < p_hdr->count; i++, p_entry++) { | ||
800 | qla8044_wr_reg_indirect(vha, p_entry->ar_addr, | ||
801 | p_entry->ar_value); | ||
802 | if (delay) { | ||
803 | if (qla8044_poll_reg(vha, p_entry->ar_addr, delay, | ||
804 | p_poll->test_mask, p_poll->test_value)) { | ||
805 | ql_dbg(ql_dbg_p3p, vha, 0xb095, | ||
806 | "%s: Timeout Error: poll " | ||
807 | "list, ", __func__); | ||
808 | ql_dbg(ql_dbg_p3p, vha, 0xb096, | ||
809 | "Item_num %d, " | ||
810 | "entry_num %d\n", i, | ||
811 | vha->reset_tmplt.seq_index); | ||
812 | } else { | ||
813 | index = vha->reset_tmplt.array_index; | ||
814 | qla8044_rd_reg_indirect(vha, | ||
815 | p_entry->dr_addr, &value); | ||
816 | vha->reset_tmplt.array[index++] = value; | ||
817 | if (index == QLA8044_MAX_RESET_SEQ_ENTRIES) | ||
818 | vha->reset_tmplt.array_index = 1; | ||
819 | } | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | |||
824 | /* | ||
825 | * qla8031_process_reset_template - Process all entries in reset template | ||
826 | * till entry with SEQ_END opcode, which indicates end of the reset template | ||
827 | * processing. Each entry has a Reset Entry header, entry opcode/command, with | ||
828 | * size of the entry, number of entries in sub-sequence and delay in microsecs | ||
829 | * or timeout in millisecs. | ||
830 | * | ||
831 | * @ha : Pointer to adapter structure | ||
832 | * @p_buff : Common reset entry header. | ||
833 | * | ||
834 | */ | ||
835 | static void | ||
836 | qla8044_process_reset_template(struct scsi_qla_host *vha, | ||
837 | char *p_buff) | ||
838 | { | ||
839 | int index, entries; | ||
840 | struct qla8044_reset_entry_hdr *p_hdr; | ||
841 | char *p_entry = p_buff; | ||
842 | |||
843 | vha->reset_tmplt.seq_end = 0; | ||
844 | vha->reset_tmplt.template_end = 0; | ||
845 | entries = vha->reset_tmplt.hdr->entries; | ||
846 | index = vha->reset_tmplt.seq_index; | ||
847 | |||
848 | for (; (!vha->reset_tmplt.seq_end) && (index < entries); index++) { | ||
849 | p_hdr = (struct qla8044_reset_entry_hdr *)p_entry; | ||
850 | switch (p_hdr->cmd) { | ||
851 | case OPCODE_NOP: | ||
852 | break; | ||
853 | case OPCODE_WRITE_LIST: | ||
854 | qla8044_write_list(vha, p_hdr); | ||
855 | break; | ||
856 | case OPCODE_READ_WRITE_LIST: | ||
857 | qla8044_read_write_list(vha, p_hdr); | ||
858 | break; | ||
859 | case OPCODE_POLL_LIST: | ||
860 | qla8044_poll_list(vha, p_hdr); | ||
861 | break; | ||
862 | case OPCODE_POLL_WRITE_LIST: | ||
863 | qla8044_poll_write_list(vha, p_hdr); | ||
864 | break; | ||
865 | case OPCODE_READ_MODIFY_WRITE: | ||
866 | qla8044_read_modify_write(vha, p_hdr); | ||
867 | break; | ||
868 | case OPCODE_SEQ_PAUSE: | ||
869 | qla8044_pause(vha, p_hdr); | ||
870 | break; | ||
871 | case OPCODE_SEQ_END: | ||
872 | vha->reset_tmplt.seq_end = 1; | ||
873 | break; | ||
874 | case OPCODE_TMPL_END: | ||
875 | qla8044_template_end(vha, p_hdr); | ||
876 | break; | ||
877 | case OPCODE_POLL_READ_LIST: | ||
878 | qla8044_poll_read_list(vha, p_hdr); | ||
879 | break; | ||
880 | default: | ||
881 | ql_log(ql_log_fatal, vha, 0xb097, | ||
882 | "%s: Unknown command ==> 0x%04x on " | ||
883 | "entry = %d\n", __func__, p_hdr->cmd, index); | ||
884 | break; | ||
885 | } | ||
886 | /* | ||
887 | *Set pointer to next entry in the sequence. | ||
888 | */ | ||
889 | p_entry += p_hdr->size; | ||
890 | } | ||
891 | vha->reset_tmplt.seq_index = index; | ||
892 | } | ||
893 | |||
894 | static void | ||
895 | qla8044_process_init_seq(struct scsi_qla_host *vha) | ||
896 | { | ||
897 | qla8044_process_reset_template(vha, | ||
898 | vha->reset_tmplt.init_offset); | ||
899 | if (vha->reset_tmplt.seq_end != 1) | ||
900 | ql_log(ql_log_fatal, vha, 0xb098, | ||
901 | "%s: Abrupt INIT Sub-Sequence end.\n", | ||
902 | __func__); | ||
903 | } | ||
904 | |||
905 | static void | ||
906 | qla8044_process_stop_seq(struct scsi_qla_host *vha) | ||
907 | { | ||
908 | vha->reset_tmplt.seq_index = 0; | ||
909 | qla8044_process_reset_template(vha, vha->reset_tmplt.stop_offset); | ||
910 | if (vha->reset_tmplt.seq_end != 1) | ||
911 | ql_log(ql_log_fatal, vha, 0xb099, | ||
912 | "%s: Abrupt STOP Sub-Sequence end.\n", __func__); | ||
913 | } | ||
914 | |||
915 | static void | ||
916 | qla8044_process_start_seq(struct scsi_qla_host *vha) | ||
917 | { | ||
918 | qla8044_process_reset_template(vha, vha->reset_tmplt.start_offset); | ||
919 | if (vha->reset_tmplt.template_end != 1) | ||
920 | ql_log(ql_log_fatal, vha, 0xb09a, | ||
921 | "%s: Abrupt START Sub-Sequence end.\n", | ||
922 | __func__); | ||
923 | } | ||
924 | |||
925 | static int | ||
926 | qla8044_lockless_flash_read_u32(struct scsi_qla_host *vha, | ||
927 | uint32_t flash_addr, uint8_t *p_data, int u32_word_count) | ||
928 | { | ||
929 | uint32_t i; | ||
930 | uint32_t u32_word; | ||
931 | uint32_t flash_offset; | ||
932 | uint32_t addr = flash_addr; | ||
933 | int ret_val = QLA_SUCCESS; | ||
934 | |||
935 | flash_offset = addr & (QLA8044_FLASH_SECTOR_SIZE - 1); | ||
936 | |||
937 | if (addr & 0x3) { | ||
938 | ql_log(ql_log_fatal, vha, 0xb09b, "%s: Illegal addr = 0x%x\n", | ||
939 | __func__, addr); | ||
940 | ret_val = QLA_FUNCTION_FAILED; | ||
941 | goto exit_lockless_read; | ||
942 | } | ||
943 | |||
944 | ret_val = qla8044_wr_reg_indirect(vha, | ||
945 | QLA8044_FLASH_DIRECT_WINDOW, (addr)); | ||
946 | |||
947 | if (ret_val != QLA_SUCCESS) { | ||
948 | ql_log(ql_log_fatal, vha, 0xb09c, | ||
949 | "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n", | ||
950 | __func__, addr); | ||
951 | goto exit_lockless_read; | ||
952 | } | ||
953 | |||
954 | /* Check if data is spread across multiple sectors */ | ||
955 | if ((flash_offset + (u32_word_count * sizeof(uint32_t))) > | ||
956 | (QLA8044_FLASH_SECTOR_SIZE - 1)) { | ||
957 | /* Multi sector read */ | ||
958 | for (i = 0; i < u32_word_count; i++) { | ||
959 | ret_val = qla8044_rd_reg_indirect(vha, | ||
960 | QLA8044_FLASH_DIRECT_DATA(addr), &u32_word); | ||
961 | if (ret_val != QLA_SUCCESS) { | ||
962 | ql_log(ql_log_fatal, vha, 0xb09d, | ||
963 | "%s: failed to read addr 0x%x!\n", | ||
964 | __func__, addr); | ||
965 | goto exit_lockless_read; | ||
966 | } | ||
967 | *(uint32_t *)p_data = u32_word; | ||
968 | p_data = p_data + 4; | ||
969 | addr = addr + 4; | ||
970 | flash_offset = flash_offset + 4; | ||
971 | if (flash_offset > (QLA8044_FLASH_SECTOR_SIZE - 1)) { | ||
972 | /* This write is needed once for each sector */ | ||
973 | ret_val = qla8044_wr_reg_indirect(vha, | ||
974 | QLA8044_FLASH_DIRECT_WINDOW, (addr)); | ||
975 | if (ret_val != QLA_SUCCESS) { | ||
976 | ql_log(ql_log_fatal, vha, 0xb09f, | ||
977 | "%s: failed to write addr " | ||
978 | "0x%x to FLASH_DIRECT_WINDOW!\n", | ||
979 | __func__, addr); | ||
980 | goto exit_lockless_read; | ||
981 | } | ||
982 | flash_offset = 0; | ||
983 | } | ||
984 | } | ||
985 | } else { | ||
986 | /* Single sector read */ | ||
987 | for (i = 0; i < u32_word_count; i++) { | ||
988 | ret_val = qla8044_rd_reg_indirect(vha, | ||
989 | QLA8044_FLASH_DIRECT_DATA(addr), &u32_word); | ||
990 | if (ret_val != QLA_SUCCESS) { | ||
991 | ql_log(ql_log_fatal, vha, 0xb0a0, | ||
992 | "%s: failed to read addr 0x%x!\n", | ||
993 | __func__, addr); | ||
994 | goto exit_lockless_read; | ||
995 | } | ||
996 | *(uint32_t *)p_data = u32_word; | ||
997 | p_data = p_data + 4; | ||
998 | addr = addr + 4; | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | exit_lockless_read: | ||
1003 | return ret_val; | ||
1004 | } | ||
1005 | |||
1006 | /* | ||
1007 | * qla8044_ms_mem_write_128b - Writes data to MS/off-chip memory | ||
1008 | * | ||
1009 | * @vha : Pointer to adapter structure | ||
1010 | * addr : Flash address to write to | ||
1011 | * data : Data to be written | ||
1012 | * count : word_count to be written | ||
1013 | * | ||
1014 | * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED | ||
1015 | */ | ||
1016 | static int | ||
1017 | qla8044_ms_mem_write_128b(struct scsi_qla_host *vha, | ||
1018 | uint64_t addr, uint32_t *data, uint32_t count) | ||
1019 | { | ||
1020 | int i, j, ret_val = QLA_SUCCESS; | ||
1021 | uint32_t agt_ctrl; | ||
1022 | unsigned long flags; | ||
1023 | struct qla_hw_data *ha = vha->hw; | ||
1024 | |||
1025 | /* Only 128-bit aligned access */ | ||
1026 | if (addr & 0xF) { | ||
1027 | ret_val = QLA_FUNCTION_FAILED; | ||
1028 | goto exit_ms_mem_write; | ||
1029 | } | ||
1030 | write_lock_irqsave(&ha->hw_lock, flags); | ||
1031 | |||
1032 | /* Write address */ | ||
1033 | ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, 0); | ||
1034 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1035 | ql_log(ql_log_fatal, vha, 0xb0a1, | ||
1036 | "%s: write to AGT_ADDR_HI failed!\n", __func__); | ||
1037 | goto exit_ms_mem_write_unlock; | ||
1038 | } | ||
1039 | |||
1040 | for (i = 0; i < count; i++, addr += 16) { | ||
1041 | if (!((QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_QDR_NET, | ||
1042 | QLA8044_ADDR_QDR_NET_MAX)) || | ||
1043 | (QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_DDR_NET, | ||
1044 | QLA8044_ADDR_DDR_NET_MAX)))) { | ||
1045 | ret_val = QLA_FUNCTION_FAILED; | ||
1046 | goto exit_ms_mem_write_unlock; | ||
1047 | } | ||
1048 | |||
1049 | ret_val = qla8044_wr_reg_indirect(vha, | ||
1050 | MD_MIU_TEST_AGT_ADDR_LO, addr); | ||
1051 | |||
1052 | /* Write data */ | ||
1053 | ret_val += qla8044_wr_reg_indirect(vha, | ||
1054 | MD_MIU_TEST_AGT_WRDATA_LO, *data++); | ||
1055 | ret_val += qla8044_wr_reg_indirect(vha, | ||
1056 | MD_MIU_TEST_AGT_WRDATA_HI, *data++); | ||
1057 | ret_val += qla8044_wr_reg_indirect(vha, | ||
1058 | MD_MIU_TEST_AGT_WRDATA_ULO, *data++); | ||
1059 | ret_val += qla8044_wr_reg_indirect(vha, | ||
1060 | MD_MIU_TEST_AGT_WRDATA_UHI, *data++); | ||
1061 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1062 | ql_log(ql_log_fatal, vha, 0xb0a2, | ||
1063 | "%s: write to AGT_WRDATA failed!\n", | ||
1064 | __func__); | ||
1065 | goto exit_ms_mem_write_unlock; | ||
1066 | } | ||
1067 | |||
1068 | /* Check write status */ | ||
1069 | ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, | ||
1070 | MIU_TA_CTL_WRITE_ENABLE); | ||
1071 | ret_val += qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, | ||
1072 | MIU_TA_CTL_WRITE_START); | ||
1073 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1074 | ql_log(ql_log_fatal, vha, 0xb0a3, | ||
1075 | "%s: write to AGT_CTRL failed!\n", __func__); | ||
1076 | goto exit_ms_mem_write_unlock; | ||
1077 | } | ||
1078 | |||
1079 | for (j = 0; j < MAX_CTL_CHECK; j++) { | ||
1080 | ret_val = qla8044_rd_reg_indirect(vha, | ||
1081 | MD_MIU_TEST_AGT_CTRL, &agt_ctrl); | ||
1082 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1083 | ql_log(ql_log_fatal, vha, 0xb0a4, | ||
1084 | "%s: failed to read " | ||
1085 | "MD_MIU_TEST_AGT_CTRL!\n", __func__); | ||
1086 | goto exit_ms_mem_write_unlock; | ||
1087 | } | ||
1088 | if ((agt_ctrl & MIU_TA_CTL_BUSY) == 0) | ||
1089 | break; | ||
1090 | } | ||
1091 | |||
1092 | /* Status check failed */ | ||
1093 | if (j >= MAX_CTL_CHECK) { | ||
1094 | ql_log(ql_log_fatal, vha, 0xb0a5, | ||
1095 | "%s: MS memory write failed!\n", | ||
1096 | __func__); | ||
1097 | ret_val = QLA_FUNCTION_FAILED; | ||
1098 | goto exit_ms_mem_write_unlock; | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | exit_ms_mem_write_unlock: | ||
1103 | write_unlock_irqrestore(&ha->hw_lock, flags); | ||
1104 | |||
1105 | exit_ms_mem_write: | ||
1106 | return ret_val; | ||
1107 | } | ||
1108 | |||
1109 | static int | ||
1110 | qla8044_copy_bootloader(struct scsi_qla_host *vha) | ||
1111 | { | ||
1112 | uint8_t *p_cache; | ||
1113 | uint32_t src, count, size; | ||
1114 | uint64_t dest; | ||
1115 | int ret_val = QLA_SUCCESS; | ||
1116 | struct qla_hw_data *ha = vha->hw; | ||
1117 | |||
1118 | src = QLA8044_BOOTLOADER_FLASH_ADDR; | ||
1119 | dest = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_ADDR); | ||
1120 | size = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_SIZE); | ||
1121 | |||
1122 | /* 128 bit alignment check */ | ||
1123 | if (size & 0xF) | ||
1124 | size = (size + 16) & ~0xF; | ||
1125 | |||
1126 | /* 16 byte count */ | ||
1127 | count = size/16; | ||
1128 | |||
1129 | p_cache = vmalloc(size); | ||
1130 | if (p_cache == NULL) { | ||
1131 | ql_log(ql_log_fatal, vha, 0xb0a6, | ||
1132 | "%s: Failed to allocate memory for " | ||
1133 | "boot loader cache\n", __func__); | ||
1134 | ret_val = QLA_FUNCTION_FAILED; | ||
1135 | goto exit_copy_bootloader; | ||
1136 | } | ||
1137 | |||
1138 | ret_val = qla8044_lockless_flash_read_u32(vha, src, | ||
1139 | p_cache, size/sizeof(uint32_t)); | ||
1140 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1141 | ql_log(ql_log_fatal, vha, 0xb0a7, | ||
1142 | "%s: Error reading F/W from flash!!!\n", __func__); | ||
1143 | goto exit_copy_error; | ||
1144 | } | ||
1145 | ql_dbg(ql_dbg_p3p, vha, 0xb0a8, "%s: Read F/W from flash!\n", | ||
1146 | __func__); | ||
1147 | |||
1148 | /* 128 bit/16 byte write to MS memory */ | ||
1149 | ret_val = qla8044_ms_mem_write_128b(vha, dest, | ||
1150 | (uint32_t *)p_cache, count); | ||
1151 | if (ret_val == QLA_FUNCTION_FAILED) { | ||
1152 | ql_log(ql_log_fatal, vha, 0xb0a9, | ||
1153 | "%s: Error writing F/W to MS !!!\n", __func__); | ||
1154 | goto exit_copy_error; | ||
1155 | } | ||
1156 | ql_dbg(ql_dbg_p3p, vha, 0xb0aa, | ||
1157 | "%s: Wrote F/W (size %d) to MS !!!\n", | ||
1158 | __func__, size); | ||
1159 | |||
1160 | exit_copy_error: | ||
1161 | vfree(p_cache); | ||
1162 | |||
1163 | exit_copy_bootloader: | ||
1164 | return ret_val; | ||
1165 | } | ||
1166 | |||
1167 | static int | ||
1168 | qla8044_restart(struct scsi_qla_host *vha) | ||
1169 | { | ||
1170 | int ret_val = QLA_SUCCESS; | ||
1171 | struct qla_hw_data *ha = vha->hw; | ||
1172 | |||
1173 | qla8044_process_stop_seq(vha); | ||
1174 | |||
1175 | /* Collect minidump */ | ||
1176 | if (ql2xmdenable) | ||
1177 | qla8044_get_minidump(vha); | ||
1178 | else | ||
1179 | ql_log(ql_log_fatal, vha, 0xb14c, | ||
1180 | "Minidump disabled.\n"); | ||
1181 | |||
1182 | qla8044_process_init_seq(vha); | ||
1183 | |||
1184 | if (qla8044_copy_bootloader(vha)) { | ||
1185 | ql_log(ql_log_fatal, vha, 0xb0ab, | ||
1186 | "%s: Copy bootloader, firmware restart failed!\n", | ||
1187 | __func__); | ||
1188 | ret_val = QLA_FUNCTION_FAILED; | ||
1189 | goto exit_restart; | ||
1190 | } | ||
1191 | |||
1192 | /* | ||
1193 | * Loads F/W from flash | ||
1194 | */ | ||
1195 | qla8044_wr_reg(ha, QLA8044_FW_IMAGE_VALID, QLA8044_BOOT_FROM_FLASH); | ||
1196 | |||
1197 | qla8044_process_start_seq(vha); | ||
1198 | |||
1199 | exit_restart: | ||
1200 | return ret_val; | ||
1201 | } | ||
1202 | |||
1203 | /* | ||
1204 | * qla8044_check_cmd_peg_status - Check peg status to see if Peg is | ||
1205 | * initialized. | ||
1206 | * | ||
1207 | * @ha : Pointer to adapter structure | ||
1208 | * | ||
1209 | * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED | ||
1210 | */ | ||
1211 | static int | ||
1212 | qla8044_check_cmd_peg_status(struct scsi_qla_host *vha) | ||
1213 | { | ||
1214 | uint32_t val, ret_val = QLA_FUNCTION_FAILED; | ||
1215 | int retries = CRB_CMDPEG_CHECK_RETRY_COUNT; | ||
1216 | struct qla_hw_data *ha = vha->hw; | ||
1217 | |||
1218 | do { | ||
1219 | val = qla8044_rd_reg(ha, QLA8044_CMDPEG_STATE); | ||
1220 | if (val == PHAN_INITIALIZE_COMPLETE) { | ||
1221 | ql_dbg(ql_dbg_p3p, vha, 0xb0ac, | ||
1222 | "%s: Command Peg initialization " | ||
1223 | "complete! state=0x%x\n", __func__, val); | ||
1224 | ret_val = QLA_SUCCESS; | ||
1225 | break; | ||
1226 | } | ||
1227 | msleep(CRB_CMDPEG_CHECK_DELAY); | ||
1228 | } while (--retries); | ||
1229 | |||
1230 | return ret_val; | ||
1231 | } | ||
1232 | |||
1233 | static int | ||
1234 | qla8044_start_firmware(struct scsi_qla_host *vha) | ||
1235 | { | ||
1236 | int ret_val = QLA_SUCCESS; | ||
1237 | |||
1238 | if (qla8044_restart(vha)) { | ||
1239 | ql_log(ql_log_fatal, vha, 0xb0ad, | ||
1240 | "%s: Restart Error!!!, Need Reset!!!\n", | ||
1241 | __func__); | ||
1242 | ret_val = QLA_FUNCTION_FAILED; | ||
1243 | goto exit_start_fw; | ||
1244 | } else | ||
1245 | ql_dbg(ql_dbg_p3p, vha, 0xb0af, | ||
1246 | "%s: Restart done!\n", __func__); | ||
1247 | |||
1248 | ret_val = qla8044_check_cmd_peg_status(vha); | ||
1249 | if (ret_val) { | ||
1250 | ql_log(ql_log_fatal, vha, 0xb0b0, | ||
1251 | "%s: Peg not initialized!\n", __func__); | ||
1252 | ret_val = QLA_FUNCTION_FAILED; | ||
1253 | } | ||
1254 | |||
1255 | exit_start_fw: | ||
1256 | return ret_val; | ||
1257 | } | ||
1258 | |||
1259 | void | ||
1260 | qla8044_clear_drv_active(struct scsi_qla_host *vha) | ||
1261 | { | ||
1262 | uint32_t drv_active; | ||
1263 | struct qla_hw_data *ha = vha->hw; | ||
1264 | |||
1265 | drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1266 | drv_active &= ~(1 << (ha->portnum)); | ||
1267 | |||
1268 | ql_log(ql_log_info, vha, 0xb0b1, | ||
1269 | "%s(%ld): drv_active: 0x%08x\n", | ||
1270 | __func__, vha->host_no, drv_active); | ||
1271 | |||
1272 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active); | ||
1273 | } | ||
1274 | |||
1275 | /* | ||
1276 | * qla8044_device_bootstrap - Initialize device, set DEV_READY, start fw | ||
1277 | * @ha: pointer to adapter structure | ||
1278 | * | ||
1279 | * Note: IDC lock must be held upon entry | ||
1280 | **/ | ||
1281 | static int | ||
1282 | qla8044_device_bootstrap(struct scsi_qla_host *vha) | ||
1283 | { | ||
1284 | int rval = QLA_FUNCTION_FAILED; | ||
1285 | int i; | ||
1286 | uint32_t old_count = 0, count = 0; | ||
1287 | int need_reset = 0; | ||
1288 | uint32_t idc_ctrl; | ||
1289 | struct qla_hw_data *ha = vha->hw; | ||
1290 | |||
1291 | need_reset = qla8044_need_reset(vha); | ||
1292 | |||
1293 | if (!need_reset) { | ||
1294 | old_count = qla8044_rd_direct(vha, | ||
1295 | QLA8044_PEG_ALIVE_COUNTER_INDEX); | ||
1296 | |||
1297 | for (i = 0; i < 10; i++) { | ||
1298 | msleep(200); | ||
1299 | |||
1300 | count = qla8044_rd_direct(vha, | ||
1301 | QLA8044_PEG_ALIVE_COUNTER_INDEX); | ||
1302 | if (count != old_count) { | ||
1303 | rval = QLA_SUCCESS; | ||
1304 | goto dev_ready; | ||
1305 | } | ||
1306 | } | ||
1307 | qla8044_flash_lock_recovery(vha); | ||
1308 | } else { | ||
1309 | /* We are trying to perform a recovery here. */ | ||
1310 | if (ha->flags.isp82xx_fw_hung) | ||
1311 | qla8044_flash_lock_recovery(vha); | ||
1312 | } | ||
1313 | |||
1314 | /* set to DEV_INITIALIZING */ | ||
1315 | ql_log(ql_log_info, vha, 0xb0b2, | ||
1316 | "%s: HW State: INITIALIZING\n", __func__); | ||
1317 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
1318 | QLA8XXX_DEV_INITIALIZING); | ||
1319 | |||
1320 | qla8044_idc_unlock(ha); | ||
1321 | rval = qla8044_start_firmware(vha); | ||
1322 | qla8044_idc_lock(ha); | ||
1323 | |||
1324 | if (rval != QLA_SUCCESS) { | ||
1325 | ql_log(ql_log_info, vha, 0xb0b3, | ||
1326 | "%s: HW State: FAILED\n", __func__); | ||
1327 | qla8044_clear_drv_active(vha); | ||
1328 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
1329 | QLA8XXX_DEV_FAILED); | ||
1330 | return rval; | ||
1331 | } | ||
1332 | |||
1333 | /* For ISP8044, If IDC_CTRL GRACEFUL_RESET_BIT1 is set , reset it after | ||
1334 | * device goes to INIT state. */ | ||
1335 | idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); | ||
1336 | if (idc_ctrl & GRACEFUL_RESET_BIT1) { | ||
1337 | qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, | ||
1338 | (idc_ctrl & ~GRACEFUL_RESET_BIT1)); | ||
1339 | ha->fw_dumped = 0; | ||
1340 | } | ||
1341 | |||
1342 | dev_ready: | ||
1343 | ql_log(ql_log_info, vha, 0xb0b4, | ||
1344 | "%s: HW State: READY\n", __func__); | ||
1345 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, QLA8XXX_DEV_READY); | ||
1346 | |||
1347 | return rval; | ||
1348 | } | ||
1349 | |||
1350 | /*-------------------------Reset Sequence Functions-----------------------*/ | ||
1351 | static void | ||
1352 | qla8044_dump_reset_seq_hdr(struct scsi_qla_host *vha) | ||
1353 | { | ||
1354 | u8 *phdr; | ||
1355 | |||
1356 | if (!vha->reset_tmplt.buff) { | ||
1357 | ql_log(ql_log_fatal, vha, 0xb0b5, | ||
1358 | "%s: Error Invalid reset_seq_template\n", __func__); | ||
1359 | return; | ||
1360 | } | ||
1361 | |||
1362 | phdr = vha->reset_tmplt.buff; | ||
1363 | ql_dbg(ql_dbg_p3p, vha, 0xb0b6, | ||
1364 | "Reset Template :\n\t0x%X 0x%X 0x%X 0x%X" | ||
1365 | "0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n" | ||
1366 | "\t0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n\n", | ||
1367 | *phdr, *(phdr+1), *(phdr+2), *(phdr+3), *(phdr+4), | ||
1368 | *(phdr+5), *(phdr+6), *(phdr+7), *(phdr + 8), | ||
1369 | *(phdr+9), *(phdr+10), *(phdr+11), *(phdr+12), | ||
1370 | *(phdr+13), *(phdr+14), *(phdr+15)); | ||
1371 | } | ||
1372 | |||
1373 | /* | ||
1374 | * qla8044_reset_seq_checksum_test - Validate Reset Sequence template. | ||
1375 | * | ||
1376 | * @ha : Pointer to adapter structure | ||
1377 | * | ||
1378 | * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED | ||
1379 | */ | ||
1380 | static int | ||
1381 | qla8044_reset_seq_checksum_test(struct scsi_qla_host *vha) | ||
1382 | { | ||
1383 | uint32_t sum = 0; | ||
1384 | uint16_t *buff = (uint16_t *)vha->reset_tmplt.buff; | ||
1385 | int u16_count = vha->reset_tmplt.hdr->size / sizeof(uint16_t); | ||
1386 | |||
1387 | while (u16_count-- > 0) | ||
1388 | sum += *buff++; | ||
1389 | |||
1390 | while (sum >> 16) | ||
1391 | sum = (sum & 0xFFFF) + (sum >> 16); | ||
1392 | |||
1393 | /* checksum of 0 indicates a valid template */ | ||
1394 | if (~sum) { | ||
1395 | return QLA_SUCCESS; | ||
1396 | } else { | ||
1397 | ql_log(ql_log_fatal, vha, 0xb0b7, | ||
1398 | "%s: Reset seq checksum failed\n", __func__); | ||
1399 | return QLA_FUNCTION_FAILED; | ||
1400 | } | ||
1401 | } | ||
1402 | |||
1403 | /* | ||
1404 | * qla8044_read_reset_template - Read Reset Template from Flash, validate | ||
1405 | * the template and store offsets of stop/start/init offsets in ha->reset_tmplt. | ||
1406 | * | ||
1407 | * @ha : Pointer to adapter structure | ||
1408 | */ | ||
1409 | void | ||
1410 | qla8044_read_reset_template(struct scsi_qla_host *vha) | ||
1411 | { | ||
1412 | uint8_t *p_buff; | ||
1413 | uint32_t addr, tmplt_hdr_def_size, tmplt_hdr_size; | ||
1414 | |||
1415 | vha->reset_tmplt.seq_error = 0; | ||
1416 | vha->reset_tmplt.buff = vmalloc(QLA8044_RESTART_TEMPLATE_SIZE); | ||
1417 | if (vha->reset_tmplt.buff == NULL) { | ||
1418 | ql_log(ql_log_fatal, vha, 0xb0b8, | ||
1419 | "%s: Failed to allocate reset template resources\n", | ||
1420 | __func__); | ||
1421 | goto exit_read_reset_template; | ||
1422 | } | ||
1423 | |||
1424 | p_buff = vha->reset_tmplt.buff; | ||
1425 | addr = QLA8044_RESET_TEMPLATE_ADDR; | ||
1426 | |||
1427 | tmplt_hdr_def_size = | ||
1428 | sizeof(struct qla8044_reset_template_hdr) / sizeof(uint32_t); | ||
1429 | |||
1430 | ql_dbg(ql_dbg_p3p, vha, 0xb0b9, | ||
1431 | "%s: Read template hdr size %d from Flash\n", | ||
1432 | __func__, tmplt_hdr_def_size); | ||
1433 | |||
1434 | /* Copy template header from flash */ | ||
1435 | if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) { | ||
1436 | ql_log(ql_log_fatal, vha, 0xb0ba, | ||
1437 | "%s: Failed to read reset template\n", __func__); | ||
1438 | goto exit_read_template_error; | ||
1439 | } | ||
1440 | |||
1441 | vha->reset_tmplt.hdr = | ||
1442 | (struct qla8044_reset_template_hdr *) vha->reset_tmplt.buff; | ||
1443 | |||
1444 | /* Validate the template header size and signature */ | ||
1445 | tmplt_hdr_size = vha->reset_tmplt.hdr->hdr_size/sizeof(uint32_t); | ||
1446 | if ((tmplt_hdr_size != tmplt_hdr_def_size) || | ||
1447 | (vha->reset_tmplt.hdr->signature != RESET_TMPLT_HDR_SIGNATURE)) { | ||
1448 | ql_log(ql_log_fatal, vha, 0xb0bb, | ||
1449 | "%s: Template Header size invalid %d " | ||
1450 | "tmplt_hdr_def_size %d!!!\n", __func__, | ||
1451 | tmplt_hdr_size, tmplt_hdr_def_size); | ||
1452 | goto exit_read_template_error; | ||
1453 | } | ||
1454 | |||
1455 | addr = QLA8044_RESET_TEMPLATE_ADDR + vha->reset_tmplt.hdr->hdr_size; | ||
1456 | p_buff = vha->reset_tmplt.buff + vha->reset_tmplt.hdr->hdr_size; | ||
1457 | tmplt_hdr_def_size = (vha->reset_tmplt.hdr->size - | ||
1458 | vha->reset_tmplt.hdr->hdr_size)/sizeof(uint32_t); | ||
1459 | |||
1460 | ql_dbg(ql_dbg_p3p, vha, 0xb0bc, | ||
1461 | "%s: Read rest of the template size %d\n", | ||
1462 | __func__, vha->reset_tmplt.hdr->size); | ||
1463 | |||
1464 | /* Copy rest of the template */ | ||
1465 | if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) { | ||
1466 | ql_log(ql_log_fatal, vha, 0xb0bd, | ||
1467 | "%s: Failed to read reset tempelate\n", __func__); | ||
1468 | goto exit_read_template_error; | ||
1469 | } | ||
1470 | |||
1471 | /* Integrity check */ | ||
1472 | if (qla8044_reset_seq_checksum_test(vha)) { | ||
1473 | ql_log(ql_log_fatal, vha, 0xb0be, | ||
1474 | "%s: Reset Seq checksum failed!\n", __func__); | ||
1475 | goto exit_read_template_error; | ||
1476 | } | ||
1477 | |||
1478 | ql_dbg(ql_dbg_p3p, vha, 0xb0bf, | ||
1479 | "%s: Reset Seq checksum passed! Get stop, " | ||
1480 | "start and init seq offsets\n", __func__); | ||
1481 | |||
1482 | /* Get STOP, START, INIT sequence offsets */ | ||
1483 | vha->reset_tmplt.init_offset = vha->reset_tmplt.buff + | ||
1484 | vha->reset_tmplt.hdr->init_seq_offset; | ||
1485 | |||
1486 | vha->reset_tmplt.start_offset = vha->reset_tmplt.buff + | ||
1487 | vha->reset_tmplt.hdr->start_seq_offset; | ||
1488 | |||
1489 | vha->reset_tmplt.stop_offset = vha->reset_tmplt.buff + | ||
1490 | vha->reset_tmplt.hdr->hdr_size; | ||
1491 | |||
1492 | qla8044_dump_reset_seq_hdr(vha); | ||
1493 | |||
1494 | goto exit_read_reset_template; | ||
1495 | |||
1496 | exit_read_template_error: | ||
1497 | vfree(vha->reset_tmplt.buff); | ||
1498 | |||
1499 | exit_read_reset_template: | ||
1500 | return; | ||
1501 | } | ||
1502 | |||
1503 | void | ||
1504 | qla8044_set_idc_dontreset(struct scsi_qla_host *vha) | ||
1505 | { | ||
1506 | uint32_t idc_ctrl; | ||
1507 | struct qla_hw_data *ha = vha->hw; | ||
1508 | |||
1509 | idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); | ||
1510 | idc_ctrl |= DONTRESET_BIT0; | ||
1511 | ql_dbg(ql_dbg_p3p, vha, 0xb0c0, | ||
1512 | "%s: idc_ctrl = %d\n", __func__, idc_ctrl); | ||
1513 | qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl); | ||
1514 | } | ||
1515 | |||
1516 | inline void | ||
1517 | qla8044_set_rst_ready(struct scsi_qla_host *vha) | ||
1518 | { | ||
1519 | uint32_t drv_state; | ||
1520 | struct qla_hw_data *ha = vha->hw; | ||
1521 | |||
1522 | drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
1523 | |||
1524 | /* For ISP8044, drv_active register has 1 bit per function, | ||
1525 | * shift 1 by func_num to set a bit for the function.*/ | ||
1526 | drv_state |= (1 << ha->portnum); | ||
1527 | |||
1528 | ql_log(ql_log_info, vha, 0xb0c1, | ||
1529 | "%s(%ld): drv_state: 0x%08x\n", | ||
1530 | __func__, vha->host_no, drv_state); | ||
1531 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, drv_state); | ||
1532 | } | ||
1533 | |||
1534 | /** | ||
1535 | * qla8044_need_reset_handler - Code to start reset sequence | ||
1536 | * @ha: pointer to adapter structure | ||
1537 | * | ||
1538 | * Note: IDC lock must be held upon entry | ||
1539 | **/ | ||
1540 | static void | ||
1541 | qla8044_need_reset_handler(struct scsi_qla_host *vha) | ||
1542 | { | ||
1543 | uint32_t dev_state = 0, drv_state, drv_active; | ||
1544 | unsigned long reset_timeout, dev_init_timeout; | ||
1545 | struct qla_hw_data *ha = vha->hw; | ||
1546 | |||
1547 | ql_log(ql_log_fatal, vha, 0xb0c2, | ||
1548 | "%s: Performing ISP error recovery\n", __func__); | ||
1549 | |||
1550 | if (vha->flags.online) { | ||
1551 | qla8044_idc_unlock(ha); | ||
1552 | qla2x00_abort_isp_cleanup(vha); | ||
1553 | ha->isp_ops->get_flash_version(vha, vha->req->ring); | ||
1554 | ha->isp_ops->nvram_config(vha); | ||
1555 | qla8044_idc_lock(ha); | ||
1556 | } | ||
1557 | |||
1558 | if (!ha->flags.nic_core_reset_owner) { | ||
1559 | ql_dbg(ql_dbg_p3p, vha, 0xb0c3, | ||
1560 | "%s(%ld): reset acknowledged\n", | ||
1561 | __func__, vha->host_no); | ||
1562 | qla8044_set_rst_ready(vha); | ||
1563 | |||
1564 | /* Non-reset owners ACK Reset and wait for device INIT state | ||
1565 | * as part of Reset Recovery by Reset Owner | ||
1566 | */ | ||
1567 | dev_init_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); | ||
1568 | |||
1569 | do { | ||
1570 | if (time_after_eq(jiffies, dev_init_timeout)) { | ||
1571 | ql_log(ql_log_info, vha, 0xb0c4, | ||
1572 | "%s: Non Reset owner DEV INIT " | ||
1573 | "TIMEOUT!\n", __func__); | ||
1574 | break; | ||
1575 | } | ||
1576 | |||
1577 | qla8044_idc_unlock(ha); | ||
1578 | msleep(1000); | ||
1579 | qla8044_idc_lock(ha); | ||
1580 | |||
1581 | dev_state = qla8044_rd_direct(vha, | ||
1582 | QLA8044_CRB_DEV_STATE_INDEX); | ||
1583 | } while (dev_state == QLA8XXX_DEV_NEED_RESET); | ||
1584 | } else { | ||
1585 | qla8044_set_rst_ready(vha); | ||
1586 | |||
1587 | /* wait for 10 seconds for reset ack from all functions */ | ||
1588 | reset_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); | ||
1589 | |||
1590 | drv_state = qla8044_rd_direct(vha, | ||
1591 | QLA8044_CRB_DRV_STATE_INDEX); | ||
1592 | drv_active = qla8044_rd_direct(vha, | ||
1593 | QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1594 | |||
1595 | ql_log(ql_log_info, vha, 0xb0c5, | ||
1596 | "%s(%ld): drv_state = 0x%x, drv_active = 0x%x\n", | ||
1597 | __func__, vha->host_no, drv_state, drv_active); | ||
1598 | |||
1599 | while (drv_state != drv_active) { | ||
1600 | if (time_after_eq(jiffies, reset_timeout)) { | ||
1601 | ql_log(ql_log_info, vha, 0xb0c6, | ||
1602 | "%s: RESET TIMEOUT!" | ||
1603 | "drv_state: 0x%08x, drv_active: 0x%08x\n", | ||
1604 | QLA2XXX_DRIVER_NAME, drv_state, drv_active); | ||
1605 | break; | ||
1606 | } | ||
1607 | |||
1608 | qla8044_idc_unlock(ha); | ||
1609 | msleep(1000); | ||
1610 | qla8044_idc_lock(ha); | ||
1611 | |||
1612 | drv_state = qla8044_rd_direct(vha, | ||
1613 | QLA8044_CRB_DRV_STATE_INDEX); | ||
1614 | drv_active = qla8044_rd_direct(vha, | ||
1615 | QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1616 | } | ||
1617 | |||
1618 | if (drv_state != drv_active) { | ||
1619 | ql_log(ql_log_info, vha, 0xb0c7, | ||
1620 | "%s(%ld): Reset_owner turning off drv_active " | ||
1621 | "of non-acking function 0x%x\n", __func__, | ||
1622 | vha->host_no, (drv_active ^ drv_state)); | ||
1623 | drv_active = drv_active & drv_state; | ||
1624 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, | ||
1625 | drv_active); | ||
1626 | } | ||
1627 | |||
1628 | /* | ||
1629 | * Clear RESET OWNER, will be set at next reset | ||
1630 | * by next RST_OWNER | ||
1631 | */ | ||
1632 | ha->flags.nic_core_reset_owner = 0; | ||
1633 | |||
1634 | /* Start Reset Recovery */ | ||
1635 | qla8044_device_bootstrap(vha); | ||
1636 | } | ||
1637 | } | ||
1638 | |||
1639 | static void | ||
1640 | qla8044_set_drv_active(struct scsi_qla_host *vha) | ||
1641 | { | ||
1642 | uint32_t drv_active; | ||
1643 | struct qla_hw_data *ha = vha->hw; | ||
1644 | |||
1645 | drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1646 | |||
1647 | /* For ISP8044, drv_active register has 1 bit per function, | ||
1648 | * shift 1 by func_num to set a bit for the function.*/ | ||
1649 | drv_active |= (1 << ha->portnum); | ||
1650 | |||
1651 | ql_log(ql_log_info, vha, 0xb0c8, | ||
1652 | "%s(%ld): drv_active: 0x%08x\n", | ||
1653 | __func__, vha->host_no, drv_active); | ||
1654 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active); | ||
1655 | } | ||
1656 | |||
1657 | static void | ||
1658 | qla8044_clear_idc_dontreset(struct scsi_qla_host *vha) | ||
1659 | { | ||
1660 | uint32_t idc_ctrl; | ||
1661 | struct qla_hw_data *ha = vha->hw; | ||
1662 | |||
1663 | idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); | ||
1664 | idc_ctrl &= ~DONTRESET_BIT0; | ||
1665 | ql_log(ql_log_info, vha, 0xb0c9, | ||
1666 | "%s: idc_ctrl = %d\n", __func__, | ||
1667 | idc_ctrl); | ||
1668 | qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl); | ||
1669 | } | ||
1670 | |||
1671 | static int | ||
1672 | qla8044_set_idc_ver(struct scsi_qla_host *vha) | ||
1673 | { | ||
1674 | int idc_ver; | ||
1675 | uint32_t drv_active; | ||
1676 | int rval = QLA_SUCCESS; | ||
1677 | struct qla_hw_data *ha = vha->hw; | ||
1678 | |||
1679 | drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1680 | if (drv_active == (1 << ha->portnum)) { | ||
1681 | idc_ver = qla8044_rd_direct(vha, | ||
1682 | QLA8044_CRB_DRV_IDC_VERSION_INDEX); | ||
1683 | idc_ver &= (~0xFF); | ||
1684 | idc_ver |= QLA8044_IDC_VER_MAJ_VALUE; | ||
1685 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_IDC_VERSION_INDEX, | ||
1686 | idc_ver); | ||
1687 | ql_log(ql_log_info, vha, 0xb0ca, | ||
1688 | "%s: IDC version updated to %d\n", | ||
1689 | __func__, idc_ver); | ||
1690 | } else { | ||
1691 | idc_ver = qla8044_rd_direct(vha, | ||
1692 | QLA8044_CRB_DRV_IDC_VERSION_INDEX); | ||
1693 | idc_ver &= 0xFF; | ||
1694 | if (QLA8044_IDC_VER_MAJ_VALUE != idc_ver) { | ||
1695 | ql_log(ql_log_info, vha, 0xb0cb, | ||
1696 | "%s: qla4xxx driver IDC version %d " | ||
1697 | "is not compatible with IDC version %d " | ||
1698 | "of other drivers!\n", | ||
1699 | __func__, QLA8044_IDC_VER_MAJ_VALUE, | ||
1700 | idc_ver); | ||
1701 | rval = QLA_FUNCTION_FAILED; | ||
1702 | goto exit_set_idc_ver; | ||
1703 | } | ||
1704 | } | ||
1705 | |||
1706 | /* Update IDC_MINOR_VERSION */ | ||
1707 | idc_ver = qla8044_rd_reg(ha, QLA8044_CRB_IDC_VER_MINOR); | ||
1708 | idc_ver &= ~(0x03 << (ha->portnum * 2)); | ||
1709 | idc_ver |= (QLA8044_IDC_VER_MIN_VALUE << (ha->portnum * 2)); | ||
1710 | qla8044_wr_reg(ha, QLA8044_CRB_IDC_VER_MINOR, idc_ver); | ||
1711 | |||
1712 | exit_set_idc_ver: | ||
1713 | return rval; | ||
1714 | } | ||
1715 | |||
1716 | static int | ||
1717 | qla8044_update_idc_reg(struct scsi_qla_host *vha) | ||
1718 | { | ||
1719 | uint32_t drv_active; | ||
1720 | int rval = QLA_SUCCESS; | ||
1721 | struct qla_hw_data *ha = vha->hw; | ||
1722 | |||
1723 | if (vha->flags.init_done) | ||
1724 | goto exit_update_idc_reg; | ||
1725 | |||
1726 | qla8044_idc_lock(ha); | ||
1727 | qla8044_set_drv_active(vha); | ||
1728 | |||
1729 | drv_active = qla8044_rd_direct(vha, | ||
1730 | QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1731 | |||
1732 | /* If we are the first driver to load and | ||
1733 | * ql2xdontresethba is not set, clear IDC_CTRL BIT0. */ | ||
1734 | if ((drv_active == (1 << ha->portnum)) && !ql2xdontresethba) | ||
1735 | qla8044_clear_idc_dontreset(vha); | ||
1736 | |||
1737 | rval = qla8044_set_idc_ver(vha); | ||
1738 | if (rval == QLA_FUNCTION_FAILED) | ||
1739 | qla8044_clear_drv_active(vha); | ||
1740 | qla8044_idc_unlock(ha); | ||
1741 | |||
1742 | exit_update_idc_reg: | ||
1743 | return rval; | ||
1744 | } | ||
1745 | |||
1746 | /** | ||
1747 | * qla8044_need_qsnt_handler - Code to start qsnt | ||
1748 | * @ha: pointer to adapter structure | ||
1749 | **/ | ||
1750 | static void | ||
1751 | qla8044_need_qsnt_handler(struct scsi_qla_host *vha) | ||
1752 | { | ||
1753 | unsigned long qsnt_timeout; | ||
1754 | uint32_t drv_state, drv_active, dev_state; | ||
1755 | struct qla_hw_data *ha = vha->hw; | ||
1756 | |||
1757 | if (vha->flags.online) | ||
1758 | qla2x00_quiesce_io(vha); | ||
1759 | else | ||
1760 | return; | ||
1761 | |||
1762 | qla8044_set_qsnt_ready(vha); | ||
1763 | |||
1764 | /* Wait for 30 secs for all functions to ack qsnt mode */ | ||
1765 | qsnt_timeout = jiffies + (QSNT_ACK_TOV * HZ); | ||
1766 | drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
1767 | drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1768 | |||
1769 | /* Shift drv_active by 1 to match drv_state. As quiescent ready bit | ||
1770 | position is at bit 1 and drv active is at bit 0 */ | ||
1771 | drv_active = drv_active << 1; | ||
1772 | |||
1773 | while (drv_state != drv_active) { | ||
1774 | if (time_after_eq(jiffies, qsnt_timeout)) { | ||
1775 | /* Other functions did not ack, changing state to | ||
1776 | * DEV_READY | ||
1777 | */ | ||
1778 | clear_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); | ||
1779 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
1780 | QLA8XXX_DEV_READY); | ||
1781 | qla8044_clear_qsnt_ready(vha); | ||
1782 | ql_log(ql_log_info, vha, 0xb0cc, | ||
1783 | "Timeout waiting for quiescent ack!!!\n"); | ||
1784 | return; | ||
1785 | } | ||
1786 | qla8044_idc_unlock(ha); | ||
1787 | msleep(1000); | ||
1788 | qla8044_idc_lock(ha); | ||
1789 | |||
1790 | drv_state = qla8044_rd_direct(vha, | ||
1791 | QLA8044_CRB_DRV_STATE_INDEX); | ||
1792 | drv_active = qla8044_rd_direct(vha, | ||
1793 | QLA8044_CRB_DRV_ACTIVE_INDEX); | ||
1794 | drv_active = drv_active << 1; | ||
1795 | } | ||
1796 | |||
1797 | /* All functions have Acked. Set quiescent state */ | ||
1798 | dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); | ||
1799 | |||
1800 | if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { | ||
1801 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
1802 | QLA8XXX_DEV_QUIESCENT); | ||
1803 | ql_log(ql_log_info, vha, 0xb0cd, | ||
1804 | "%s: HW State: QUIESCENT\n", __func__); | ||
1805 | } | ||
1806 | } | ||
1807 | |||
1808 | /* | ||
1809 | * qla8044_device_state_handler - Adapter state machine | ||
1810 | * @ha: pointer to host adapter structure. | ||
1811 | * | ||
1812 | * Note: IDC lock must be UNLOCKED upon entry | ||
1813 | **/ | ||
1814 | int | ||
1815 | qla8044_device_state_handler(struct scsi_qla_host *vha) | ||
1816 | { | ||
1817 | uint32_t dev_state; | ||
1818 | int rval = QLA_SUCCESS; | ||
1819 | unsigned long dev_init_timeout; | ||
1820 | struct qla_hw_data *ha = vha->hw; | ||
1821 | |||
1822 | rval = qla8044_update_idc_reg(vha); | ||
1823 | if (rval == QLA_FUNCTION_FAILED) | ||
1824 | goto exit_error; | ||
1825 | |||
1826 | dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); | ||
1827 | ql_dbg(ql_dbg_p3p, vha, 0xb0ce, | ||
1828 | "Device state is 0x%x = %s\n", | ||
1829 | dev_state, dev_state < MAX_STATES ? | ||
1830 | qdev_state(dev_state) : "Unknown"); | ||
1831 | |||
1832 | /* wait for 30 seconds for device to go ready */ | ||
1833 | dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); | ||
1834 | |||
1835 | qla8044_idc_lock(ha); | ||
1836 | |||
1837 | while (1) { | ||
1838 | if (time_after_eq(jiffies, dev_init_timeout)) { | ||
1839 | ql_log(ql_log_warn, vha, 0xb0cf, | ||
1840 | "%s: Device Init Failed 0x%x = %s\n", | ||
1841 | QLA2XXX_DRIVER_NAME, dev_state, | ||
1842 | dev_state < MAX_STATES ? | ||
1843 | qdev_state(dev_state) : "Unknown"); | ||
1844 | |||
1845 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
1846 | QLA8XXX_DEV_FAILED); | ||
1847 | } | ||
1848 | |||
1849 | dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); | ||
1850 | ql_log(ql_log_info, vha, 0xb0d0, | ||
1851 | "Device state is 0x%x = %s\n", | ||
1852 | dev_state, dev_state < MAX_STATES ? | ||
1853 | qdev_state(dev_state) : "Unknown"); | ||
1854 | |||
1855 | /* NOTE: Make sure idc unlocked upon exit of switch statement */ | ||
1856 | switch (dev_state) { | ||
1857 | case QLA8XXX_DEV_READY: | ||
1858 | ha->flags.nic_core_reset_owner = 0; | ||
1859 | goto exit; | ||
1860 | case QLA8XXX_DEV_COLD: | ||
1861 | rval = qla8044_device_bootstrap(vha); | ||
1862 | goto exit; | ||
1863 | case QLA8XXX_DEV_INITIALIZING: | ||
1864 | qla8044_idc_unlock(ha); | ||
1865 | msleep(1000); | ||
1866 | qla8044_idc_lock(ha); | ||
1867 | break; | ||
1868 | case QLA8XXX_DEV_NEED_RESET: | ||
1869 | /* For ISP8044, if NEED_RESET is set by any driver, | ||
1870 | * it should be honored, irrespective of IDC_CTRL | ||
1871 | * DONTRESET_BIT0 */ | ||
1872 | qla8044_need_reset_handler(vha); | ||
1873 | break; | ||
1874 | case QLA8XXX_DEV_NEED_QUIESCENT: | ||
1875 | /* idc locked/unlocked in handler */ | ||
1876 | qla8044_need_qsnt_handler(vha); | ||
1877 | |||
1878 | /* Reset the init timeout after qsnt handler */ | ||
1879 | dev_init_timeout = jiffies + | ||
1880 | (ha->fcoe_reset_timeout * HZ); | ||
1881 | break; | ||
1882 | case QLA8XXX_DEV_QUIESCENT: | ||
1883 | ql_log(ql_log_info, vha, 0xb0d1, | ||
1884 | "HW State: QUIESCENT\n"); | ||
1885 | |||
1886 | qla8044_idc_unlock(ha); | ||
1887 | msleep(1000); | ||
1888 | qla8044_idc_lock(ha); | ||
1889 | |||
1890 | /* Reset the init timeout after qsnt handler */ | ||
1891 | dev_init_timeout = jiffies + | ||
1892 | (ha->fcoe_reset_timeout * HZ); | ||
1893 | break; | ||
1894 | case QLA8XXX_DEV_FAILED: | ||
1895 | ha->flags.nic_core_reset_owner = 0; | ||
1896 | qla8044_idc_unlock(ha); | ||
1897 | qla8xxx_dev_failed_handler(vha); | ||
1898 | rval = QLA_FUNCTION_FAILED; | ||
1899 | qla8044_idc_lock(ha); | ||
1900 | goto exit; | ||
1901 | default: | ||
1902 | qla8044_idc_unlock(ha); | ||
1903 | qla8xxx_dev_failed_handler(vha); | ||
1904 | rval = QLA_FUNCTION_FAILED; | ||
1905 | qla8044_idc_lock(ha); | ||
1906 | goto exit; | ||
1907 | } | ||
1908 | } | ||
1909 | exit: | ||
1910 | qla8044_idc_unlock(ha); | ||
1911 | |||
1912 | exit_error: | ||
1913 | return rval; | ||
1914 | } | ||
1915 | |||
1916 | /** | ||
1917 | * qla4_8xxx_check_temp - Check the ISP82XX temperature. | ||
1918 | * @ha: adapter block pointer. | ||
1919 | * | ||
1920 | * Note: The caller should not hold the idc lock. | ||
1921 | **/ | ||
1922 | static int | ||
1923 | qla8044_check_temp(struct scsi_qla_host *vha) | ||
1924 | { | ||
1925 | uint32_t temp, temp_state, temp_val; | ||
1926 | int status = QLA_SUCCESS; | ||
1927 | |||
1928 | temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX); | ||
1929 | temp_state = qla82xx_get_temp_state(temp); | ||
1930 | temp_val = qla82xx_get_temp_val(temp); | ||
1931 | |||
1932 | if (temp_state == QLA82XX_TEMP_PANIC) { | ||
1933 | ql_log(ql_log_warn, vha, 0xb0d2, | ||
1934 | "Device temperature %d degrees C" | ||
1935 | " exceeds maximum allowed. Hardware has been shut" | ||
1936 | " down\n", temp_val); | ||
1937 | status = QLA_FUNCTION_FAILED; | ||
1938 | return status; | ||
1939 | } else if (temp_state == QLA82XX_TEMP_WARN) { | ||
1940 | ql_log(ql_log_warn, vha, 0xb0d3, | ||
1941 | "Device temperature %d" | ||
1942 | " degrees C exceeds operating range." | ||
1943 | " Immediate action needed.\n", temp_val); | ||
1944 | } | ||
1945 | return 0; | ||
1946 | } | ||
1947 | |||
1948 | int qla8044_read_temperature(scsi_qla_host_t *vha) | ||
1949 | { | ||
1950 | uint32_t temp; | ||
1951 | |||
1952 | temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX); | ||
1953 | return qla82xx_get_temp_val(temp); | ||
1954 | } | ||
1955 | |||
1956 | /** | ||
1957 | * qla8044_check_fw_alive - Check firmware health | ||
1958 | * @ha: Pointer to host adapter structure. | ||
1959 | * | ||
1960 | * Context: Interrupt | ||
1961 | **/ | ||
1962 | int | ||
1963 | qla8044_check_fw_alive(struct scsi_qla_host *vha) | ||
1964 | { | ||
1965 | uint32_t fw_heartbeat_counter; | ||
1966 | uint32_t halt_status1, halt_status2; | ||
1967 | int status = QLA_SUCCESS; | ||
1968 | |||
1969 | fw_heartbeat_counter = qla8044_rd_direct(vha, | ||
1970 | QLA8044_PEG_ALIVE_COUNTER_INDEX); | ||
1971 | |||
1972 | /* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */ | ||
1973 | if (fw_heartbeat_counter == 0xffffffff) { | ||
1974 | ql_dbg(ql_dbg_p3p, vha, 0xb0d4, | ||
1975 | "scsi%ld: %s: Device in frozen " | ||
1976 | "state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n", | ||
1977 | vha->host_no, __func__); | ||
1978 | return status; | ||
1979 | } | ||
1980 | |||
1981 | if (vha->fw_heartbeat_counter == fw_heartbeat_counter) { | ||
1982 | vha->seconds_since_last_heartbeat++; | ||
1983 | /* FW not alive after 2 seconds */ | ||
1984 | if (vha->seconds_since_last_heartbeat == 2) { | ||
1985 | vha->seconds_since_last_heartbeat = 0; | ||
1986 | halt_status1 = qla8044_rd_direct(vha, | ||
1987 | QLA8044_PEG_HALT_STATUS1_INDEX); | ||
1988 | halt_status2 = qla8044_rd_direct(vha, | ||
1989 | QLA8044_PEG_HALT_STATUS2_INDEX); | ||
1990 | |||
1991 | ql_log(ql_log_info, vha, 0xb0d5, | ||
1992 | "scsi(%ld): %s, ISP8044 " | ||
1993 | "Dumping hw/fw registers:\n" | ||
1994 | " PEG_HALT_STATUS1: 0x%x, " | ||
1995 | "PEG_HALT_STATUS2: 0x%x,\n", | ||
1996 | vha->host_no, __func__, halt_status1, | ||
1997 | halt_status2); | ||
1998 | status = QLA_FUNCTION_FAILED; | ||
1999 | } | ||
2000 | } else | ||
2001 | vha->seconds_since_last_heartbeat = 0; | ||
2002 | |||
2003 | vha->fw_heartbeat_counter = fw_heartbeat_counter; | ||
2004 | return status; | ||
2005 | } | ||
2006 | |||
2007 | void | ||
2008 | qla8044_watchdog(struct scsi_qla_host *vha) | ||
2009 | { | ||
2010 | uint32_t dev_state, halt_status; | ||
2011 | int halt_status_unrecoverable = 0; | ||
2012 | struct qla_hw_data *ha = vha->hw; | ||
2013 | |||
2014 | /* don't poll if reset is going on or FW hang in quiescent state */ | ||
2015 | if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
2016 | test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
2017 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags) || | ||
2018 | test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))) { | ||
2019 | dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); | ||
2020 | |||
2021 | if (qla8044_check_temp(vha)) { | ||
2022 | set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); | ||
2023 | ha->flags.isp82xx_fw_hung = 1; | ||
2024 | qla2xxx_wake_dpc(vha); | ||
2025 | } else if (dev_state == QLA8XXX_DEV_NEED_RESET && | ||
2026 | !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { | ||
2027 | ql_log(ql_log_info, vha, 0xb0d6, | ||
2028 | "%s: HW State: NEED RESET!\n", | ||
2029 | __func__); | ||
2030 | set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); | ||
2031 | qla2xxx_wake_dpc(vha); | ||
2032 | } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT && | ||
2033 | !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { | ||
2034 | ql_log(ql_log_info, vha, 0xb0d7, | ||
2035 | "%s: HW State: NEED QUIES detected!\n", | ||
2036 | __func__); | ||
2037 | set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); | ||
2038 | qla2xxx_wake_dpc(vha); | ||
2039 | } else { | ||
2040 | /* Check firmware health */ | ||
2041 | if (qla8044_check_fw_alive(vha)) { | ||
2042 | halt_status = qla8044_rd_direct(vha, | ||
2043 | QLA8044_PEG_HALT_STATUS1_INDEX); | ||
2044 | if (halt_status & | ||
2045 | QLA8044_HALT_STATUS_FW_RESET) { | ||
2046 | ql_log(ql_log_fatal, vha, | ||
2047 | 0xb0d8, "%s: Firmware " | ||
2048 | "error detected device " | ||
2049 | "is being reset\n", | ||
2050 | __func__); | ||
2051 | } else if (halt_status & | ||
2052 | QLA8044_HALT_STATUS_UNRECOVERABLE) { | ||
2053 | halt_status_unrecoverable = 1; | ||
2054 | } | ||
2055 | |||
2056 | /* Since we cannot change dev_state in interrupt | ||
2057 | * context, set appropriate DPC flag then wakeup | ||
2058 | * DPC */ | ||
2059 | if (halt_status_unrecoverable) { | ||
2060 | set_bit(ISP_UNRECOVERABLE, | ||
2061 | &vha->dpc_flags); | ||
2062 | } else { | ||
2063 | if (dev_state == | ||
2064 | QLA8XXX_DEV_QUIESCENT) { | ||
2065 | set_bit(FCOE_CTX_RESET_NEEDED, | ||
2066 | &vha->dpc_flags); | ||
2067 | ql_log(ql_log_info, vha, 0xb0d9, | ||
2068 | "%s: FW CONTEXT Reset " | ||
2069 | "needed!\n", __func__); | ||
2070 | } else { | ||
2071 | ql_log(ql_log_info, vha, | ||
2072 | 0xb0da, "%s: " | ||
2073 | "detect abort needed\n", | ||
2074 | __func__); | ||
2075 | set_bit(ISP_ABORT_NEEDED, | ||
2076 | &vha->dpc_flags); | ||
2077 | qla82xx_clear_pending_mbx(vha); | ||
2078 | } | ||
2079 | } | ||
2080 | ha->flags.isp82xx_fw_hung = 1; | ||
2081 | ql_log(ql_log_warn, vha, 0xb10a, | ||
2082 | "Firmware hung.\n"); | ||
2083 | qla2xxx_wake_dpc(vha); | ||
2084 | } | ||
2085 | } | ||
2086 | |||
2087 | } | ||
2088 | } | ||
2089 | |||
2090 | static int | ||
2091 | qla8044_minidump_process_control(struct scsi_qla_host *vha, | ||
2092 | struct qla8044_minidump_entry_hdr *entry_hdr) | ||
2093 | { | ||
2094 | struct qla8044_minidump_entry_crb *crb_entry; | ||
2095 | uint32_t read_value, opcode, poll_time, addr, index; | ||
2096 | uint32_t crb_addr, rval = QLA_SUCCESS; | ||
2097 | unsigned long wtime; | ||
2098 | struct qla8044_minidump_template_hdr *tmplt_hdr; | ||
2099 | int i; | ||
2100 | struct qla_hw_data *ha = vha->hw; | ||
2101 | |||
2102 | ql_dbg(ql_dbg_p3p, vha, 0xb0dd, "Entering fn: %s\n", __func__); | ||
2103 | tmplt_hdr = (struct qla8044_minidump_template_hdr *) | ||
2104 | ha->md_tmplt_hdr; | ||
2105 | crb_entry = (struct qla8044_minidump_entry_crb *)entry_hdr; | ||
2106 | |||
2107 | crb_addr = crb_entry->addr; | ||
2108 | for (i = 0; i < crb_entry->op_count; i++) { | ||
2109 | opcode = crb_entry->crb_ctrl.opcode; | ||
2110 | |||
2111 | if (opcode & QLA82XX_DBG_OPCODE_WR) { | ||
2112 | qla8044_wr_reg_indirect(vha, crb_addr, | ||
2113 | crb_entry->value_1); | ||
2114 | opcode &= ~QLA82XX_DBG_OPCODE_WR; | ||
2115 | } | ||
2116 | |||
2117 | if (opcode & QLA82XX_DBG_OPCODE_RW) { | ||
2118 | qla8044_rd_reg_indirect(vha, crb_addr, &read_value); | ||
2119 | qla8044_wr_reg_indirect(vha, crb_addr, read_value); | ||
2120 | opcode &= ~QLA82XX_DBG_OPCODE_RW; | ||
2121 | } | ||
2122 | |||
2123 | if (opcode & QLA82XX_DBG_OPCODE_AND) { | ||
2124 | qla8044_rd_reg_indirect(vha, crb_addr, &read_value); | ||
2125 | read_value &= crb_entry->value_2; | ||
2126 | opcode &= ~QLA82XX_DBG_OPCODE_AND; | ||
2127 | if (opcode & QLA82XX_DBG_OPCODE_OR) { | ||
2128 | read_value |= crb_entry->value_3; | ||
2129 | opcode &= ~QLA82XX_DBG_OPCODE_OR; | ||
2130 | } | ||
2131 | qla8044_wr_reg_indirect(vha, crb_addr, read_value); | ||
2132 | } | ||
2133 | if (opcode & QLA82XX_DBG_OPCODE_OR) { | ||
2134 | qla8044_rd_reg_indirect(vha, crb_addr, &read_value); | ||
2135 | read_value |= crb_entry->value_3; | ||
2136 | qla8044_wr_reg_indirect(vha, crb_addr, read_value); | ||
2137 | opcode &= ~QLA82XX_DBG_OPCODE_OR; | ||
2138 | } | ||
2139 | if (opcode & QLA82XX_DBG_OPCODE_POLL) { | ||
2140 | poll_time = crb_entry->crb_strd.poll_timeout; | ||
2141 | wtime = jiffies + poll_time; | ||
2142 | qla8044_rd_reg_indirect(vha, crb_addr, &read_value); | ||
2143 | |||
2144 | do { | ||
2145 | if ((read_value & crb_entry->value_2) == | ||
2146 | crb_entry->value_1) { | ||
2147 | break; | ||
2148 | } else if (time_after_eq(jiffies, wtime)) { | ||
2149 | /* capturing dump failed */ | ||
2150 | rval = QLA_FUNCTION_FAILED; | ||
2151 | break; | ||
2152 | } else { | ||
2153 | qla8044_rd_reg_indirect(vha, | ||
2154 | crb_addr, &read_value); | ||
2155 | } | ||
2156 | } while (1); | ||
2157 | opcode &= ~QLA82XX_DBG_OPCODE_POLL; | ||
2158 | } | ||
2159 | |||
2160 | if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) { | ||
2161 | if (crb_entry->crb_strd.state_index_a) { | ||
2162 | index = crb_entry->crb_strd.state_index_a; | ||
2163 | addr = tmplt_hdr->saved_state_array[index]; | ||
2164 | } else { | ||
2165 | addr = crb_addr; | ||
2166 | } | ||
2167 | |||
2168 | qla8044_rd_reg_indirect(vha, addr, &read_value); | ||
2169 | index = crb_entry->crb_ctrl.state_index_v; | ||
2170 | tmplt_hdr->saved_state_array[index] = read_value; | ||
2171 | opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE; | ||
2172 | } | ||
2173 | |||
2174 | if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) { | ||
2175 | if (crb_entry->crb_strd.state_index_a) { | ||
2176 | index = crb_entry->crb_strd.state_index_a; | ||
2177 | addr = tmplt_hdr->saved_state_array[index]; | ||
2178 | } else { | ||
2179 | addr = crb_addr; | ||
2180 | } | ||
2181 | |||
2182 | if (crb_entry->crb_ctrl.state_index_v) { | ||
2183 | index = crb_entry->crb_ctrl.state_index_v; | ||
2184 | read_value = | ||
2185 | tmplt_hdr->saved_state_array[index]; | ||
2186 | } else { | ||
2187 | read_value = crb_entry->value_1; | ||
2188 | } | ||
2189 | |||
2190 | qla8044_wr_reg_indirect(vha, addr, read_value); | ||
2191 | opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE; | ||
2192 | } | ||
2193 | |||
2194 | if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) { | ||
2195 | index = crb_entry->crb_ctrl.state_index_v; | ||
2196 | read_value = tmplt_hdr->saved_state_array[index]; | ||
2197 | read_value <<= crb_entry->crb_ctrl.shl; | ||
2198 | read_value >>= crb_entry->crb_ctrl.shr; | ||
2199 | if (crb_entry->value_2) | ||
2200 | read_value &= crb_entry->value_2; | ||
2201 | read_value |= crb_entry->value_3; | ||
2202 | read_value += crb_entry->value_1; | ||
2203 | tmplt_hdr->saved_state_array[index] = read_value; | ||
2204 | opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE; | ||
2205 | } | ||
2206 | crb_addr += crb_entry->crb_strd.addr_stride; | ||
2207 | } | ||
2208 | return rval; | ||
2209 | } | ||
2210 | |||
2211 | static void | ||
2212 | qla8044_minidump_process_rdcrb(struct scsi_qla_host *vha, | ||
2213 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2214 | { | ||
2215 | uint32_t r_addr, r_stride, loop_cnt, i, r_value; | ||
2216 | struct qla8044_minidump_entry_crb *crb_hdr; | ||
2217 | uint32_t *data_ptr = *d_ptr; | ||
2218 | |||
2219 | ql_dbg(ql_dbg_p3p, vha, 0xb0de, "Entering fn: %s\n", __func__); | ||
2220 | crb_hdr = (struct qla8044_minidump_entry_crb *)entry_hdr; | ||
2221 | r_addr = crb_hdr->addr; | ||
2222 | r_stride = crb_hdr->crb_strd.addr_stride; | ||
2223 | loop_cnt = crb_hdr->op_count; | ||
2224 | |||
2225 | for (i = 0; i < loop_cnt; i++) { | ||
2226 | qla8044_rd_reg_indirect(vha, r_addr, &r_value); | ||
2227 | *data_ptr++ = r_addr; | ||
2228 | *data_ptr++ = r_value; | ||
2229 | r_addr += r_stride; | ||
2230 | } | ||
2231 | *d_ptr = data_ptr; | ||
2232 | } | ||
2233 | |||
2234 | static int | ||
2235 | qla8044_minidump_process_rdmem(struct scsi_qla_host *vha, | ||
2236 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2237 | { | ||
2238 | uint32_t r_addr, r_value, r_data; | ||
2239 | uint32_t i, j, loop_cnt; | ||
2240 | struct qla8044_minidump_entry_rdmem *m_hdr; | ||
2241 | unsigned long flags; | ||
2242 | uint32_t *data_ptr = *d_ptr; | ||
2243 | struct qla_hw_data *ha = vha->hw; | ||
2244 | |||
2245 | ql_dbg(ql_dbg_p3p, vha, 0xb0df, "Entering fn: %s\n", __func__); | ||
2246 | m_hdr = (struct qla8044_minidump_entry_rdmem *)entry_hdr; | ||
2247 | r_addr = m_hdr->read_addr; | ||
2248 | loop_cnt = m_hdr->read_data_size/16; | ||
2249 | |||
2250 | ql_dbg(ql_dbg_p3p, vha, 0xb0f0, | ||
2251 | "[%s]: Read addr: 0x%x, read_data_size: 0x%x\n", | ||
2252 | __func__, r_addr, m_hdr->read_data_size); | ||
2253 | |||
2254 | if (r_addr & 0xf) { | ||
2255 | ql_dbg(ql_dbg_p3p, vha, 0xb0f1, | ||
2256 | "[%s]: Read addr 0x%x not 16 bytes alligned\n", | ||
2257 | __func__, r_addr); | ||
2258 | return QLA_FUNCTION_FAILED; | ||
2259 | } | ||
2260 | |||
2261 | if (m_hdr->read_data_size % 16) { | ||
2262 | ql_dbg(ql_dbg_p3p, vha, 0xb0f2, | ||
2263 | "[%s]: Read data[0x%x] not multiple of 16 bytes\n", | ||
2264 | __func__, m_hdr->read_data_size); | ||
2265 | return QLA_FUNCTION_FAILED; | ||
2266 | } | ||
2267 | |||
2268 | ql_dbg(ql_dbg_p3p, vha, 0xb0f3, | ||
2269 | "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n", | ||
2270 | __func__, r_addr, m_hdr->read_data_size, loop_cnt); | ||
2271 | |||
2272 | write_lock_irqsave(&ha->hw_lock, flags); | ||
2273 | for (i = 0; i < loop_cnt; i++) { | ||
2274 | qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_LO, r_addr); | ||
2275 | r_value = 0; | ||
2276 | qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, r_value); | ||
2277 | r_value = MIU_TA_CTL_ENABLE; | ||
2278 | qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value); | ||
2279 | r_value = MIU_TA_CTL_START_ENABLE; | ||
2280 | qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value); | ||
2281 | |||
2282 | for (j = 0; j < MAX_CTL_CHECK; j++) { | ||
2283 | qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, | ||
2284 | &r_value); | ||
2285 | if ((r_value & MIU_TA_CTL_BUSY) == 0) | ||
2286 | break; | ||
2287 | } | ||
2288 | |||
2289 | if (j >= MAX_CTL_CHECK) { | ||
2290 | printk_ratelimited(KERN_ERR | ||
2291 | "%s: failed to read through agent\n", __func__); | ||
2292 | write_unlock_irqrestore(&ha->hw_lock, flags); | ||
2293 | return QLA_SUCCESS; | ||
2294 | } | ||
2295 | |||
2296 | for (j = 0; j < 4; j++) { | ||
2297 | qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_RDDATA[j], | ||
2298 | &r_data); | ||
2299 | *data_ptr++ = r_data; | ||
2300 | } | ||
2301 | |||
2302 | r_addr += 16; | ||
2303 | } | ||
2304 | write_unlock_irqrestore(&ha->hw_lock, flags); | ||
2305 | |||
2306 | ql_dbg(ql_dbg_p3p, vha, 0xb0f4, | ||
2307 | "Leaving fn: %s datacount: 0x%x\n", | ||
2308 | __func__, (loop_cnt * 16)); | ||
2309 | |||
2310 | *d_ptr = data_ptr; | ||
2311 | return QLA_SUCCESS; | ||
2312 | } | ||
2313 | |||
2314 | /* ISP83xx flash read for _RDROM _BOARD */ | ||
2315 | static uint32_t | ||
2316 | qla8044_minidump_process_rdrom(struct scsi_qla_host *vha, | ||
2317 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2318 | { | ||
2319 | uint32_t fl_addr, u32_count, rval; | ||
2320 | struct qla8044_minidump_entry_rdrom *rom_hdr; | ||
2321 | uint32_t *data_ptr = *d_ptr; | ||
2322 | |||
2323 | rom_hdr = (struct qla8044_minidump_entry_rdrom *)entry_hdr; | ||
2324 | fl_addr = rom_hdr->read_addr; | ||
2325 | u32_count = (rom_hdr->read_data_size)/sizeof(uint32_t); | ||
2326 | |||
2327 | ql_dbg(ql_dbg_p3p, vha, 0xb0f5, "[%s]: fl_addr: 0x%x, count: 0x%x\n", | ||
2328 | __func__, fl_addr, u32_count); | ||
2329 | |||
2330 | rval = qla8044_lockless_flash_read_u32(vha, fl_addr, | ||
2331 | (u8 *)(data_ptr), u32_count); | ||
2332 | |||
2333 | if (rval != QLA_SUCCESS) { | ||
2334 | ql_log(ql_log_fatal, vha, 0xb0f6, | ||
2335 | "%s: Flash Read Error,Count=%d\n", __func__, u32_count); | ||
2336 | return QLA_FUNCTION_FAILED; | ||
2337 | } else { | ||
2338 | data_ptr += u32_count; | ||
2339 | *d_ptr = data_ptr; | ||
2340 | return QLA_SUCCESS; | ||
2341 | } | ||
2342 | } | ||
2343 | |||
2344 | static void | ||
2345 | qla8044_mark_entry_skipped(struct scsi_qla_host *vha, | ||
2346 | struct qla8044_minidump_entry_hdr *entry_hdr, int index) | ||
2347 | { | ||
2348 | entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG; | ||
2349 | |||
2350 | ql_log(ql_log_info, vha, 0xb0f7, | ||
2351 | "scsi(%ld): Skipping entry[%d]: ETYPE[0x%x]-ELEVEL[0x%x]\n", | ||
2352 | vha->host_no, index, entry_hdr->entry_type, | ||
2353 | entry_hdr->d_ctrl.entry_capture_mask); | ||
2354 | } | ||
2355 | |||
2356 | static int | ||
2357 | qla8044_minidump_process_l2tag(struct scsi_qla_host *vha, | ||
2358 | struct qla8044_minidump_entry_hdr *entry_hdr, | ||
2359 | uint32_t **d_ptr) | ||
2360 | { | ||
2361 | uint32_t addr, r_addr, c_addr, t_r_addr; | ||
2362 | uint32_t i, k, loop_count, t_value, r_cnt, r_value; | ||
2363 | unsigned long p_wait, w_time, p_mask; | ||
2364 | uint32_t c_value_w, c_value_r; | ||
2365 | struct qla8044_minidump_entry_cache *cache_hdr; | ||
2366 | int rval = QLA_FUNCTION_FAILED; | ||
2367 | uint32_t *data_ptr = *d_ptr; | ||
2368 | |||
2369 | ql_dbg(ql_dbg_p3p, vha, 0xb0f8, "Entering fn: %s\n", __func__); | ||
2370 | cache_hdr = (struct qla8044_minidump_entry_cache *)entry_hdr; | ||
2371 | |||
2372 | loop_count = cache_hdr->op_count; | ||
2373 | r_addr = cache_hdr->read_addr; | ||
2374 | c_addr = cache_hdr->control_addr; | ||
2375 | c_value_w = cache_hdr->cache_ctrl.write_value; | ||
2376 | |||
2377 | t_r_addr = cache_hdr->tag_reg_addr; | ||
2378 | t_value = cache_hdr->addr_ctrl.init_tag_value; | ||
2379 | r_cnt = cache_hdr->read_ctrl.read_addr_cnt; | ||
2380 | p_wait = cache_hdr->cache_ctrl.poll_wait; | ||
2381 | p_mask = cache_hdr->cache_ctrl.poll_mask; | ||
2382 | |||
2383 | for (i = 0; i < loop_count; i++) { | ||
2384 | qla8044_wr_reg_indirect(vha, t_r_addr, t_value); | ||
2385 | if (c_value_w) | ||
2386 | qla8044_wr_reg_indirect(vha, c_addr, c_value_w); | ||
2387 | |||
2388 | if (p_mask) { | ||
2389 | w_time = jiffies + p_wait; | ||
2390 | do { | ||
2391 | qla8044_rd_reg_indirect(vha, c_addr, | ||
2392 | &c_value_r); | ||
2393 | if ((c_value_r & p_mask) == 0) { | ||
2394 | break; | ||
2395 | } else if (time_after_eq(jiffies, w_time)) { | ||
2396 | /* capturing dump failed */ | ||
2397 | return rval; | ||
2398 | } | ||
2399 | } while (1); | ||
2400 | } | ||
2401 | |||
2402 | addr = r_addr; | ||
2403 | for (k = 0; k < r_cnt; k++) { | ||
2404 | qla8044_rd_reg_indirect(vha, addr, &r_value); | ||
2405 | *data_ptr++ = r_value; | ||
2406 | addr += cache_hdr->read_ctrl.read_addr_stride; | ||
2407 | } | ||
2408 | t_value += cache_hdr->addr_ctrl.tag_value_stride; | ||
2409 | } | ||
2410 | *d_ptr = data_ptr; | ||
2411 | return QLA_SUCCESS; | ||
2412 | } | ||
2413 | |||
2414 | static void | ||
2415 | qla8044_minidump_process_l1cache(struct scsi_qla_host *vha, | ||
2416 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2417 | { | ||
2418 | uint32_t addr, r_addr, c_addr, t_r_addr; | ||
2419 | uint32_t i, k, loop_count, t_value, r_cnt, r_value; | ||
2420 | uint32_t c_value_w; | ||
2421 | struct qla8044_minidump_entry_cache *cache_hdr; | ||
2422 | uint32_t *data_ptr = *d_ptr; | ||
2423 | |||
2424 | cache_hdr = (struct qla8044_minidump_entry_cache *)entry_hdr; | ||
2425 | loop_count = cache_hdr->op_count; | ||
2426 | r_addr = cache_hdr->read_addr; | ||
2427 | c_addr = cache_hdr->control_addr; | ||
2428 | c_value_w = cache_hdr->cache_ctrl.write_value; | ||
2429 | |||
2430 | t_r_addr = cache_hdr->tag_reg_addr; | ||
2431 | t_value = cache_hdr->addr_ctrl.init_tag_value; | ||
2432 | r_cnt = cache_hdr->read_ctrl.read_addr_cnt; | ||
2433 | |||
2434 | for (i = 0; i < loop_count; i++) { | ||
2435 | qla8044_wr_reg_indirect(vha, t_r_addr, t_value); | ||
2436 | qla8044_wr_reg_indirect(vha, c_addr, c_value_w); | ||
2437 | addr = r_addr; | ||
2438 | for (k = 0; k < r_cnt; k++) { | ||
2439 | qla8044_rd_reg_indirect(vha, addr, &r_value); | ||
2440 | *data_ptr++ = r_value; | ||
2441 | addr += cache_hdr->read_ctrl.read_addr_stride; | ||
2442 | } | ||
2443 | t_value += cache_hdr->addr_ctrl.tag_value_stride; | ||
2444 | } | ||
2445 | *d_ptr = data_ptr; | ||
2446 | } | ||
2447 | |||
2448 | static void | ||
2449 | qla8044_minidump_process_rdocm(struct scsi_qla_host *vha, | ||
2450 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2451 | { | ||
2452 | uint32_t r_addr, r_stride, loop_cnt, i, r_value; | ||
2453 | struct qla8044_minidump_entry_rdocm *ocm_hdr; | ||
2454 | uint32_t *data_ptr = *d_ptr; | ||
2455 | struct qla_hw_data *ha = vha->hw; | ||
2456 | |||
2457 | ql_dbg(ql_dbg_p3p, vha, 0xb0f9, "Entering fn: %s\n", __func__); | ||
2458 | |||
2459 | ocm_hdr = (struct qla8044_minidump_entry_rdocm *)entry_hdr; | ||
2460 | r_addr = ocm_hdr->read_addr; | ||
2461 | r_stride = ocm_hdr->read_addr_stride; | ||
2462 | loop_cnt = ocm_hdr->op_count; | ||
2463 | |||
2464 | ql_dbg(ql_dbg_p3p, vha, 0xb0fa, | ||
2465 | "[%s]: r_addr: 0x%x, r_stride: 0x%x, loop_cnt: 0x%x\n", | ||
2466 | __func__, r_addr, r_stride, loop_cnt); | ||
2467 | |||
2468 | for (i = 0; i < loop_cnt; i++) { | ||
2469 | r_value = readl((void __iomem *)(r_addr + ha->nx_pcibase)); | ||
2470 | *data_ptr++ = r_value; | ||
2471 | r_addr += r_stride; | ||
2472 | } | ||
2473 | ql_dbg(ql_dbg_p3p, vha, 0xb0fb, "Leaving fn: %s datacount: 0x%lx\n", | ||
2474 | __func__, (long unsigned int) (loop_cnt * sizeof(uint32_t))); | ||
2475 | |||
2476 | *d_ptr = data_ptr; | ||
2477 | } | ||
2478 | |||
2479 | static void | ||
2480 | qla8044_minidump_process_rdmux(struct scsi_qla_host *vha, | ||
2481 | struct qla8044_minidump_entry_hdr *entry_hdr, | ||
2482 | uint32_t **d_ptr) | ||
2483 | { | ||
2484 | uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value; | ||
2485 | struct qla8044_minidump_entry_mux *mux_hdr; | ||
2486 | uint32_t *data_ptr = *d_ptr; | ||
2487 | |||
2488 | ql_dbg(ql_dbg_p3p, vha, 0xb0fc, "Entering fn: %s\n", __func__); | ||
2489 | |||
2490 | mux_hdr = (struct qla8044_minidump_entry_mux *)entry_hdr; | ||
2491 | r_addr = mux_hdr->read_addr; | ||
2492 | s_addr = mux_hdr->select_addr; | ||
2493 | s_stride = mux_hdr->select_value_stride; | ||
2494 | s_value = mux_hdr->select_value; | ||
2495 | loop_cnt = mux_hdr->op_count; | ||
2496 | |||
2497 | for (i = 0; i < loop_cnt; i++) { | ||
2498 | qla8044_wr_reg_indirect(vha, s_addr, s_value); | ||
2499 | qla8044_rd_reg_indirect(vha, r_addr, &r_value); | ||
2500 | *data_ptr++ = s_value; | ||
2501 | *data_ptr++ = r_value; | ||
2502 | s_value += s_stride; | ||
2503 | } | ||
2504 | *d_ptr = data_ptr; | ||
2505 | } | ||
2506 | |||
2507 | static void | ||
2508 | qla8044_minidump_process_queue(struct scsi_qla_host *vha, | ||
2509 | struct qla8044_minidump_entry_hdr *entry_hdr, | ||
2510 | uint32_t **d_ptr) | ||
2511 | { | ||
2512 | uint32_t s_addr, r_addr; | ||
2513 | uint32_t r_stride, r_value, r_cnt, qid = 0; | ||
2514 | uint32_t i, k, loop_cnt; | ||
2515 | struct qla8044_minidump_entry_queue *q_hdr; | ||
2516 | uint32_t *data_ptr = *d_ptr; | ||
2517 | |||
2518 | ql_dbg(ql_dbg_p3p, vha, 0xb0fd, "Entering fn: %s\n", __func__); | ||
2519 | q_hdr = (struct qla8044_minidump_entry_queue *)entry_hdr; | ||
2520 | s_addr = q_hdr->select_addr; | ||
2521 | r_cnt = q_hdr->rd_strd.read_addr_cnt; | ||
2522 | r_stride = q_hdr->rd_strd.read_addr_stride; | ||
2523 | loop_cnt = q_hdr->op_count; | ||
2524 | |||
2525 | for (i = 0; i < loop_cnt; i++) { | ||
2526 | qla8044_wr_reg_indirect(vha, s_addr, qid); | ||
2527 | r_addr = q_hdr->read_addr; | ||
2528 | for (k = 0; k < r_cnt; k++) { | ||
2529 | qla8044_rd_reg_indirect(vha, r_addr, &r_value); | ||
2530 | *data_ptr++ = r_value; | ||
2531 | r_addr += r_stride; | ||
2532 | } | ||
2533 | qid += q_hdr->q_strd.queue_id_stride; | ||
2534 | } | ||
2535 | *d_ptr = data_ptr; | ||
2536 | } | ||
2537 | |||
2538 | /* ISP83xx functions to process new minidump entries... */ | ||
2539 | static uint32_t | ||
2540 | qla8044_minidump_process_pollrd(struct scsi_qla_host *vha, | ||
2541 | struct qla8044_minidump_entry_hdr *entry_hdr, | ||
2542 | uint32_t **d_ptr) | ||
2543 | { | ||
2544 | uint32_t r_addr, s_addr, s_value, r_value, poll_wait, poll_mask; | ||
2545 | uint16_t s_stride, i; | ||
2546 | struct qla8044_minidump_entry_pollrd *pollrd_hdr; | ||
2547 | uint32_t *data_ptr = *d_ptr; | ||
2548 | |||
2549 | pollrd_hdr = (struct qla8044_minidump_entry_pollrd *) entry_hdr; | ||
2550 | s_addr = pollrd_hdr->select_addr; | ||
2551 | r_addr = pollrd_hdr->read_addr; | ||
2552 | s_value = pollrd_hdr->select_value; | ||
2553 | s_stride = pollrd_hdr->select_value_stride; | ||
2554 | |||
2555 | poll_wait = pollrd_hdr->poll_wait; | ||
2556 | poll_mask = pollrd_hdr->poll_mask; | ||
2557 | |||
2558 | for (i = 0; i < pollrd_hdr->op_count; i++) { | ||
2559 | qla8044_wr_reg_indirect(vha, s_addr, s_value); | ||
2560 | poll_wait = pollrd_hdr->poll_wait; | ||
2561 | while (1) { | ||
2562 | qla8044_rd_reg_indirect(vha, s_addr, &r_value); | ||
2563 | if ((r_value & poll_mask) != 0) { | ||
2564 | break; | ||
2565 | } else { | ||
2566 | usleep_range(1000, 1100); | ||
2567 | if (--poll_wait == 0) { | ||
2568 | ql_log(ql_log_fatal, vha, 0xb0fe, | ||
2569 | "%s: TIMEOUT\n", __func__); | ||
2570 | goto error; | ||
2571 | } | ||
2572 | } | ||
2573 | } | ||
2574 | qla8044_rd_reg_indirect(vha, r_addr, &r_value); | ||
2575 | *data_ptr++ = s_value; | ||
2576 | *data_ptr++ = r_value; | ||
2577 | |||
2578 | s_value += s_stride; | ||
2579 | } | ||
2580 | *d_ptr = data_ptr; | ||
2581 | return QLA_SUCCESS; | ||
2582 | |||
2583 | error: | ||
2584 | return QLA_FUNCTION_FAILED; | ||
2585 | } | ||
2586 | |||
2587 | static void | ||
2588 | qla8044_minidump_process_rdmux2(struct scsi_qla_host *vha, | ||
2589 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2590 | { | ||
2591 | uint32_t sel_val1, sel_val2, t_sel_val, data, i; | ||
2592 | uint32_t sel_addr1, sel_addr2, sel_val_mask, read_addr; | ||
2593 | struct qla8044_minidump_entry_rdmux2 *rdmux2_hdr; | ||
2594 | uint32_t *data_ptr = *d_ptr; | ||
2595 | |||
2596 | rdmux2_hdr = (struct qla8044_minidump_entry_rdmux2 *) entry_hdr; | ||
2597 | sel_val1 = rdmux2_hdr->select_value_1; | ||
2598 | sel_val2 = rdmux2_hdr->select_value_2; | ||
2599 | sel_addr1 = rdmux2_hdr->select_addr_1; | ||
2600 | sel_addr2 = rdmux2_hdr->select_addr_2; | ||
2601 | sel_val_mask = rdmux2_hdr->select_value_mask; | ||
2602 | read_addr = rdmux2_hdr->read_addr; | ||
2603 | |||
2604 | for (i = 0; i < rdmux2_hdr->op_count; i++) { | ||
2605 | qla8044_wr_reg_indirect(vha, sel_addr1, sel_val1); | ||
2606 | t_sel_val = sel_val1 & sel_val_mask; | ||
2607 | *data_ptr++ = t_sel_val; | ||
2608 | |||
2609 | qla8044_wr_reg_indirect(vha, sel_addr2, t_sel_val); | ||
2610 | qla8044_rd_reg_indirect(vha, read_addr, &data); | ||
2611 | |||
2612 | *data_ptr++ = data; | ||
2613 | |||
2614 | qla8044_wr_reg_indirect(vha, sel_addr1, sel_val2); | ||
2615 | t_sel_val = sel_val2 & sel_val_mask; | ||
2616 | *data_ptr++ = t_sel_val; | ||
2617 | |||
2618 | qla8044_wr_reg_indirect(vha, sel_addr2, t_sel_val); | ||
2619 | qla8044_rd_reg_indirect(vha, read_addr, &data); | ||
2620 | |||
2621 | *data_ptr++ = data; | ||
2622 | |||
2623 | sel_val1 += rdmux2_hdr->select_value_stride; | ||
2624 | sel_val2 += rdmux2_hdr->select_value_stride; | ||
2625 | } | ||
2626 | |||
2627 | *d_ptr = data_ptr; | ||
2628 | } | ||
2629 | |||
2630 | static uint32_t | ||
2631 | qla8044_minidump_process_pollrdmwr(struct scsi_qla_host *vha, | ||
2632 | struct qla8044_minidump_entry_hdr *entry_hdr, | ||
2633 | uint32_t **d_ptr) | ||
2634 | { | ||
2635 | uint32_t poll_wait, poll_mask, r_value, data; | ||
2636 | uint32_t addr_1, addr_2, value_1, value_2; | ||
2637 | struct qla8044_minidump_entry_pollrdmwr *poll_hdr; | ||
2638 | uint32_t *data_ptr = *d_ptr; | ||
2639 | |||
2640 | poll_hdr = (struct qla8044_minidump_entry_pollrdmwr *) entry_hdr; | ||
2641 | addr_1 = poll_hdr->addr_1; | ||
2642 | addr_2 = poll_hdr->addr_2; | ||
2643 | value_1 = poll_hdr->value_1; | ||
2644 | value_2 = poll_hdr->value_2; | ||
2645 | poll_mask = poll_hdr->poll_mask; | ||
2646 | |||
2647 | qla8044_wr_reg_indirect(vha, addr_1, value_1); | ||
2648 | |||
2649 | poll_wait = poll_hdr->poll_wait; | ||
2650 | while (1) { | ||
2651 | qla8044_rd_reg_indirect(vha, addr_1, &r_value); | ||
2652 | |||
2653 | if ((r_value & poll_mask) != 0) { | ||
2654 | break; | ||
2655 | } else { | ||
2656 | usleep_range(1000, 1100); | ||
2657 | if (--poll_wait == 0) { | ||
2658 | ql_log(ql_log_fatal, vha, 0xb0ff, | ||
2659 | "%s: TIMEOUT\n", __func__); | ||
2660 | goto error; | ||
2661 | } | ||
2662 | } | ||
2663 | } | ||
2664 | |||
2665 | qla8044_rd_reg_indirect(vha, addr_2, &data); | ||
2666 | data &= poll_hdr->modify_mask; | ||
2667 | qla8044_wr_reg_indirect(vha, addr_2, data); | ||
2668 | qla8044_wr_reg_indirect(vha, addr_1, value_2); | ||
2669 | |||
2670 | poll_wait = poll_hdr->poll_wait; | ||
2671 | while (1) { | ||
2672 | qla8044_rd_reg_indirect(vha, addr_1, &r_value); | ||
2673 | |||
2674 | if ((r_value & poll_mask) != 0) { | ||
2675 | break; | ||
2676 | } else { | ||
2677 | usleep_range(1000, 1100); | ||
2678 | if (--poll_wait == 0) { | ||
2679 | ql_log(ql_log_fatal, vha, 0xb100, | ||
2680 | "%s: TIMEOUT2\n", __func__); | ||
2681 | goto error; | ||
2682 | } | ||
2683 | } | ||
2684 | } | ||
2685 | |||
2686 | *data_ptr++ = addr_2; | ||
2687 | *data_ptr++ = data; | ||
2688 | |||
2689 | *d_ptr = data_ptr; | ||
2690 | |||
2691 | return QLA_SUCCESS; | ||
2692 | |||
2693 | error: | ||
2694 | return QLA_FUNCTION_FAILED; | ||
2695 | } | ||
2696 | |||
2697 | #define ISP8044_PEX_DMA_ENGINE_INDEX 8 | ||
2698 | #define ISP8044_PEX_DMA_BASE_ADDRESS 0x77320000 | ||
2699 | #define ISP8044_PEX_DMA_NUM_OFFSET 0x10000 | ||
2700 | #define ISP8044_PEX_DMA_CMD_ADDR_LOW 0x0 | ||
2701 | #define ISP8044_PEX_DMA_CMD_ADDR_HIGH 0x04 | ||
2702 | #define ISP8044_PEX_DMA_CMD_STS_AND_CNTRL 0x08 | ||
2703 | |||
2704 | #define ISP8044_PEX_DMA_READ_SIZE (16 * 1024) | ||
2705 | #define ISP8044_PEX_DMA_MAX_WAIT (100 * 100) /* Max wait of 100 msecs */ | ||
2706 | |||
2707 | static int | ||
2708 | qla8044_check_dma_engine_state(struct scsi_qla_host *vha) | ||
2709 | { | ||
2710 | struct qla_hw_data *ha = vha->hw; | ||
2711 | int rval = QLA_SUCCESS; | ||
2712 | uint32_t dma_eng_num = 0, cmd_sts_and_cntrl = 0; | ||
2713 | uint64_t dma_base_addr = 0; | ||
2714 | struct qla8044_minidump_template_hdr *tmplt_hdr = NULL; | ||
2715 | |||
2716 | tmplt_hdr = ha->md_tmplt_hdr; | ||
2717 | dma_eng_num = | ||
2718 | tmplt_hdr->saved_state_array[ISP8044_PEX_DMA_ENGINE_INDEX]; | ||
2719 | dma_base_addr = ISP8044_PEX_DMA_BASE_ADDRESS + | ||
2720 | (dma_eng_num * ISP8044_PEX_DMA_NUM_OFFSET); | ||
2721 | |||
2722 | /* Read the pex-dma's command-status-and-control register. */ | ||
2723 | rval = qla8044_rd_reg_indirect(vha, | ||
2724 | (dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL), | ||
2725 | &cmd_sts_and_cntrl); | ||
2726 | if (rval) | ||
2727 | return QLA_FUNCTION_FAILED; | ||
2728 | |||
2729 | /* Check if requested pex-dma engine is available. */ | ||
2730 | if (cmd_sts_and_cntrl & BIT_31) | ||
2731 | return QLA_SUCCESS; | ||
2732 | |||
2733 | return QLA_FUNCTION_FAILED; | ||
2734 | } | ||
2735 | |||
2736 | static int | ||
2737 | qla8044_start_pex_dma(struct scsi_qla_host *vha, | ||
2738 | struct qla8044_minidump_entry_rdmem_pex_dma *m_hdr) | ||
2739 | { | ||
2740 | struct qla_hw_data *ha = vha->hw; | ||
2741 | int rval = QLA_SUCCESS, wait = 0; | ||
2742 | uint32_t dma_eng_num = 0, cmd_sts_and_cntrl = 0; | ||
2743 | uint64_t dma_base_addr = 0; | ||
2744 | struct qla8044_minidump_template_hdr *tmplt_hdr = NULL; | ||
2745 | |||
2746 | tmplt_hdr = ha->md_tmplt_hdr; | ||
2747 | dma_eng_num = | ||
2748 | tmplt_hdr->saved_state_array[ISP8044_PEX_DMA_ENGINE_INDEX]; | ||
2749 | dma_base_addr = ISP8044_PEX_DMA_BASE_ADDRESS + | ||
2750 | (dma_eng_num * ISP8044_PEX_DMA_NUM_OFFSET); | ||
2751 | |||
2752 | rval = qla8044_wr_reg_indirect(vha, | ||
2753 | dma_base_addr + ISP8044_PEX_DMA_CMD_ADDR_LOW, | ||
2754 | m_hdr->desc_card_addr); | ||
2755 | if (rval) | ||
2756 | goto error_exit; | ||
2757 | |||
2758 | rval = qla8044_wr_reg_indirect(vha, | ||
2759 | dma_base_addr + ISP8044_PEX_DMA_CMD_ADDR_HIGH, 0); | ||
2760 | if (rval) | ||
2761 | goto error_exit; | ||
2762 | |||
2763 | rval = qla8044_wr_reg_indirect(vha, | ||
2764 | dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL, | ||
2765 | m_hdr->start_dma_cmd); | ||
2766 | if (rval) | ||
2767 | goto error_exit; | ||
2768 | |||
2769 | /* Wait for dma operation to complete. */ | ||
2770 | for (wait = 0; wait < ISP8044_PEX_DMA_MAX_WAIT; wait++) { | ||
2771 | rval = qla8044_rd_reg_indirect(vha, | ||
2772 | (dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL), | ||
2773 | &cmd_sts_and_cntrl); | ||
2774 | if (rval) | ||
2775 | goto error_exit; | ||
2776 | |||
2777 | if ((cmd_sts_and_cntrl & BIT_1) == 0) | ||
2778 | break; | ||
2779 | |||
2780 | udelay(10); | ||
2781 | } | ||
2782 | |||
2783 | /* Wait a max of 100 ms, otherwise fallback to rdmem entry read */ | ||
2784 | if (wait >= ISP8044_PEX_DMA_MAX_WAIT) { | ||
2785 | rval = QLA_FUNCTION_FAILED; | ||
2786 | goto error_exit; | ||
2787 | } | ||
2788 | |||
2789 | error_exit: | ||
2790 | return rval; | ||
2791 | } | ||
2792 | |||
2793 | static int | ||
2794 | qla8044_minidump_pex_dma_read(struct scsi_qla_host *vha, | ||
2795 | struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) | ||
2796 | { | ||
2797 | struct qla_hw_data *ha = vha->hw; | ||
2798 | int rval = QLA_SUCCESS; | ||
2799 | struct qla8044_minidump_entry_rdmem_pex_dma *m_hdr = NULL; | ||
2800 | uint32_t chunk_size, read_size; | ||
2801 | uint8_t *data_ptr = (uint8_t *)*d_ptr; | ||
2802 | void *rdmem_buffer = NULL; | ||
2803 | dma_addr_t rdmem_dma; | ||
2804 | struct qla8044_pex_dma_descriptor dma_desc; | ||
2805 | |||
2806 | rval = qla8044_check_dma_engine_state(vha); | ||
2807 | if (rval != QLA_SUCCESS) { | ||
2808 | ql_dbg(ql_dbg_p3p, vha, 0xb147, | ||
2809 | "DMA engine not available. Fallback to rdmem-read.\n"); | ||
2810 | return QLA_FUNCTION_FAILED; | ||
2811 | } | ||
2812 | |||
2813 | m_hdr = (void *)entry_hdr; | ||
2814 | |||
2815 | rdmem_buffer = dma_alloc_coherent(&ha->pdev->dev, | ||
2816 | ISP8044_PEX_DMA_READ_SIZE, &rdmem_dma, GFP_KERNEL); | ||
2817 | if (!rdmem_buffer) { | ||
2818 | ql_dbg(ql_dbg_p3p, vha, 0xb148, | ||
2819 | "Unable to allocate rdmem dma buffer\n"); | ||
2820 | return QLA_FUNCTION_FAILED; | ||
2821 | } | ||
2822 | |||
2823 | /* Prepare pex-dma descriptor to be written to MS memory. */ | ||
2824 | /* dma-desc-cmd layout: | ||
2825 | * 0-3: dma-desc-cmd 0-3 | ||
2826 | * 4-7: pcid function number | ||
2827 | * 8-15: dma-desc-cmd 8-15 | ||
2828 | * dma_bus_addr: dma buffer address | ||
2829 | * cmd.read_data_size: amount of data-chunk to be read. | ||
2830 | */ | ||
2831 | dma_desc.cmd.dma_desc_cmd = (m_hdr->dma_desc_cmd & 0xff0f); | ||
2832 | dma_desc.cmd.dma_desc_cmd |= | ||
2833 | ((PCI_FUNC(ha->pdev->devfn) & 0xf) << 0x4); | ||
2834 | |||
2835 | dma_desc.dma_bus_addr = rdmem_dma; | ||
2836 | dma_desc.cmd.read_data_size = chunk_size = ISP8044_PEX_DMA_READ_SIZE; | ||
2837 | read_size = 0; | ||
2838 | |||
2839 | /* | ||
2840 | * Perform rdmem operation using pex-dma. | ||
2841 | * Prepare dma in chunks of ISP8044_PEX_DMA_READ_SIZE. | ||
2842 | */ | ||
2843 | while (read_size < m_hdr->read_data_size) { | ||
2844 | if (m_hdr->read_data_size - read_size < | ||
2845 | ISP8044_PEX_DMA_READ_SIZE) { | ||
2846 | chunk_size = (m_hdr->read_data_size - read_size); | ||
2847 | dma_desc.cmd.read_data_size = chunk_size; | ||
2848 | } | ||
2849 | |||
2850 | dma_desc.src_addr = m_hdr->read_addr + read_size; | ||
2851 | |||
2852 | /* Prepare: Write pex-dma descriptor to MS memory. */ | ||
2853 | rval = qla8044_ms_mem_write_128b(vha, | ||
2854 | m_hdr->desc_card_addr, (void *)&dma_desc, | ||
2855 | (sizeof(struct qla8044_pex_dma_descriptor)/16)); | ||
2856 | if (rval) { | ||
2857 | ql_log(ql_log_warn, vha, 0xb14a, | ||
2858 | "%s: Error writing rdmem-dma-init to MS !!!\n", | ||
2859 | __func__); | ||
2860 | goto error_exit; | ||
2861 | } | ||
2862 | ql_dbg(ql_dbg_p3p, vha, 0xb14b, | ||
2863 | "%s: Dma-descriptor: Instruct for rdmem dma " | ||
2864 | "(chunk_size 0x%x).\n", __func__, chunk_size); | ||
2865 | |||
2866 | /* Execute: Start pex-dma operation. */ | ||
2867 | rval = qla8044_start_pex_dma(vha, m_hdr); | ||
2868 | if (rval) | ||
2869 | goto error_exit; | ||
2870 | |||
2871 | memcpy(data_ptr, rdmem_buffer, chunk_size); | ||
2872 | data_ptr += chunk_size; | ||
2873 | read_size += chunk_size; | ||
2874 | } | ||
2875 | |||
2876 | *d_ptr = (void *)data_ptr; | ||
2877 | |||
2878 | error_exit: | ||
2879 | if (rdmem_buffer) | ||
2880 | dma_free_coherent(&ha->pdev->dev, ISP8044_PEX_DMA_READ_SIZE, | ||
2881 | rdmem_buffer, rdmem_dma); | ||
2882 | |||
2883 | return rval; | ||
2884 | } | ||
2885 | |||
2886 | /* | ||
2887 | * | ||
2888 | * qla8044_collect_md_data - Retrieve firmware minidump data. | ||
2889 | * @ha: pointer to adapter structure | ||
2890 | **/ | ||
2891 | int | ||
2892 | qla8044_collect_md_data(struct scsi_qla_host *vha) | ||
2893 | { | ||
2894 | int num_entry_hdr = 0; | ||
2895 | struct qla8044_minidump_entry_hdr *entry_hdr; | ||
2896 | struct qla8044_minidump_template_hdr *tmplt_hdr; | ||
2897 | uint32_t *data_ptr; | ||
2898 | uint32_t data_collected = 0, f_capture_mask; | ||
2899 | int i, rval = QLA_FUNCTION_FAILED; | ||
2900 | uint64_t now; | ||
2901 | uint32_t timestamp, idc_control; | ||
2902 | struct qla_hw_data *ha = vha->hw; | ||
2903 | |||
2904 | if (!ha->md_dump) { | ||
2905 | ql_log(ql_log_info, vha, 0xb101, | ||
2906 | "%s(%ld) No buffer to dump\n", | ||
2907 | __func__, vha->host_no); | ||
2908 | return rval; | ||
2909 | } | ||
2910 | |||
2911 | if (ha->fw_dumped) { | ||
2912 | ql_log(ql_log_warn, vha, 0xb10d, | ||
2913 | "Firmware has been previously dumped (%p) " | ||
2914 | "-- ignoring request.\n", ha->fw_dump); | ||
2915 | goto md_failed; | ||
2916 | } | ||
2917 | |||
2918 | ha->fw_dumped = 0; | ||
2919 | |||
2920 | if (!ha->md_tmplt_hdr || !ha->md_dump) { | ||
2921 | ql_log(ql_log_warn, vha, 0xb10e, | ||
2922 | "Memory not allocated for minidump capture\n"); | ||
2923 | goto md_failed; | ||
2924 | } | ||
2925 | |||
2926 | qla8044_idc_lock(ha); | ||
2927 | idc_control = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); | ||
2928 | if (idc_control & GRACEFUL_RESET_BIT1) { | ||
2929 | ql_log(ql_log_warn, vha, 0xb112, | ||
2930 | "Forced reset from application, " | ||
2931 | "ignore minidump capture\n"); | ||
2932 | qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, | ||
2933 | (idc_control & ~GRACEFUL_RESET_BIT1)); | ||
2934 | qla8044_idc_unlock(ha); | ||
2935 | |||
2936 | goto md_failed; | ||
2937 | } | ||
2938 | qla8044_idc_unlock(ha); | ||
2939 | |||
2940 | if (qla82xx_validate_template_chksum(vha)) { | ||
2941 | ql_log(ql_log_info, vha, 0xb109, | ||
2942 | "Template checksum validation error\n"); | ||
2943 | goto md_failed; | ||
2944 | } | ||
2945 | |||
2946 | tmplt_hdr = (struct qla8044_minidump_template_hdr *) | ||
2947 | ha->md_tmplt_hdr; | ||
2948 | data_ptr = (uint32_t *)((uint8_t *)ha->md_dump); | ||
2949 | num_entry_hdr = tmplt_hdr->num_of_entries; | ||
2950 | |||
2951 | ql_dbg(ql_dbg_p3p, vha, 0xb11a, | ||
2952 | "Capture Mask obtained: 0x%x\n", tmplt_hdr->capture_debug_level); | ||
2953 | |||
2954 | f_capture_mask = tmplt_hdr->capture_debug_level & 0xFF; | ||
2955 | |||
2956 | /* Validate whether required debug level is set */ | ||
2957 | if ((f_capture_mask & 0x3) != 0x3) { | ||
2958 | ql_log(ql_log_warn, vha, 0xb10f, | ||
2959 | "Minimum required capture mask[0x%x] level not set\n", | ||
2960 | f_capture_mask); | ||
2961 | |||
2962 | } | ||
2963 | tmplt_hdr->driver_capture_mask = ql2xmdcapmask; | ||
2964 | ql_log(ql_log_info, vha, 0xb102, | ||
2965 | "[%s]: starting data ptr: %p\n", | ||
2966 | __func__, data_ptr); | ||
2967 | ql_log(ql_log_info, vha, 0xb10b, | ||
2968 | "[%s]: no of entry headers in Template: 0x%x\n", | ||
2969 | __func__, num_entry_hdr); | ||
2970 | ql_log(ql_log_info, vha, 0xb10c, | ||
2971 | "[%s]: Total_data_size 0x%x, %d obtained\n", | ||
2972 | __func__, ha->md_dump_size, ha->md_dump_size); | ||
2973 | |||
2974 | /* Update current timestamp before taking dump */ | ||
2975 | now = get_jiffies_64(); | ||
2976 | timestamp = (u32)(jiffies_to_msecs(now) / 1000); | ||
2977 | tmplt_hdr->driver_timestamp = timestamp; | ||
2978 | |||
2979 | entry_hdr = (struct qla8044_minidump_entry_hdr *) | ||
2980 | (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset); | ||
2981 | tmplt_hdr->saved_state_array[QLA8044_SS_OCM_WNDREG_INDEX] = | ||
2982 | tmplt_hdr->ocm_window_reg[ha->portnum]; | ||
2983 | |||
2984 | /* Walk through the entry headers - validate/perform required action */ | ||
2985 | for (i = 0; i < num_entry_hdr; i++) { | ||
2986 | if (data_collected > ha->md_dump_size) { | ||
2987 | ql_log(ql_log_info, vha, 0xb103, | ||
2988 | "Data collected: [0x%x], " | ||
2989 | "Total Dump size: [0x%x]\n", | ||
2990 | data_collected, ha->md_dump_size); | ||
2991 | return rval; | ||
2992 | } | ||
2993 | |||
2994 | if (!(entry_hdr->d_ctrl.entry_capture_mask & | ||
2995 | ql2xmdcapmask)) { | ||
2996 | entry_hdr->d_ctrl.driver_flags |= | ||
2997 | QLA82XX_DBG_SKIPPED_FLAG; | ||
2998 | goto skip_nxt_entry; | ||
2999 | } | ||
3000 | |||
3001 | ql_dbg(ql_dbg_p3p, vha, 0xb104, | ||
3002 | "Data collected: [0x%x], Dump size left:[0x%x]\n", | ||
3003 | data_collected, | ||
3004 | (ha->md_dump_size - data_collected)); | ||
3005 | |||
3006 | /* Decode the entry type and take required action to capture | ||
3007 | * debug data | ||
3008 | */ | ||
3009 | switch (entry_hdr->entry_type) { | ||
3010 | case QLA82XX_RDEND: | ||
3011 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3012 | break; | ||
3013 | case QLA82XX_CNTRL: | ||
3014 | rval = qla8044_minidump_process_control(vha, | ||
3015 | entry_hdr); | ||
3016 | if (rval != QLA_SUCCESS) { | ||
3017 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3018 | goto md_failed; | ||
3019 | } | ||
3020 | break; | ||
3021 | case QLA82XX_RDCRB: | ||
3022 | qla8044_minidump_process_rdcrb(vha, | ||
3023 | entry_hdr, &data_ptr); | ||
3024 | break; | ||
3025 | case QLA82XX_RDMEM: | ||
3026 | rval = qla8044_minidump_pex_dma_read(vha, | ||
3027 | entry_hdr, &data_ptr); | ||
3028 | if (rval != QLA_SUCCESS) { | ||
3029 | rval = qla8044_minidump_process_rdmem(vha, | ||
3030 | entry_hdr, &data_ptr); | ||
3031 | if (rval != QLA_SUCCESS) { | ||
3032 | qla8044_mark_entry_skipped(vha, | ||
3033 | entry_hdr, i); | ||
3034 | goto md_failed; | ||
3035 | } | ||
3036 | } | ||
3037 | break; | ||
3038 | case QLA82XX_BOARD: | ||
3039 | case QLA82XX_RDROM: | ||
3040 | rval = qla8044_minidump_process_rdrom(vha, | ||
3041 | entry_hdr, &data_ptr); | ||
3042 | if (rval != QLA_SUCCESS) { | ||
3043 | qla8044_mark_entry_skipped(vha, | ||
3044 | entry_hdr, i); | ||
3045 | } | ||
3046 | break; | ||
3047 | case QLA82XX_L2DTG: | ||
3048 | case QLA82XX_L2ITG: | ||
3049 | case QLA82XX_L2DAT: | ||
3050 | case QLA82XX_L2INS: | ||
3051 | rval = qla8044_minidump_process_l2tag(vha, | ||
3052 | entry_hdr, &data_ptr); | ||
3053 | if (rval != QLA_SUCCESS) { | ||
3054 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3055 | goto md_failed; | ||
3056 | } | ||
3057 | break; | ||
3058 | case QLA8044_L1DTG: | ||
3059 | case QLA8044_L1ITG: | ||
3060 | case QLA82XX_L1DAT: | ||
3061 | case QLA82XX_L1INS: | ||
3062 | qla8044_minidump_process_l1cache(vha, | ||
3063 | entry_hdr, &data_ptr); | ||
3064 | break; | ||
3065 | case QLA82XX_RDOCM: | ||
3066 | qla8044_minidump_process_rdocm(vha, | ||
3067 | entry_hdr, &data_ptr); | ||
3068 | break; | ||
3069 | case QLA82XX_RDMUX: | ||
3070 | qla8044_minidump_process_rdmux(vha, | ||
3071 | entry_hdr, &data_ptr); | ||
3072 | break; | ||
3073 | case QLA82XX_QUEUE: | ||
3074 | qla8044_minidump_process_queue(vha, | ||
3075 | entry_hdr, &data_ptr); | ||
3076 | break; | ||
3077 | case QLA8044_POLLRD: | ||
3078 | rval = qla8044_minidump_process_pollrd(vha, | ||
3079 | entry_hdr, &data_ptr); | ||
3080 | if (rval != QLA_SUCCESS) | ||
3081 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3082 | break; | ||
3083 | case QLA8044_RDMUX2: | ||
3084 | qla8044_minidump_process_rdmux2(vha, | ||
3085 | entry_hdr, &data_ptr); | ||
3086 | break; | ||
3087 | case QLA8044_POLLRDMWR: | ||
3088 | rval = qla8044_minidump_process_pollrdmwr(vha, | ||
3089 | entry_hdr, &data_ptr); | ||
3090 | if (rval != QLA_SUCCESS) | ||
3091 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3092 | break; | ||
3093 | case QLA82XX_RDNOP: | ||
3094 | default: | ||
3095 | qla8044_mark_entry_skipped(vha, entry_hdr, i); | ||
3096 | break; | ||
3097 | } | ||
3098 | |||
3099 | data_collected = (uint8_t *)data_ptr - | ||
3100 | (uint8_t *)((uint8_t *)ha->md_dump); | ||
3101 | skip_nxt_entry: | ||
3102 | /* | ||
3103 | * next entry in the template | ||
3104 | */ | ||
3105 | entry_hdr = (struct qla8044_minidump_entry_hdr *) | ||
3106 | (((uint8_t *)entry_hdr) + entry_hdr->entry_size); | ||
3107 | } | ||
3108 | |||
3109 | if (data_collected != ha->md_dump_size) { | ||
3110 | ql_log(ql_log_info, vha, 0xb105, | ||
3111 | "Dump data mismatch: Data collected: " | ||
3112 | "[0x%x], total_data_size:[0x%x]\n", | ||
3113 | data_collected, ha->md_dump_size); | ||
3114 | goto md_failed; | ||
3115 | } | ||
3116 | |||
3117 | ql_log(ql_log_info, vha, 0xb110, | ||
3118 | "Firmware dump saved to temp buffer (%ld/%p %ld/%p).\n", | ||
3119 | vha->host_no, ha->md_tmplt_hdr, vha->host_no, ha->md_dump); | ||
3120 | ha->fw_dumped = 1; | ||
3121 | qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); | ||
3122 | |||
3123 | |||
3124 | ql_log(ql_log_info, vha, 0xb106, | ||
3125 | "Leaving fn: %s Last entry: 0x%x\n", | ||
3126 | __func__, i); | ||
3127 | md_failed: | ||
3128 | return rval; | ||
3129 | } | ||
3130 | |||
3131 | void | ||
3132 | qla8044_get_minidump(struct scsi_qla_host *vha) | ||
3133 | { | ||
3134 | struct qla_hw_data *ha = vha->hw; | ||
3135 | |||
3136 | if (!qla8044_collect_md_data(vha)) { | ||
3137 | ha->fw_dumped = 1; | ||
3138 | } else { | ||
3139 | ql_log(ql_log_fatal, vha, 0xb0db, | ||
3140 | "%s: Unable to collect minidump\n", | ||
3141 | __func__); | ||
3142 | } | ||
3143 | } | ||
3144 | |||
3145 | static int | ||
3146 | qla8044_poll_flash_status_reg(struct scsi_qla_host *vha) | ||
3147 | { | ||
3148 | uint32_t flash_status; | ||
3149 | int retries = QLA8044_FLASH_READ_RETRY_COUNT; | ||
3150 | int ret_val = QLA_SUCCESS; | ||
3151 | |||
3152 | while (retries--) { | ||
3153 | ret_val = qla8044_rd_reg_indirect(vha, QLA8044_FLASH_STATUS, | ||
3154 | &flash_status); | ||
3155 | if (ret_val) { | ||
3156 | ql_log(ql_log_warn, vha, 0xb13c, | ||
3157 | "%s: Failed to read FLASH_STATUS reg.\n", | ||
3158 | __func__); | ||
3159 | break; | ||
3160 | } | ||
3161 | if ((flash_status & QLA8044_FLASH_STATUS_READY) == | ||
3162 | QLA8044_FLASH_STATUS_READY) | ||
3163 | break; | ||
3164 | msleep(QLA8044_FLASH_STATUS_REG_POLL_DELAY); | ||
3165 | } | ||
3166 | |||
3167 | if (!retries) | ||
3168 | ret_val = QLA_FUNCTION_FAILED; | ||
3169 | |||
3170 | return ret_val; | ||
3171 | } | ||
3172 | |||
3173 | static int | ||
3174 | qla8044_write_flash_status_reg(struct scsi_qla_host *vha, | ||
3175 | uint32_t data) | ||
3176 | { | ||
3177 | int ret_val = QLA_SUCCESS; | ||
3178 | uint32_t cmd; | ||
3179 | |||
3180 | cmd = vha->hw->fdt_wrt_sts_reg_cmd; | ||
3181 | |||
3182 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3183 | QLA8044_FLASH_STATUS_WRITE_DEF_SIG | cmd); | ||
3184 | if (ret_val) { | ||
3185 | ql_log(ql_log_warn, vha, 0xb125, | ||
3186 | "%s: Failed to write to FLASH_ADDR.\n", __func__); | ||
3187 | goto exit_func; | ||
3188 | } | ||
3189 | |||
3190 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, data); | ||
3191 | if (ret_val) { | ||
3192 | ql_log(ql_log_warn, vha, 0xb126, | ||
3193 | "%s: Failed to write to FLASH_WRDATA.\n", __func__); | ||
3194 | goto exit_func; | ||
3195 | } | ||
3196 | |||
3197 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, | ||
3198 | QLA8044_FLASH_SECOND_ERASE_MS_VAL); | ||
3199 | if (ret_val) { | ||
3200 | ql_log(ql_log_warn, vha, 0xb127, | ||
3201 | "%s: Failed to write to FLASH_CONTROL.\n", __func__); | ||
3202 | goto exit_func; | ||
3203 | } | ||
3204 | |||
3205 | ret_val = qla8044_poll_flash_status_reg(vha); | ||
3206 | if (ret_val) | ||
3207 | ql_log(ql_log_warn, vha, 0xb128, | ||
3208 | "%s: Error polling flash status reg.\n", __func__); | ||
3209 | |||
3210 | exit_func: | ||
3211 | return ret_val; | ||
3212 | } | ||
3213 | |||
3214 | /* | ||
3215 | * This function assumes that the flash lock is held. | ||
3216 | */ | ||
3217 | static int | ||
3218 | qla8044_unprotect_flash(scsi_qla_host_t *vha) | ||
3219 | { | ||
3220 | int ret_val; | ||
3221 | struct qla_hw_data *ha = vha->hw; | ||
3222 | |||
3223 | ret_val = qla8044_write_flash_status_reg(vha, ha->fdt_wrt_enable); | ||
3224 | if (ret_val) | ||
3225 | ql_log(ql_log_warn, vha, 0xb139, | ||
3226 | "%s: Write flash status failed.\n", __func__); | ||
3227 | |||
3228 | return ret_val; | ||
3229 | } | ||
3230 | |||
3231 | /* | ||
3232 | * This function assumes that the flash lock is held. | ||
3233 | */ | ||
3234 | static int | ||
3235 | qla8044_protect_flash(scsi_qla_host_t *vha) | ||
3236 | { | ||
3237 | int ret_val; | ||
3238 | struct qla_hw_data *ha = vha->hw; | ||
3239 | |||
3240 | ret_val = qla8044_write_flash_status_reg(vha, ha->fdt_wrt_disable); | ||
3241 | if (ret_val) | ||
3242 | ql_log(ql_log_warn, vha, 0xb13b, | ||
3243 | "%s: Write flash status failed.\n", __func__); | ||
3244 | |||
3245 | return ret_val; | ||
3246 | } | ||
3247 | |||
3248 | |||
3249 | static int | ||
3250 | qla8044_erase_flash_sector(struct scsi_qla_host *vha, | ||
3251 | uint32_t sector_start_addr) | ||
3252 | { | ||
3253 | uint32_t reversed_addr; | ||
3254 | int ret_val = QLA_SUCCESS; | ||
3255 | |||
3256 | ret_val = qla8044_poll_flash_status_reg(vha); | ||
3257 | if (ret_val) { | ||
3258 | ql_log(ql_log_warn, vha, 0xb12e, | ||
3259 | "%s: Poll flash status after erase failed..\n", __func__); | ||
3260 | } | ||
3261 | |||
3262 | reversed_addr = (((sector_start_addr & 0xFF) << 16) | | ||
3263 | (sector_start_addr & 0xFF00) | | ||
3264 | ((sector_start_addr & 0xFF0000) >> 16)); | ||
3265 | |||
3266 | ret_val = qla8044_wr_reg_indirect(vha, | ||
3267 | QLA8044_FLASH_WRDATA, reversed_addr); | ||
3268 | if (ret_val) { | ||
3269 | ql_log(ql_log_warn, vha, 0xb12f, | ||
3270 | "%s: Failed to write to FLASH_WRDATA.\n", __func__); | ||
3271 | } | ||
3272 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3273 | QLA8044_FLASH_ERASE_SIG | vha->hw->fdt_erase_cmd); | ||
3274 | if (ret_val) { | ||
3275 | ql_log(ql_log_warn, vha, 0xb130, | ||
3276 | "%s: Failed to write to FLASH_ADDR.\n", __func__); | ||
3277 | } | ||
3278 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, | ||
3279 | QLA8044_FLASH_LAST_ERASE_MS_VAL); | ||
3280 | if (ret_val) { | ||
3281 | ql_log(ql_log_warn, vha, 0xb131, | ||
3282 | "%s: Failed write to FLASH_CONTROL.\n", __func__); | ||
3283 | } | ||
3284 | ret_val = qla8044_poll_flash_status_reg(vha); | ||
3285 | if (ret_val) { | ||
3286 | ql_log(ql_log_warn, vha, 0xb132, | ||
3287 | "%s: Poll flash status failed.\n", __func__); | ||
3288 | } | ||
3289 | |||
3290 | |||
3291 | return ret_val; | ||
3292 | } | ||
3293 | |||
3294 | /* | ||
3295 | * qla8044_flash_write_u32 - Write data to flash | ||
3296 | * | ||
3297 | * @ha : Pointer to adapter structure | ||
3298 | * addr : Flash address to write to | ||
3299 | * p_data : Data to be written | ||
3300 | * | ||
3301 | * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED | ||
3302 | * | ||
3303 | * NOTE: Lock should be held on entry | ||
3304 | */ | ||
3305 | static int | ||
3306 | qla8044_flash_write_u32(struct scsi_qla_host *vha, uint32_t addr, | ||
3307 | uint32_t *p_data) | ||
3308 | { | ||
3309 | int ret_val = QLA_SUCCESS; | ||
3310 | |||
3311 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3312 | 0x00800000 | (addr >> 2)); | ||
3313 | if (ret_val) { | ||
3314 | ql_log(ql_log_warn, vha, 0xb134, | ||
3315 | "%s: Failed write to FLASH_ADDR.\n", __func__); | ||
3316 | goto exit_func; | ||
3317 | } | ||
3318 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *p_data); | ||
3319 | if (ret_val) { | ||
3320 | ql_log(ql_log_warn, vha, 0xb135, | ||
3321 | "%s: Failed write to FLASH_WRDATA.\n", __func__); | ||
3322 | goto exit_func; | ||
3323 | } | ||
3324 | ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, 0x3D); | ||
3325 | if (ret_val) { | ||
3326 | ql_log(ql_log_warn, vha, 0xb136, | ||
3327 | "%s: Failed write to FLASH_CONTROL.\n", __func__); | ||
3328 | goto exit_func; | ||
3329 | } | ||
3330 | ret_val = qla8044_poll_flash_status_reg(vha); | ||
3331 | if (ret_val) { | ||
3332 | ql_log(ql_log_warn, vha, 0xb137, | ||
3333 | "%s: Poll flash status failed.\n", __func__); | ||
3334 | } | ||
3335 | |||
3336 | exit_func: | ||
3337 | return ret_val; | ||
3338 | } | ||
3339 | |||
3340 | static int | ||
3341 | qla8044_write_flash_buffer_mode(scsi_qla_host_t *vha, uint32_t *dwptr, | ||
3342 | uint32_t faddr, uint32_t dwords) | ||
3343 | { | ||
3344 | int ret = QLA_FUNCTION_FAILED; | ||
3345 | uint32_t spi_val; | ||
3346 | |||
3347 | if (dwords < QLA8044_MIN_OPTROM_BURST_DWORDS || | ||
3348 | dwords > QLA8044_MAX_OPTROM_BURST_DWORDS) { | ||
3349 | ql_dbg(ql_dbg_user, vha, 0xb123, | ||
3350 | "Got unsupported dwords = 0x%x.\n", | ||
3351 | dwords); | ||
3352 | return QLA_FUNCTION_FAILED; | ||
3353 | } | ||
3354 | |||
3355 | qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, &spi_val); | ||
3356 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, | ||
3357 | spi_val | QLA8044_FLASH_SPI_CTL); | ||
3358 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3359 | QLA8044_FLASH_FIRST_TEMP_VAL); | ||
3360 | |||
3361 | /* First DWORD write to FLASH_WRDATA */ | ||
3362 | ret = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, | ||
3363 | *dwptr++); | ||
3364 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, | ||
3365 | QLA8044_FLASH_FIRST_MS_PATTERN); | ||
3366 | |||
3367 | ret = qla8044_poll_flash_status_reg(vha); | ||
3368 | if (ret) { | ||
3369 | ql_log(ql_log_warn, vha, 0xb124, | ||
3370 | "%s: Failed.\n", __func__); | ||
3371 | goto exit_func; | ||
3372 | } | ||
3373 | |||
3374 | dwords--; | ||
3375 | |||
3376 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3377 | QLA8044_FLASH_SECOND_TEMP_VAL); | ||
3378 | |||
3379 | |||
3380 | /* Second to N-1 DWORDS writes */ | ||
3381 | while (dwords != 1) { | ||
3382 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *dwptr++); | ||
3383 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, | ||
3384 | QLA8044_FLASH_SECOND_MS_PATTERN); | ||
3385 | ret = qla8044_poll_flash_status_reg(vha); | ||
3386 | if (ret) { | ||
3387 | ql_log(ql_log_warn, vha, 0xb129, | ||
3388 | "%s: Failed.\n", __func__); | ||
3389 | goto exit_func; | ||
3390 | } | ||
3391 | dwords--; | ||
3392 | } | ||
3393 | |||
3394 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, | ||
3395 | QLA8044_FLASH_FIRST_TEMP_VAL | (faddr >> 2)); | ||
3396 | |||
3397 | /* Last DWORD write */ | ||
3398 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *dwptr++); | ||
3399 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, | ||
3400 | QLA8044_FLASH_LAST_MS_PATTERN); | ||
3401 | ret = qla8044_poll_flash_status_reg(vha); | ||
3402 | if (ret) { | ||
3403 | ql_log(ql_log_warn, vha, 0xb12a, | ||
3404 | "%s: Failed.\n", __func__); | ||
3405 | goto exit_func; | ||
3406 | } | ||
3407 | qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_STATUS, &spi_val); | ||
3408 | |||
3409 | if ((spi_val & QLA8044_FLASH_SPI_CTL) == QLA8044_FLASH_SPI_CTL) { | ||
3410 | ql_log(ql_log_warn, vha, 0xb12b, | ||
3411 | "%s: Failed.\n", __func__); | ||
3412 | spi_val = 0; | ||
3413 | /* Operation failed, clear error bit. */ | ||
3414 | qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, | ||
3415 | &spi_val); | ||
3416 | qla8044_wr_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, | ||
3417 | spi_val | QLA8044_FLASH_SPI_CTL); | ||
3418 | } | ||
3419 | exit_func: | ||
3420 | return ret; | ||
3421 | } | ||
3422 | |||
3423 | static int | ||
3424 | qla8044_write_flash_dword_mode(scsi_qla_host_t *vha, uint32_t *dwptr, | ||
3425 | uint32_t faddr, uint32_t dwords) | ||
3426 | { | ||
3427 | int ret = QLA_FUNCTION_FAILED; | ||
3428 | uint32_t liter; | ||
3429 | |||
3430 | for (liter = 0; liter < dwords; liter++, faddr += 4, dwptr++) { | ||
3431 | ret = qla8044_flash_write_u32(vha, faddr, dwptr); | ||
3432 | if (ret) { | ||
3433 | ql_dbg(ql_dbg_p3p, vha, 0xb141, | ||
3434 | "%s: flash address=%x data=%x.\n", __func__, | ||
3435 | faddr, *dwptr); | ||
3436 | break; | ||
3437 | } | ||
3438 | } | ||
3439 | |||
3440 | return ret; | ||
3441 | } | ||
3442 | |||
3443 | int | ||
3444 | qla8044_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, | ||
3445 | uint32_t offset, uint32_t length) | ||
3446 | { | ||
3447 | int rval = QLA_FUNCTION_FAILED, i, burst_iter_count; | ||
3448 | int dword_count, erase_sec_count; | ||
3449 | uint32_t erase_offset; | ||
3450 | uint8_t *p_cache, *p_src; | ||
3451 | |||
3452 | erase_offset = offset; | ||
3453 | |||
3454 | p_cache = kcalloc(length, sizeof(uint8_t), GFP_KERNEL); | ||
3455 | if (!p_cache) | ||
3456 | return QLA_FUNCTION_FAILED; | ||
3457 | |||
3458 | memcpy(p_cache, buf, length); | ||
3459 | p_src = p_cache; | ||
3460 | dword_count = length / sizeof(uint32_t); | ||
3461 | /* Since the offset and legth are sector aligned, it will be always | ||
3462 | * multiple of burst_iter_count (64) | ||
3463 | */ | ||
3464 | burst_iter_count = dword_count / QLA8044_MAX_OPTROM_BURST_DWORDS; | ||
3465 | erase_sec_count = length / QLA8044_SECTOR_SIZE; | ||
3466 | |||
3467 | /* Suspend HBA. */ | ||
3468 | scsi_block_requests(vha->host); | ||
3469 | /* Lock and enable write for whole operation. */ | ||
3470 | qla8044_flash_lock(vha); | ||
3471 | qla8044_unprotect_flash(vha); | ||
3472 | |||
3473 | /* Erasing the sectors */ | ||
3474 | for (i = 0; i < erase_sec_count; i++) { | ||
3475 | rval = qla8044_erase_flash_sector(vha, erase_offset); | ||
3476 | ql_dbg(ql_dbg_user, vha, 0xb138, | ||
3477 | "Done erase of sector=0x%x.\n", | ||
3478 | erase_offset); | ||
3479 | if (rval) { | ||
3480 | ql_log(ql_log_warn, vha, 0xb121, | ||
3481 | "Failed to erase the sector having address: " | ||
3482 | "0x%x.\n", erase_offset); | ||
3483 | goto out; | ||
3484 | } | ||
3485 | erase_offset += QLA8044_SECTOR_SIZE; | ||
3486 | } | ||
3487 | ql_dbg(ql_dbg_user, vha, 0xb13f, | ||
3488 | "Got write for addr = 0x%x length=0x%x.\n", | ||
3489 | offset, length); | ||
3490 | |||
3491 | for (i = 0; i < burst_iter_count; i++) { | ||
3492 | |||
3493 | /* Go with write. */ | ||
3494 | rval = qla8044_write_flash_buffer_mode(vha, (uint32_t *)p_src, | ||
3495 | offset, QLA8044_MAX_OPTROM_BURST_DWORDS); | ||
3496 | if (rval) { | ||
3497 | /* Buffer Mode failed skip to dword mode */ | ||
3498 | ql_log(ql_log_warn, vha, 0xb122, | ||
3499 | "Failed to write flash in buffer mode, " | ||
3500 | "Reverting to slow-write.\n"); | ||
3501 | rval = qla8044_write_flash_dword_mode(vha, | ||
3502 | (uint32_t *)p_src, offset, | ||
3503 | QLA8044_MAX_OPTROM_BURST_DWORDS); | ||
3504 | } | ||
3505 | p_src += sizeof(uint32_t) * QLA8044_MAX_OPTROM_BURST_DWORDS; | ||
3506 | offset += sizeof(uint32_t) * QLA8044_MAX_OPTROM_BURST_DWORDS; | ||
3507 | } | ||
3508 | ql_dbg(ql_dbg_user, vha, 0xb133, | ||
3509 | "Done writing.\n"); | ||
3510 | |||
3511 | out: | ||
3512 | qla8044_protect_flash(vha); | ||
3513 | qla8044_flash_unlock(vha); | ||
3514 | scsi_unblock_requests(vha->host); | ||
3515 | kfree(p_cache); | ||
3516 | |||
3517 | return rval; | ||
3518 | } | ||
3519 | |||
3520 | #define LEG_INT_PTR_B31 (1 << 31) | ||
3521 | #define LEG_INT_PTR_B30 (1 << 30) | ||
3522 | #define PF_BITS_MASK (0xF << 16) | ||
3523 | /** | ||
3524 | * qla8044_intr_handler() - Process interrupts for the ISP8044 | ||
3525 | * @irq: | ||
3526 | * @dev_id: SCSI driver HA context | ||
3527 | * | ||
3528 | * Called by system whenever the host adapter generates an interrupt. | ||
3529 | * | ||
3530 | * Returns handled flag. | ||
3531 | */ | ||
3532 | irqreturn_t | ||
3533 | qla8044_intr_handler(int irq, void *dev_id) | ||
3534 | { | ||
3535 | scsi_qla_host_t *vha; | ||
3536 | struct qla_hw_data *ha; | ||
3537 | struct rsp_que *rsp; | ||
3538 | struct device_reg_82xx __iomem *reg; | ||
3539 | int status = 0; | ||
3540 | unsigned long flags; | ||
3541 | unsigned long iter; | ||
3542 | uint32_t stat; | ||
3543 | uint16_t mb[4]; | ||
3544 | uint32_t leg_int_ptr = 0, pf_bit; | ||
3545 | |||
3546 | rsp = (struct rsp_que *) dev_id; | ||
3547 | if (!rsp) { | ||
3548 | ql_log(ql_log_info, NULL, 0xb143, | ||
3549 | "%s(): NULL response queue pointer\n", __func__); | ||
3550 | return IRQ_NONE; | ||
3551 | } | ||
3552 | ha = rsp->hw; | ||
3553 | vha = pci_get_drvdata(ha->pdev); | ||
3554 | |||
3555 | if (unlikely(pci_channel_offline(ha->pdev))) | ||
3556 | return IRQ_HANDLED; | ||
3557 | |||
3558 | leg_int_ptr = qla8044_rd_reg(ha, LEG_INTR_PTR_OFFSET); | ||
3559 | |||
3560 | /* Legacy interrupt is valid if bit31 of leg_int_ptr is set */ | ||
3561 | if (!(leg_int_ptr & (LEG_INT_PTR_B31))) { | ||
3562 | ql_dbg(ql_dbg_p3p, vha, 0xb144, | ||
3563 | "%s: Legacy Interrupt Bit 31 not set, " | ||
3564 | "spurious interrupt!\n", __func__); | ||
3565 | return IRQ_NONE; | ||
3566 | } | ||
3567 | |||
3568 | pf_bit = ha->portnum << 16; | ||
3569 | /* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */ | ||
3570 | if ((leg_int_ptr & (PF_BITS_MASK)) != pf_bit) { | ||
3571 | ql_dbg(ql_dbg_p3p, vha, 0xb145, | ||
3572 | "%s: Incorrect function ID 0x%x in " | ||
3573 | "legacy interrupt register, " | ||
3574 | "ha->pf_bit = 0x%x\n", __func__, | ||
3575 | (leg_int_ptr & (PF_BITS_MASK)), pf_bit); | ||
3576 | return IRQ_NONE; | ||
3577 | } | ||
3578 | |||
3579 | /* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger | ||
3580 | * Control register and poll till Legacy Interrupt Pointer register | ||
3581 | * bit32 is 0. | ||
3582 | */ | ||
3583 | qla8044_wr_reg(ha, LEG_INTR_TRIG_OFFSET, 0); | ||
3584 | do { | ||
3585 | leg_int_ptr = qla8044_rd_reg(ha, LEG_INTR_PTR_OFFSET); | ||
3586 | if ((leg_int_ptr & (PF_BITS_MASK)) != pf_bit) | ||
3587 | break; | ||
3588 | } while (leg_int_ptr & (LEG_INT_PTR_B30)); | ||
3589 | |||
3590 | reg = &ha->iobase->isp82; | ||
3591 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
3592 | for (iter = 1; iter--; ) { | ||
3593 | |||
3594 | if (RD_REG_DWORD(®->host_int)) { | ||
3595 | stat = RD_REG_DWORD(®->host_status); | ||
3596 | if ((stat & HSRX_RISC_INT) == 0) | ||
3597 | break; | ||
3598 | |||
3599 | switch (stat & 0xff) { | ||
3600 | case 0x1: | ||
3601 | case 0x2: | ||
3602 | case 0x10: | ||
3603 | case 0x11: | ||
3604 | qla82xx_mbx_completion(vha, MSW(stat)); | ||
3605 | status |= MBX_INTERRUPT; | ||
3606 | break; | ||
3607 | case 0x12: | ||
3608 | mb[0] = MSW(stat); | ||
3609 | mb[1] = RD_REG_WORD(®->mailbox_out[1]); | ||
3610 | mb[2] = RD_REG_WORD(®->mailbox_out[2]); | ||
3611 | mb[3] = RD_REG_WORD(®->mailbox_out[3]); | ||
3612 | qla2x00_async_event(vha, rsp, mb); | ||
3613 | break; | ||
3614 | case 0x13: | ||
3615 | qla24xx_process_response_queue(vha, rsp); | ||
3616 | break; | ||
3617 | default: | ||
3618 | ql_dbg(ql_dbg_p3p, vha, 0xb146, | ||
3619 | "Unrecognized interrupt type " | ||
3620 | "(%d).\n", stat & 0xff); | ||
3621 | break; | ||
3622 | } | ||
3623 | } | ||
3624 | WRT_REG_DWORD(®->host_int, 0); | ||
3625 | } | ||
3626 | |||
3627 | qla2x00_handle_mbx_completion(ha, status); | ||
3628 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
3629 | |||
3630 | return IRQ_HANDLED; | ||
3631 | } | ||
3632 | |||
3633 | static int | ||
3634 | qla8044_idc_dontreset(struct qla_hw_data *ha) | ||
3635 | { | ||
3636 | uint32_t idc_ctrl; | ||
3637 | |||
3638 | idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); | ||
3639 | return idc_ctrl & DONTRESET_BIT0; | ||
3640 | } | ||
3641 | |||
3642 | static void | ||
3643 | qla8044_clear_rst_ready(scsi_qla_host_t *vha) | ||
3644 | { | ||
3645 | uint32_t drv_state; | ||
3646 | |||
3647 | drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); | ||
3648 | |||
3649 | /* | ||
3650 | * For ISP8044, drv_active register has 1 bit per function, | ||
3651 | * shift 1 by func_num to set a bit for the function. | ||
3652 | * For ISP82xx, drv_active has 4 bits per function | ||
3653 | */ | ||
3654 | drv_state &= ~(1 << vha->hw->portnum); | ||
3655 | |||
3656 | ql_dbg(ql_dbg_p3p, vha, 0xb13d, | ||
3657 | "drv_state: 0x%08x\n", drv_state); | ||
3658 | qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, drv_state); | ||
3659 | } | ||
3660 | |||
3661 | int | ||
3662 | qla8044_abort_isp(scsi_qla_host_t *vha) | ||
3663 | { | ||
3664 | int rval; | ||
3665 | uint32_t dev_state; | ||
3666 | struct qla_hw_data *ha = vha->hw; | ||
3667 | |||
3668 | qla8044_idc_lock(ha); | ||
3669 | dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); | ||
3670 | |||
3671 | if (ql2xdontresethba) | ||
3672 | qla8044_set_idc_dontreset(vha); | ||
3673 | |||
3674 | /* If device_state is NEED_RESET, go ahead with | ||
3675 | * Reset,irrespective of ql2xdontresethba. This is to allow a | ||
3676 | * non-reset-owner to force a reset. Non-reset-owner sets | ||
3677 | * the IDC_CTRL BIT0 to prevent Reset-owner from doing a Reset | ||
3678 | * and then forces a Reset by setting device_state to | ||
3679 | * NEED_RESET. */ | ||
3680 | if (dev_state == QLA8XXX_DEV_READY) { | ||
3681 | /* If IDC_CTRL DONTRESETHBA_BIT0 is set don't do reset | ||
3682 | * recovery */ | ||
3683 | if (qla8044_idc_dontreset(ha) == DONTRESET_BIT0) { | ||
3684 | ql_dbg(ql_dbg_p3p, vha, 0xb13e, | ||
3685 | "Reset recovery disabled\n"); | ||
3686 | rval = QLA_FUNCTION_FAILED; | ||
3687 | goto exit_isp_reset; | ||
3688 | } | ||
3689 | |||
3690 | ql_dbg(ql_dbg_p3p, vha, 0xb140, | ||
3691 | "HW State: NEED RESET\n"); | ||
3692 | qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, | ||
3693 | QLA8XXX_DEV_NEED_RESET); | ||
3694 | } | ||
3695 | |||
3696 | /* For ISP8044, Reset owner is NIC, iSCSI or FCOE based on priority | ||
3697 | * and which drivers are present. Unlike ISP82XX, the function setting | ||
3698 | * NEED_RESET, may not be the Reset owner. */ | ||
3699 | qla83xx_reset_ownership(vha); | ||
3700 | |||
3701 | qla8044_idc_unlock(ha); | ||
3702 | rval = qla8044_device_state_handler(vha); | ||
3703 | qla8044_idc_lock(ha); | ||
3704 | qla8044_clear_rst_ready(vha); | ||
3705 | |||
3706 | exit_isp_reset: | ||
3707 | qla8044_idc_unlock(ha); | ||
3708 | if (rval == QLA_SUCCESS) { | ||
3709 | ha->flags.isp82xx_fw_hung = 0; | ||
3710 | ha->flags.nic_core_reset_hdlr_active = 0; | ||
3711 | rval = qla82xx_restart_isp(vha); | ||
3712 | } | ||
3713 | |||
3714 | return rval; | ||
3715 | } | ||
3716 | |||