162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QLogic iSCSI HBA Driver
462306a36Sopenharmony_ci * Copyright (c)   2003-2013 QLogic Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/ratelimit.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "ql4_def.h"
1062306a36Sopenharmony_ci#include "ql4_version.h"
1162306a36Sopenharmony_ci#include "ql4_glbl.h"
1262306a36Sopenharmony_ci#include "ql4_dbg.h"
1362306a36Sopenharmony_ci#include "ql4_inline.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciuint32_t qla4_83xx_rd_reg(struct scsi_qla_host *ha, ulong addr)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return readl((void __iomem *)(ha->nx_pcibase + addr));
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_civoid qla4_83xx_wr_reg(struct scsi_qla_host *ha, ulong addr, uint32_t val)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	writel(val, (void __iomem *)(ha->nx_pcibase + addr));
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int qla4_83xx_set_win_base(struct scsi_qla_host *ha, uint32_t addr)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	uint32_t val;
2862306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num), addr);
3162306a36Sopenharmony_ci	val = qla4_83xx_rd_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num));
3262306a36Sopenharmony_ci	if (val != addr) {
3362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Failed to set register window : addr written 0x%x, read 0x%x!\n",
3462306a36Sopenharmony_ci			   __func__, addr, val);
3562306a36Sopenharmony_ci		ret_val = QLA_ERROR;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return ret_val;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciint qla4_83xx_rd_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
4262306a36Sopenharmony_ci			      uint32_t *data)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	int ret_val;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	ret_val = qla4_83xx_set_win_base(ha, addr);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (ret_val == QLA_SUCCESS) {
4962306a36Sopenharmony_ci		*data = qla4_83xx_rd_reg(ha, QLA83XX_WILDCARD);
5062306a36Sopenharmony_ci	} else {
5162306a36Sopenharmony_ci		*data = 0xffffffff;
5262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: failed read of addr 0x%x!\n",
5362306a36Sopenharmony_ci			   __func__, addr);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return ret_val;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint qla4_83xx_wr_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
6062306a36Sopenharmony_ci			      uint32_t data)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	int ret_val;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret_val = qla4_83xx_set_win_base(ha, addr);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (ret_val == QLA_SUCCESS)
6762306a36Sopenharmony_ci		qla4_83xx_wr_reg(ha, QLA83XX_WILDCARD, data);
6862306a36Sopenharmony_ci	else
6962306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: failed wrt to addr 0x%x, data 0x%x\n",
7062306a36Sopenharmony_ci			   __func__, addr, data);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return ret_val;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int qla4_83xx_flash_lock(struct scsi_qla_host *ha)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	int lock_owner;
7862306a36Sopenharmony_ci	int timeout = 0;
7962306a36Sopenharmony_ci	uint32_t lock_status = 0;
8062306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	while (lock_status == 0) {
8362306a36Sopenharmony_ci		lock_status = qla4_83xx_rd_reg(ha, QLA83XX_FLASH_LOCK);
8462306a36Sopenharmony_ci		if (lock_status)
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		if (++timeout >= QLA83XX_FLASH_LOCK_TIMEOUT / 20) {
8862306a36Sopenharmony_ci			lock_owner = qla4_83xx_rd_reg(ha,
8962306a36Sopenharmony_ci						      QLA83XX_FLASH_LOCK_ID);
9062306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: flash lock by func %d failed, held by func %d\n",
9162306a36Sopenharmony_ci				   __func__, ha->func_num, lock_owner);
9262306a36Sopenharmony_ci			ret_val = QLA_ERROR;
9362306a36Sopenharmony_ci			break;
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci		msleep(20);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, ha->func_num);
9962306a36Sopenharmony_ci	return ret_val;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void qla4_83xx_flash_unlock(struct scsi_qla_host *ha)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	/* Reading FLASH_UNLOCK register unlocks the Flash */
10562306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, 0xFF);
10662306a36Sopenharmony_ci	qla4_83xx_rd_reg(ha, QLA83XX_FLASH_UNLOCK);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ciint qla4_83xx_flash_read_u32(struct scsi_qla_host *ha, uint32_t flash_addr,
11062306a36Sopenharmony_ci			     uint8_t *p_data, int u32_word_count)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int i;
11362306a36Sopenharmony_ci	uint32_t u32_word;
11462306a36Sopenharmony_ci	uint32_t addr = flash_addr;
11562306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	ret_val = qla4_83xx_flash_lock(ha);
11862306a36Sopenharmony_ci	if (ret_val == QLA_ERROR)
11962306a36Sopenharmony_ci		goto exit_lock_error;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (addr & 0x03) {
12262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
12362306a36Sopenharmony_ci			   __func__, addr);
12462306a36Sopenharmony_ci		ret_val = QLA_ERROR;
12562306a36Sopenharmony_ci		goto exit_flash_read;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	for (i = 0; i < u32_word_count; i++) {
12962306a36Sopenharmony_ci		ret_val = qla4_83xx_wr_reg_indirect(ha,
13062306a36Sopenharmony_ci						    QLA83XX_FLASH_DIRECT_WINDOW,
13162306a36Sopenharmony_ci						    (addr & 0xFFFF0000));
13262306a36Sopenharmony_ci		if (ret_val == QLA_ERROR) {
13362306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW\n!",
13462306a36Sopenharmony_ci				   __func__, addr);
13562306a36Sopenharmony_ci			goto exit_flash_read;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		ret_val = qla4_83xx_rd_reg_indirect(ha,
13962306a36Sopenharmony_ci						QLA83XX_FLASH_DIRECT_DATA(addr),
14062306a36Sopenharmony_ci						&u32_word);
14162306a36Sopenharmony_ci		if (ret_val == QLA_ERROR) {
14262306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
14362306a36Sopenharmony_ci				   __func__, addr);
14462306a36Sopenharmony_ci			goto exit_flash_read;
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		*(__le32 *)p_data = le32_to_cpu(u32_word);
14862306a36Sopenharmony_ci		p_data = p_data + 4;
14962306a36Sopenharmony_ci		addr = addr + 4;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciexit_flash_read:
15362306a36Sopenharmony_ci	qla4_83xx_flash_unlock(ha);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciexit_lock_error:
15662306a36Sopenharmony_ci	return ret_val;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciint qla4_83xx_lockless_flash_read_u32(struct scsi_qla_host *ha,
16062306a36Sopenharmony_ci				      uint32_t flash_addr, uint8_t *p_data,
16162306a36Sopenharmony_ci				      int u32_word_count)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	uint32_t i;
16462306a36Sopenharmony_ci	uint32_t u32_word;
16562306a36Sopenharmony_ci	uint32_t flash_offset;
16662306a36Sopenharmony_ci	uint32_t addr = flash_addr;
16762306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	flash_offset = addr & (QLA83XX_FLASH_SECTOR_SIZE - 1);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (addr & 0x3) {
17262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
17362306a36Sopenharmony_ci			   __func__, addr);
17462306a36Sopenharmony_ci		ret_val = QLA_ERROR;
17562306a36Sopenharmony_ci		goto exit_lockless_read;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret_val = qla4_83xx_wr_reg_indirect(ha, QLA83XX_FLASH_DIRECT_WINDOW,
17962306a36Sopenharmony_ci					    addr);
18062306a36Sopenharmony_ci	if (ret_val == QLA_ERROR) {
18162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
18262306a36Sopenharmony_ci			   __func__, addr);
18362306a36Sopenharmony_ci		goto exit_lockless_read;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* Check if data is spread across multiple sectors  */
18762306a36Sopenharmony_ci	if ((flash_offset + (u32_word_count * sizeof(uint32_t))) >
18862306a36Sopenharmony_ci	    (QLA83XX_FLASH_SECTOR_SIZE - 1)) {
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		/* Multi sector read */
19162306a36Sopenharmony_ci		for (i = 0; i < u32_word_count; i++) {
19262306a36Sopenharmony_ci			ret_val = qla4_83xx_rd_reg_indirect(ha,
19362306a36Sopenharmony_ci						QLA83XX_FLASH_DIRECT_DATA(addr),
19462306a36Sopenharmony_ci						&u32_word);
19562306a36Sopenharmony_ci			if (ret_val == QLA_ERROR) {
19662306a36Sopenharmony_ci				ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
19762306a36Sopenharmony_ci					   __func__, addr);
19862306a36Sopenharmony_ci				goto exit_lockless_read;
19962306a36Sopenharmony_ci			}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci			*(__le32 *)p_data  = le32_to_cpu(u32_word);
20262306a36Sopenharmony_ci			p_data = p_data + 4;
20362306a36Sopenharmony_ci			addr = addr + 4;
20462306a36Sopenharmony_ci			flash_offset = flash_offset + 4;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			if (flash_offset > (QLA83XX_FLASH_SECTOR_SIZE - 1)) {
20762306a36Sopenharmony_ci				/* This write is needed once for each sector */
20862306a36Sopenharmony_ci				ret_val = qla4_83xx_wr_reg_indirect(ha,
20962306a36Sopenharmony_ci						   QLA83XX_FLASH_DIRECT_WINDOW,
21062306a36Sopenharmony_ci						   addr);
21162306a36Sopenharmony_ci				if (ret_val == QLA_ERROR) {
21262306a36Sopenharmony_ci					ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
21362306a36Sopenharmony_ci						   __func__, addr);
21462306a36Sopenharmony_ci					goto exit_lockless_read;
21562306a36Sopenharmony_ci				}
21662306a36Sopenharmony_ci				flash_offset = 0;
21762306a36Sopenharmony_ci			}
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	} else {
22062306a36Sopenharmony_ci		/* Single sector read */
22162306a36Sopenharmony_ci		for (i = 0; i < u32_word_count; i++) {
22262306a36Sopenharmony_ci			ret_val = qla4_83xx_rd_reg_indirect(ha,
22362306a36Sopenharmony_ci						QLA83XX_FLASH_DIRECT_DATA(addr),
22462306a36Sopenharmony_ci						&u32_word);
22562306a36Sopenharmony_ci			if (ret_val == QLA_ERROR) {
22662306a36Sopenharmony_ci				ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
22762306a36Sopenharmony_ci					   __func__, addr);
22862306a36Sopenharmony_ci				goto exit_lockless_read;
22962306a36Sopenharmony_ci			}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci			*(__le32 *)p_data = le32_to_cpu(u32_word);
23262306a36Sopenharmony_ci			p_data = p_data + 4;
23362306a36Sopenharmony_ci			addr = addr + 4;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ciexit_lockless_read:
23862306a36Sopenharmony_ci	return ret_val;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid qla4_83xx_rom_lock_recovery(struct scsi_qla_host *ha)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	if (qla4_83xx_flash_lock(ha))
24462306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Resetting rom lock\n", __func__);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/*
24762306a36Sopenharmony_ci	 * We got the lock, or someone else is holding the lock
24862306a36Sopenharmony_ci	 * since we are restting, forcefully unlock
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	qla4_83xx_flash_unlock(ha);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci#define INTENT_TO_RECOVER	0x01
25462306a36Sopenharmony_ci#define PROCEED_TO_RECOVER	0x02
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int qla4_83xx_lock_recovery(struct scsi_qla_host *ha)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	uint32_t lock = 0, lockid;
26062306a36Sopenharmony_ci	int ret_val = QLA_ERROR;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Check for other Recovery in progress, go wait */
26562306a36Sopenharmony_ci	if ((lockid & 0x3) != 0)
26662306a36Sopenharmony_ci		goto exit_lock_recovery;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Intent to Recover */
26962306a36Sopenharmony_ci	ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY,
27062306a36Sopenharmony_ci				   (ha->func_num << 2) | INTENT_TO_RECOVER);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	msleep(200);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Check Intent to Recover is advertised */
27562306a36Sopenharmony_ci	lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY);
27662306a36Sopenharmony_ci	if ((lockid & 0x3C) != (ha->func_num << 2))
27762306a36Sopenharmony_ci		goto exit_lock_recovery;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ql4_printk(KERN_INFO, ha, "%s: IDC Lock recovery initiated for func %d\n",
28062306a36Sopenharmony_ci		   __func__, ha->func_num);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Proceed to Recover */
28362306a36Sopenharmony_ci	ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY,
28462306a36Sopenharmony_ci				   (ha->func_num << 2) | PROCEED_TO_RECOVER);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Force Unlock */
28762306a36Sopenharmony_ci	ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCK_ID, 0xFF);
28862306a36Sopenharmony_ci	ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_UNLOCK);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Clear bits 0-5 in IDC_RECOVERY register*/
29162306a36Sopenharmony_ci	ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY, 0);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Get lock */
29462306a36Sopenharmony_ci	lock = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCK);
29562306a36Sopenharmony_ci	if (lock) {
29662306a36Sopenharmony_ci		lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCK_ID);
29762306a36Sopenharmony_ci		lockid = ((lockid + (1 << 8)) & ~0xFF) | ha->func_num;
29862306a36Sopenharmony_ci		ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCK_ID, lockid);
29962306a36Sopenharmony_ci		ret_val = QLA_SUCCESS;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ciexit_lock_recovery:
30362306a36Sopenharmony_ci	return ret_val;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#define	QLA83XX_DRV_LOCK_MSLEEP		200
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciint qla4_83xx_drv_lock(struct scsi_qla_host *ha)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int timeout = 0;
31162306a36Sopenharmony_ci	uint32_t status = 0;
31262306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
31362306a36Sopenharmony_ci	uint32_t first_owner = 0;
31462306a36Sopenharmony_ci	uint32_t tmo_owner = 0;
31562306a36Sopenharmony_ci	uint32_t lock_id;
31662306a36Sopenharmony_ci	uint32_t func_num;
31762306a36Sopenharmony_ci	uint32_t lock_cnt;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	while (status == 0) {
32062306a36Sopenharmony_ci		status = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK);
32162306a36Sopenharmony_ci		if (status) {
32262306a36Sopenharmony_ci			/* Increment Counter (8-31) and update func_num (0-7) on
32362306a36Sopenharmony_ci			 * getting a successful lock  */
32462306a36Sopenharmony_ci			lock_id = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
32562306a36Sopenharmony_ci			lock_id = ((lock_id + (1 << 8)) & ~0xFF) | ha->func_num;
32662306a36Sopenharmony_ci			qla4_83xx_wr_reg(ha, QLA83XX_DRV_LOCK_ID, lock_id);
32762306a36Sopenharmony_ci			break;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		if (timeout == 0)
33162306a36Sopenharmony_ci			/* Save counter + ID of function holding the lock for
33262306a36Sopenharmony_ci			 * first failure */
33362306a36Sopenharmony_ci			first_owner = ha->isp_ops->rd_reg_direct(ha,
33462306a36Sopenharmony_ci							  QLA83XX_DRV_LOCK_ID);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (++timeout >=
33762306a36Sopenharmony_ci		    (QLA83XX_DRV_LOCK_TIMEOUT / QLA83XX_DRV_LOCK_MSLEEP)) {
33862306a36Sopenharmony_ci			tmo_owner = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
33962306a36Sopenharmony_ci			func_num = tmo_owner & 0xFF;
34062306a36Sopenharmony_ci			lock_cnt = tmo_owner >> 8;
34162306a36Sopenharmony_ci			ql4_printk(KERN_INFO, ha, "%s: Lock by func %d failed after 2s, lock held by func %d, lock count %d, first_owner %d\n",
34262306a36Sopenharmony_ci				   __func__, ha->func_num, func_num, lock_cnt,
34362306a36Sopenharmony_ci				   (first_owner & 0xFF));
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			if (first_owner != tmo_owner) {
34662306a36Sopenharmony_ci				/* Some other driver got lock, OR same driver
34762306a36Sopenharmony_ci				 * got lock again (counter value changed), when
34862306a36Sopenharmony_ci				 * we were waiting for lock.
34962306a36Sopenharmony_ci				 * Retry for another 2 sec */
35062306a36Sopenharmony_ci				ql4_printk(KERN_INFO, ha, "%s: IDC lock failed for func %d\n",
35162306a36Sopenharmony_ci					   __func__, ha->func_num);
35262306a36Sopenharmony_ci				timeout = 0;
35362306a36Sopenharmony_ci			} else {
35462306a36Sopenharmony_ci				/* Same driver holding lock > 2sec.
35562306a36Sopenharmony_ci				 * Force Recovery */
35662306a36Sopenharmony_ci				ret_val = qla4_83xx_lock_recovery(ha);
35762306a36Sopenharmony_ci				if (ret_val == QLA_SUCCESS) {
35862306a36Sopenharmony_ci					/* Recovered and got lock */
35962306a36Sopenharmony_ci					ql4_printk(KERN_INFO, ha, "%s: IDC lock Recovery by %d successful\n",
36062306a36Sopenharmony_ci						   __func__, ha->func_num);
36162306a36Sopenharmony_ci					break;
36262306a36Sopenharmony_ci				}
36362306a36Sopenharmony_ci				/* Recovery Failed, some other function
36462306a36Sopenharmony_ci				 * has the lock, wait for 2secs and retry */
36562306a36Sopenharmony_ci				ql4_printk(KERN_INFO, ha, "%s: IDC lock Recovery by %d failed, Retrying timeout\n",
36662306a36Sopenharmony_ci					   __func__, ha->func_num);
36762306a36Sopenharmony_ci				timeout = 0;
36862306a36Sopenharmony_ci			}
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci		msleep(QLA83XX_DRV_LOCK_MSLEEP);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return ret_val;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_civoid qla4_83xx_drv_unlock(struct scsi_qla_host *ha)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	int id;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	id = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if ((id & 0xFF) != ha->func_num) {
38362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: IDC Unlock by %d failed, lock owner is %d\n",
38462306a36Sopenharmony_ci			   __func__, ha->func_num, (id & 0xFF));
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Keep lock counter value, update the ha->func_num to 0xFF */
38962306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_DRV_LOCK_ID, (id | 0xFF));
39062306a36Sopenharmony_ci	qla4_83xx_rd_reg(ha, QLA83XX_DRV_UNLOCK);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_civoid qla4_83xx_set_idc_dontreset(struct scsi_qla_host *ha)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	uint32_t idc_ctrl;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
39862306a36Sopenharmony_ci	idc_ctrl |= DONTRESET_BIT0;
39962306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL, idc_ctrl);
40062306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: idc_ctrl = %d\n", __func__,
40162306a36Sopenharmony_ci			  idc_ctrl));
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_civoid qla4_83xx_clear_idc_dontreset(struct scsi_qla_host *ha)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	uint32_t idc_ctrl;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
40962306a36Sopenharmony_ci	idc_ctrl &= ~DONTRESET_BIT0;
41062306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL, idc_ctrl);
41162306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: idc_ctrl = %d\n", __func__,
41262306a36Sopenharmony_ci			  idc_ctrl));
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciint qla4_83xx_idc_dontreset(struct scsi_qla_host *ha)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	uint32_t idc_ctrl;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
42062306a36Sopenharmony_ci	return idc_ctrl & DONTRESET_BIT0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/*-------------------------IDC State Machine ---------------------*/
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cienum {
42662306a36Sopenharmony_ci	UNKNOWN_CLASS = 0,
42762306a36Sopenharmony_ci	NIC_CLASS,
42862306a36Sopenharmony_ci	FCOE_CLASS,
42962306a36Sopenharmony_ci	ISCSI_CLASS
43062306a36Sopenharmony_ci};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistruct device_info {
43362306a36Sopenharmony_ci	int func_num;
43462306a36Sopenharmony_ci	int device_type;
43562306a36Sopenharmony_ci	int port_num;
43662306a36Sopenharmony_ci};
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciint qla4_83xx_can_perform_reset(struct scsi_qla_host *ha)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	uint32_t drv_active;
44162306a36Sopenharmony_ci	uint32_t dev_part, dev_part1, dev_part2;
44262306a36Sopenharmony_ci	int i;
44362306a36Sopenharmony_ci	struct device_info device_map[16];
44462306a36Sopenharmony_ci	int func_nibble;
44562306a36Sopenharmony_ci	int nibble;
44662306a36Sopenharmony_ci	int nic_present = 0;
44762306a36Sopenharmony_ci	int iscsi_present = 0;
44862306a36Sopenharmony_ci	int iscsi_func_low = 0;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Use the dev_partition register to determine the PCI function number
45162306a36Sopenharmony_ci	 * and then check drv_active register to see which driver is loaded */
45262306a36Sopenharmony_ci	dev_part1 = qla4_83xx_rd_reg(ha,
45362306a36Sopenharmony_ci				     ha->reg_tbl[QLA8XXX_CRB_DEV_PART_INFO]);
45462306a36Sopenharmony_ci	dev_part2 = qla4_83xx_rd_reg(ha, QLA83XX_CRB_DEV_PART_INFO2);
45562306a36Sopenharmony_ci	drv_active = qla4_83xx_rd_reg(ha, ha->reg_tbl[QLA8XXX_CRB_DRV_ACTIVE]);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Each function has 4 bits in dev_partition Info register,
45862306a36Sopenharmony_ci	 * Lower 2 bits - device type, Upper 2 bits - physical port number */
45962306a36Sopenharmony_ci	dev_part = dev_part1;
46062306a36Sopenharmony_ci	for (i = nibble = 0; i <= 15; i++, nibble++) {
46162306a36Sopenharmony_ci		func_nibble = dev_part & (0xF << (nibble * 4));
46262306a36Sopenharmony_ci		func_nibble >>= (nibble * 4);
46362306a36Sopenharmony_ci		device_map[i].func_num = i;
46462306a36Sopenharmony_ci		device_map[i].device_type = func_nibble & 0x3;
46562306a36Sopenharmony_ci		device_map[i].port_num = func_nibble & 0xC;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (device_map[i].device_type == NIC_CLASS) {
46862306a36Sopenharmony_ci			if (drv_active & (1 << device_map[i].func_num)) {
46962306a36Sopenharmony_ci				nic_present++;
47062306a36Sopenharmony_ci				break;
47162306a36Sopenharmony_ci			}
47262306a36Sopenharmony_ci		} else if (device_map[i].device_type == ISCSI_CLASS) {
47362306a36Sopenharmony_ci			if (drv_active & (1 << device_map[i].func_num)) {
47462306a36Sopenharmony_ci				if (!iscsi_present ||
47562306a36Sopenharmony_ci				iscsi_func_low > device_map[i].func_num)
47662306a36Sopenharmony_ci					iscsi_func_low = device_map[i].func_num;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci				iscsi_present++;
47962306a36Sopenharmony_ci			}
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		/* For function_num[8..15] get info from dev_part2 register */
48362306a36Sopenharmony_ci		if (nibble == 7) {
48462306a36Sopenharmony_ci			nibble = 0;
48562306a36Sopenharmony_ci			dev_part = dev_part2;
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* NIC, iSCSI and FCOE are the Reset owners based on order, NIC gets
49062306a36Sopenharmony_ci	 * precedence over iSCSI and FCOE and iSCSI over FCOE, based on drivers
49162306a36Sopenharmony_ci	 * present. */
49262306a36Sopenharmony_ci	if (!nic_present && (ha->func_num == iscsi_func_low)) {
49362306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha,
49462306a36Sopenharmony_ci				  "%s: can reset - NIC not present and lower iSCSI function is %d\n",
49562306a36Sopenharmony_ci				  __func__, ha->func_num));
49662306a36Sopenharmony_ci		return 1;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/**
50362306a36Sopenharmony_ci * qla4_83xx_need_reset_handler - Code to start reset sequence
50462306a36Sopenharmony_ci * @ha: pointer to adapter structure
50562306a36Sopenharmony_ci *
50662306a36Sopenharmony_ci * Note: IDC lock must be held upon entry
50762306a36Sopenharmony_ci **/
50862306a36Sopenharmony_civoid qla4_83xx_need_reset_handler(struct scsi_qla_host *ha)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	uint32_t dev_state, drv_state, drv_active;
51162306a36Sopenharmony_ci	unsigned long reset_timeout, dev_init_timeout;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	ql4_printk(KERN_INFO, ha, "%s: Performing ISP error recovery\n",
51462306a36Sopenharmony_ci		   __func__);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!test_bit(AF_8XXX_RST_OWNER, &ha->flags)) {
51762306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha, "%s: reset acknowledged\n",
51862306a36Sopenharmony_ci				  __func__));
51962306a36Sopenharmony_ci		qla4_8xxx_set_rst_ready(ha);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		/* Non-reset owners ACK Reset and wait for device INIT state
52262306a36Sopenharmony_ci		 * as part of Reset Recovery by Reset Owner */
52362306a36Sopenharmony_ci		dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		do {
52662306a36Sopenharmony_ci			if (time_after_eq(jiffies, dev_init_timeout)) {
52762306a36Sopenharmony_ci				ql4_printk(KERN_INFO, ha, "%s: Non Reset owner dev init timeout\n",
52862306a36Sopenharmony_ci					   __func__);
52962306a36Sopenharmony_ci				break;
53062306a36Sopenharmony_ci			}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci			ha->isp_ops->idc_unlock(ha);
53362306a36Sopenharmony_ci			msleep(1000);
53462306a36Sopenharmony_ci			ha->isp_ops->idc_lock(ha);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci			dev_state = qla4_8xxx_rd_direct(ha,
53762306a36Sopenharmony_ci							QLA8XXX_CRB_DEV_STATE);
53862306a36Sopenharmony_ci		} while (dev_state == QLA8XXX_DEV_NEED_RESET);
53962306a36Sopenharmony_ci	} else {
54062306a36Sopenharmony_ci		qla4_8xxx_set_rst_ready(ha);
54162306a36Sopenharmony_ci		reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
54262306a36Sopenharmony_ci		drv_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
54362306a36Sopenharmony_ci		drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: drv_state = 0x%x, drv_active = 0x%x\n",
54662306a36Sopenharmony_ci			   __func__, drv_state, drv_active);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		while (drv_state != drv_active) {
54962306a36Sopenharmony_ci			if (time_after_eq(jiffies, reset_timeout)) {
55062306a36Sopenharmony_ci				ql4_printk(KERN_INFO, ha, "%s: %s: RESET TIMEOUT! drv_state: 0x%08x, drv_active: 0x%08x\n",
55162306a36Sopenharmony_ci					   __func__, DRIVER_NAME, drv_state,
55262306a36Sopenharmony_ci					   drv_active);
55362306a36Sopenharmony_ci				break;
55462306a36Sopenharmony_ci			}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			ha->isp_ops->idc_unlock(ha);
55762306a36Sopenharmony_ci			msleep(1000);
55862306a36Sopenharmony_ci			ha->isp_ops->idc_lock(ha);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci			drv_state = qla4_8xxx_rd_direct(ha,
56162306a36Sopenharmony_ci							QLA8XXX_CRB_DRV_STATE);
56262306a36Sopenharmony_ci			drv_active = qla4_8xxx_rd_direct(ha,
56362306a36Sopenharmony_ci							QLA8XXX_CRB_DRV_ACTIVE);
56462306a36Sopenharmony_ci		}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		if (drv_state != drv_active) {
56762306a36Sopenharmony_ci			ql4_printk(KERN_INFO, ha, "%s: Reset_owner turning off drv_active of non-acking function 0x%x\n",
56862306a36Sopenharmony_ci				   __func__, (drv_active ^ drv_state));
56962306a36Sopenharmony_ci			drv_active = drv_active & drv_state;
57062306a36Sopenharmony_ci			qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_ACTIVE,
57162306a36Sopenharmony_ci					    drv_active);
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		clear_bit(AF_8XXX_RST_OWNER, &ha->flags);
57562306a36Sopenharmony_ci		/* Start Reset Recovery */
57662306a36Sopenharmony_ci		qla4_8xxx_device_bootstrap(ha);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_civoid qla4_83xx_get_idc_param(struct scsi_qla_host *ha)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	uint32_t idc_params, ret_val;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	ret_val = qla4_83xx_flash_read_u32(ha, QLA83XX_IDC_PARAM_ADDR,
58562306a36Sopenharmony_ci					   (uint8_t *)&idc_params, 1);
58662306a36Sopenharmony_ci	if (ret_val == QLA_SUCCESS) {
58762306a36Sopenharmony_ci		ha->nx_dev_init_timeout = idc_params & 0xFFFF;
58862306a36Sopenharmony_ci		ha->nx_reset_timeout = (idc_params >> 16) & 0xFFFF;
58962306a36Sopenharmony_ci	} else {
59062306a36Sopenharmony_ci		ha->nx_dev_init_timeout = ROM_DEV_INIT_TIMEOUT;
59162306a36Sopenharmony_ci		ha->nx_reset_timeout = ROM_DRV_RESET_ACK_TIMEOUT;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_DEBUG, ha,
59562306a36Sopenharmony_ci			  "%s: ha->nx_dev_init_timeout = %d, ha->nx_reset_timeout = %d\n",
59662306a36Sopenharmony_ci			  __func__, ha->nx_dev_init_timeout,
59762306a36Sopenharmony_ci			  ha->nx_reset_timeout));
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*-------------------------Reset Sequence Functions-----------------------*/
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic void qla4_83xx_dump_reset_seq_hdr(struct scsi_qla_host *ha)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	uint8_t *phdr;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (!ha->reset_tmplt.buff) {
60762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Error: Invalid reset_seq_template\n",
60862306a36Sopenharmony_ci			   __func__);
60962306a36Sopenharmony_ci		return;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	phdr = ha->reset_tmplt.buff;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
61562306a36Sopenharmony_ci			  "Reset Template: 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
61662306a36Sopenharmony_ci			  *phdr, *(phdr+1), *(phdr+2), *(phdr+3), *(phdr+4),
61762306a36Sopenharmony_ci			  *(phdr+5), *(phdr+6), *(phdr+7), *(phdr + 8),
61862306a36Sopenharmony_ci			  *(phdr+9), *(phdr+10), *(phdr+11), *(phdr+12),
61962306a36Sopenharmony_ci			  *(phdr+13), *(phdr+14), *(phdr+15)));
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int qla4_83xx_copy_bootloader(struct scsi_qla_host *ha)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	uint8_t *p_cache;
62562306a36Sopenharmony_ci	uint32_t src, count, size;
62662306a36Sopenharmony_ci	uint64_t dest;
62762306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	src = QLA83XX_BOOTLOADER_FLASH_ADDR;
63062306a36Sopenharmony_ci	dest = qla4_83xx_rd_reg(ha, QLA83XX_BOOTLOADER_ADDR);
63162306a36Sopenharmony_ci	size = qla4_83xx_rd_reg(ha, QLA83XX_BOOTLOADER_SIZE);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* 128 bit alignment check */
63462306a36Sopenharmony_ci	if (size & 0xF)
63562306a36Sopenharmony_ci		size = (size + 16) & ~0xF;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* 16 byte count */
63862306a36Sopenharmony_ci	count = size/16;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	p_cache = vmalloc(size);
64162306a36Sopenharmony_ci	if (p_cache == NULL) {
64262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Failed to allocate memory for boot loader cache\n",
64362306a36Sopenharmony_ci			   __func__);
64462306a36Sopenharmony_ci		ret_val = QLA_ERROR;
64562306a36Sopenharmony_ci		goto exit_copy_bootloader;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ret_val = qla4_83xx_lockless_flash_read_u32(ha, src, p_cache,
64962306a36Sopenharmony_ci						    size / sizeof(uint32_t));
65062306a36Sopenharmony_ci	if (ret_val == QLA_ERROR) {
65162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Error reading firmware from flash\n",
65262306a36Sopenharmony_ci			   __func__);
65362306a36Sopenharmony_ci		goto exit_copy_error;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Read firmware from flash\n",
65662306a36Sopenharmony_ci			  __func__));
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* 128 bit/16 byte write to MS memory */
65962306a36Sopenharmony_ci	ret_val = qla4_8xxx_ms_mem_write_128b(ha, dest, (uint32_t *)p_cache,
66062306a36Sopenharmony_ci					      count);
66162306a36Sopenharmony_ci	if (ret_val == QLA_ERROR) {
66262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Error writing firmware to MS\n",
66362306a36Sopenharmony_ci			   __func__);
66462306a36Sopenharmony_ci		goto exit_copy_error;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Wrote firmware size %d to MS\n",
66862306a36Sopenharmony_ci			  __func__, size));
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ciexit_copy_error:
67162306a36Sopenharmony_ci	vfree(p_cache);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ciexit_copy_bootloader:
67462306a36Sopenharmony_ci	return ret_val;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic int qla4_83xx_check_cmd_peg_status(struct scsi_qla_host *ha)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	uint32_t val, ret_val = QLA_ERROR;
68062306a36Sopenharmony_ci	int retries = CRB_CMDPEG_CHECK_RETRY_COUNT;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	do {
68362306a36Sopenharmony_ci		val = qla4_83xx_rd_reg(ha, QLA83XX_CMDPEG_STATE);
68462306a36Sopenharmony_ci		if (val == PHAN_INITIALIZE_COMPLETE) {
68562306a36Sopenharmony_ci			DEBUG2(ql4_printk(KERN_INFO, ha,
68662306a36Sopenharmony_ci					  "%s: Command Peg initialization complete. State=0x%x\n",
68762306a36Sopenharmony_ci					  __func__, val));
68862306a36Sopenharmony_ci			ret_val = QLA_SUCCESS;
68962306a36Sopenharmony_ci			break;
69062306a36Sopenharmony_ci		}
69162306a36Sopenharmony_ci		msleep(CRB_CMDPEG_CHECK_DELAY);
69262306a36Sopenharmony_ci	} while (--retries);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return ret_val;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/**
69862306a36Sopenharmony_ci * qla4_83xx_poll_reg - Poll the given CRB addr for duration msecs till
69962306a36Sopenharmony_ci * value read ANDed with test_mask is equal to test_result.
70062306a36Sopenharmony_ci *
70162306a36Sopenharmony_ci * @ha : Pointer to adapter structure
70262306a36Sopenharmony_ci * @addr : CRB register address
70362306a36Sopenharmony_ci * @duration : Poll for total of "duration" msecs
70462306a36Sopenharmony_ci * @test_mask : Mask value read with "test_mask"
70562306a36Sopenharmony_ci * @test_result : Compare (value&test_mask) with test_result.
70662306a36Sopenharmony_ci **/
70762306a36Sopenharmony_cistatic int qla4_83xx_poll_reg(struct scsi_qla_host *ha, uint32_t addr,
70862306a36Sopenharmony_ci			      int duration, uint32_t test_mask,
70962306a36Sopenharmony_ci			      uint32_t test_result)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	uint32_t value;
71262306a36Sopenharmony_ci	uint8_t retries;
71362306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	ret_val = qla4_83xx_rd_reg_indirect(ha, addr, &value);
71662306a36Sopenharmony_ci	if (ret_val == QLA_ERROR)
71762306a36Sopenharmony_ci		goto exit_poll_reg;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	retries = duration / 10;
72062306a36Sopenharmony_ci	do {
72162306a36Sopenharmony_ci		if ((value & test_mask) != test_result) {
72262306a36Sopenharmony_ci			msleep(duration / 10);
72362306a36Sopenharmony_ci			ret_val = qla4_83xx_rd_reg_indirect(ha, addr, &value);
72462306a36Sopenharmony_ci			if (ret_val == QLA_ERROR)
72562306a36Sopenharmony_ci				goto exit_poll_reg;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci			ret_val = QLA_ERROR;
72862306a36Sopenharmony_ci		} else {
72962306a36Sopenharmony_ci			ret_val = QLA_SUCCESS;
73062306a36Sopenharmony_ci			break;
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci	} while (retries--);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ciexit_poll_reg:
73562306a36Sopenharmony_ci	if (ret_val == QLA_ERROR) {
73662306a36Sopenharmony_ci		ha->reset_tmplt.seq_error++;
73762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Poll Failed:  0x%08x 0x%08x 0x%08x\n",
73862306a36Sopenharmony_ci			   __func__, value, test_mask, test_result);
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	return ret_val;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int qla4_83xx_reset_seq_checksum_test(struct scsi_qla_host *ha)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	uint32_t sum =  0;
74762306a36Sopenharmony_ci	uint16_t *buff = (uint16_t *)ha->reset_tmplt.buff;
74862306a36Sopenharmony_ci	int u16_count =  ha->reset_tmplt.hdr->size / sizeof(uint16_t);
74962306a36Sopenharmony_ci	int ret_val;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	while (u16_count-- > 0)
75262306a36Sopenharmony_ci		sum += *buff++;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	while (sum >> 16)
75562306a36Sopenharmony_ci		sum = (sum & 0xFFFF) +  (sum >> 16);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* checksum of 0 indicates a valid template */
75862306a36Sopenharmony_ci	if (~sum) {
75962306a36Sopenharmony_ci		ret_val = QLA_SUCCESS;
76062306a36Sopenharmony_ci	} else {
76162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Reset seq checksum failed\n",
76262306a36Sopenharmony_ci			   __func__);
76362306a36Sopenharmony_ci		ret_val = QLA_ERROR;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return ret_val;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci/**
77062306a36Sopenharmony_ci * qla4_83xx_read_reset_template - Read Reset Template from Flash
77162306a36Sopenharmony_ci * @ha: Pointer to adapter structure
77262306a36Sopenharmony_ci **/
77362306a36Sopenharmony_civoid qla4_83xx_read_reset_template(struct scsi_qla_host *ha)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	uint8_t *p_buff;
77662306a36Sopenharmony_ci	uint32_t addr, tmplt_hdr_def_size, tmplt_hdr_size;
77762306a36Sopenharmony_ci	uint32_t ret_val;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ha->reset_tmplt.seq_error = 0;
78062306a36Sopenharmony_ci	ha->reset_tmplt.buff = vmalloc(QLA83XX_RESTART_TEMPLATE_SIZE);
78162306a36Sopenharmony_ci	if (ha->reset_tmplt.buff == NULL) {
78262306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Failed to allocate reset template resources\n",
78362306a36Sopenharmony_ci			   __func__);
78462306a36Sopenharmony_ci		goto exit_read_reset_template;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	p_buff = ha->reset_tmplt.buff;
78862306a36Sopenharmony_ci	addr = QLA83XX_RESET_TEMPLATE_ADDR;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	tmplt_hdr_def_size = sizeof(struct qla4_83xx_reset_template_hdr) /
79162306a36Sopenharmony_ci				    sizeof(uint32_t);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
79462306a36Sopenharmony_ci			  "%s: Read template hdr size %d from Flash\n",
79562306a36Sopenharmony_ci			  __func__, tmplt_hdr_def_size));
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* Copy template header from flash */
79862306a36Sopenharmony_ci	ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
79962306a36Sopenharmony_ci					   tmplt_hdr_def_size);
80062306a36Sopenharmony_ci	if (ret_val != QLA_SUCCESS) {
80162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n",
80262306a36Sopenharmony_ci			   __func__);
80362306a36Sopenharmony_ci		goto exit_read_template_error;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	ha->reset_tmplt.hdr =
80762306a36Sopenharmony_ci		(struct qla4_83xx_reset_template_hdr *)ha->reset_tmplt.buff;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Validate the template header size and signature */
81062306a36Sopenharmony_ci	tmplt_hdr_size = ha->reset_tmplt.hdr->hdr_size/sizeof(uint32_t);
81162306a36Sopenharmony_ci	if ((tmplt_hdr_size != tmplt_hdr_def_size) ||
81262306a36Sopenharmony_ci	    (ha->reset_tmplt.hdr->signature != RESET_TMPLT_HDR_SIGNATURE)) {
81362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Template Header size %d is invalid, tmplt_hdr_def_size %d\n",
81462306a36Sopenharmony_ci			   __func__, tmplt_hdr_size, tmplt_hdr_def_size);
81562306a36Sopenharmony_ci		goto exit_read_template_error;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	addr = QLA83XX_RESET_TEMPLATE_ADDR + ha->reset_tmplt.hdr->hdr_size;
81962306a36Sopenharmony_ci	p_buff = ha->reset_tmplt.buff + ha->reset_tmplt.hdr->hdr_size;
82062306a36Sopenharmony_ci	tmplt_hdr_def_size = (ha->reset_tmplt.hdr->size -
82162306a36Sopenharmony_ci			      ha->reset_tmplt.hdr->hdr_size) / sizeof(uint32_t);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
82462306a36Sopenharmony_ci			  "%s: Read rest of the template size %d\n",
82562306a36Sopenharmony_ci			  __func__, ha->reset_tmplt.hdr->size));
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* Copy rest of the template */
82862306a36Sopenharmony_ci	ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
82962306a36Sopenharmony_ci					   tmplt_hdr_def_size);
83062306a36Sopenharmony_ci	if (ret_val != QLA_SUCCESS) {
83162306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n",
83262306a36Sopenharmony_ci			   __func__);
83362306a36Sopenharmony_ci		goto exit_read_template_error;
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* Integrity check */
83762306a36Sopenharmony_ci	if (qla4_83xx_reset_seq_checksum_test(ha)) {
83862306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Reset Seq checksum failed!\n",
83962306a36Sopenharmony_ci			   __func__);
84062306a36Sopenharmony_ci		goto exit_read_template_error;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
84362306a36Sopenharmony_ci			  "%s: Reset Seq checksum passed, Get stop, start and init seq offsets\n",
84462306a36Sopenharmony_ci			  __func__));
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* Get STOP, START, INIT sequence offsets */
84762306a36Sopenharmony_ci	ha->reset_tmplt.init_offset = ha->reset_tmplt.buff +
84862306a36Sopenharmony_ci				      ha->reset_tmplt.hdr->init_seq_offset;
84962306a36Sopenharmony_ci	ha->reset_tmplt.start_offset = ha->reset_tmplt.buff +
85062306a36Sopenharmony_ci				       ha->reset_tmplt.hdr->start_seq_offset;
85162306a36Sopenharmony_ci	ha->reset_tmplt.stop_offset = ha->reset_tmplt.buff +
85262306a36Sopenharmony_ci				      ha->reset_tmplt.hdr->hdr_size;
85362306a36Sopenharmony_ci	qla4_83xx_dump_reset_seq_hdr(ha);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	goto exit_read_reset_template;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ciexit_read_template_error:
85862306a36Sopenharmony_ci	vfree(ha->reset_tmplt.buff);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciexit_read_reset_template:
86162306a36Sopenharmony_ci	return;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/**
86562306a36Sopenharmony_ci * qla4_83xx_read_write_crb_reg - Read from raddr and write value to waddr.
86662306a36Sopenharmony_ci *
86762306a36Sopenharmony_ci * @ha : Pointer to adapter structure
86862306a36Sopenharmony_ci * @raddr : CRB address to read from
86962306a36Sopenharmony_ci * @waddr : CRB address to write to
87062306a36Sopenharmony_ci **/
87162306a36Sopenharmony_cistatic void qla4_83xx_read_write_crb_reg(struct scsi_qla_host *ha,
87262306a36Sopenharmony_ci					 uint32_t raddr, uint32_t waddr)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	uint32_t value;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	qla4_83xx_rd_reg_indirect(ha, raddr, &value);
87762306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, waddr, value);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci/**
88162306a36Sopenharmony_ci * qla4_83xx_rmw_crb_reg - Read Modify Write crb register
88262306a36Sopenharmony_ci *
88362306a36Sopenharmony_ci * This function read value from raddr, AND with test_mask,
88462306a36Sopenharmony_ci * Shift Left,Right/OR/XOR with values RMW header and write value to waddr.
88562306a36Sopenharmony_ci *
88662306a36Sopenharmony_ci * @ha : Pointer to adapter structure
88762306a36Sopenharmony_ci * @raddr : CRB address to read from
88862306a36Sopenharmony_ci * @waddr : CRB address to write to
88962306a36Sopenharmony_ci * @p_rmw_hdr : header with shift/or/xor values.
89062306a36Sopenharmony_ci **/
89162306a36Sopenharmony_cistatic void qla4_83xx_rmw_crb_reg(struct scsi_qla_host *ha, uint32_t raddr,
89262306a36Sopenharmony_ci				  uint32_t waddr,
89362306a36Sopenharmony_ci				  struct qla4_83xx_rmw *p_rmw_hdr)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	uint32_t value;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (p_rmw_hdr->index_a)
89862306a36Sopenharmony_ci		value = ha->reset_tmplt.array[p_rmw_hdr->index_a];
89962306a36Sopenharmony_ci	else
90062306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha, raddr, &value);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	value &= p_rmw_hdr->test_mask;
90362306a36Sopenharmony_ci	value <<= p_rmw_hdr->shl;
90462306a36Sopenharmony_ci	value >>= p_rmw_hdr->shr;
90562306a36Sopenharmony_ci	value |= p_rmw_hdr->or_value;
90662306a36Sopenharmony_ci	value ^= p_rmw_hdr->xor_value;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, waddr, value);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic void qla4_83xx_write_list(struct scsi_qla_host *ha,
91462306a36Sopenharmony_ci				 struct qla4_83xx_reset_entry_hdr *p_hdr)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct qla4_83xx_entry *p_entry;
91762306a36Sopenharmony_ci	uint32_t i;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_entry *)
92062306a36Sopenharmony_ci		  ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	for (i = 0; i < p_hdr->count; i++, p_entry++) {
92362306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, p_entry->arg1, p_entry->arg2);
92462306a36Sopenharmony_ci		if (p_hdr->delay)
92562306a36Sopenharmony_ci			udelay((uint32_t)(p_hdr->delay));
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic void qla4_83xx_read_write_list(struct scsi_qla_host *ha,
93062306a36Sopenharmony_ci				      struct qla4_83xx_reset_entry_hdr *p_hdr)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct qla4_83xx_entry *p_entry;
93362306a36Sopenharmony_ci	uint32_t i;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_entry *)
93662306a36Sopenharmony_ci		  ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	for (i = 0; i < p_hdr->count; i++, p_entry++) {
93962306a36Sopenharmony_ci		qla4_83xx_read_write_crb_reg(ha, p_entry->arg1, p_entry->arg2);
94062306a36Sopenharmony_ci		if (p_hdr->delay)
94162306a36Sopenharmony_ci			udelay((uint32_t)(p_hdr->delay));
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic void qla4_83xx_poll_list(struct scsi_qla_host *ha,
94662306a36Sopenharmony_ci				struct qla4_83xx_reset_entry_hdr *p_hdr)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	long delay;
94962306a36Sopenharmony_ci	struct qla4_83xx_entry *p_entry;
95062306a36Sopenharmony_ci	struct qla4_83xx_poll *p_poll;
95162306a36Sopenharmony_ci	uint32_t i;
95262306a36Sopenharmony_ci	uint32_t value;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	p_poll = (struct qla4_83xx_poll *)
95562306a36Sopenharmony_ci		 ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* Entries start after 8 byte qla4_83xx_poll, poll header contains
95862306a36Sopenharmony_ci	 * the test_mask, test_value. */
95962306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_entry *)((char *)p_poll +
96062306a36Sopenharmony_ci					     sizeof(struct qla4_83xx_poll));
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	delay = (long)p_hdr->delay;
96362306a36Sopenharmony_ci	if (!delay) {
96462306a36Sopenharmony_ci		for (i = 0; i < p_hdr->count; i++, p_entry++) {
96562306a36Sopenharmony_ci			qla4_83xx_poll_reg(ha, p_entry->arg1, delay,
96662306a36Sopenharmony_ci					   p_poll->test_mask,
96762306a36Sopenharmony_ci					   p_poll->test_value);
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci	} else {
97062306a36Sopenharmony_ci		for (i = 0; i < p_hdr->count; i++, p_entry++) {
97162306a36Sopenharmony_ci			if (qla4_83xx_poll_reg(ha, p_entry->arg1, delay,
97262306a36Sopenharmony_ci					       p_poll->test_mask,
97362306a36Sopenharmony_ci					       p_poll->test_value)) {
97462306a36Sopenharmony_ci				qla4_83xx_rd_reg_indirect(ha, p_entry->arg1,
97562306a36Sopenharmony_ci							  &value);
97662306a36Sopenharmony_ci				qla4_83xx_rd_reg_indirect(ha, p_entry->arg2,
97762306a36Sopenharmony_ci							  &value);
97862306a36Sopenharmony_ci			}
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic void qla4_83xx_poll_write_list(struct scsi_qla_host *ha,
98462306a36Sopenharmony_ci				      struct qla4_83xx_reset_entry_hdr *p_hdr)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	long delay;
98762306a36Sopenharmony_ci	struct qla4_83xx_quad_entry *p_entry;
98862306a36Sopenharmony_ci	struct qla4_83xx_poll *p_poll;
98962306a36Sopenharmony_ci	uint32_t i;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	p_poll = (struct qla4_83xx_poll *)
99262306a36Sopenharmony_ci		 ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
99362306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_quad_entry *)
99462306a36Sopenharmony_ci		  ((char *)p_poll + sizeof(struct qla4_83xx_poll));
99562306a36Sopenharmony_ci	delay = (long)p_hdr->delay;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	for (i = 0; i < p_hdr->count; i++, p_entry++) {
99862306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, p_entry->dr_addr,
99962306a36Sopenharmony_ci					  p_entry->dr_value);
100062306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, p_entry->ar_addr,
100162306a36Sopenharmony_ci					  p_entry->ar_value);
100262306a36Sopenharmony_ci		if (delay) {
100362306a36Sopenharmony_ci			if (qla4_83xx_poll_reg(ha, p_entry->ar_addr, delay,
100462306a36Sopenharmony_ci					       p_poll->test_mask,
100562306a36Sopenharmony_ci					       p_poll->test_value)) {
100662306a36Sopenharmony_ci				DEBUG2(ql4_printk(KERN_INFO, ha,
100762306a36Sopenharmony_ci						  "%s: Timeout Error: poll list, item_num %d, entry_num %d\n",
100862306a36Sopenharmony_ci						  __func__, i,
100962306a36Sopenharmony_ci						  ha->reset_tmplt.seq_index));
101062306a36Sopenharmony_ci			}
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void qla4_83xx_read_modify_write(struct scsi_qla_host *ha,
101662306a36Sopenharmony_ci					struct qla4_83xx_reset_entry_hdr *p_hdr)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct qla4_83xx_entry *p_entry;
101962306a36Sopenharmony_ci	struct qla4_83xx_rmw *p_rmw_hdr;
102062306a36Sopenharmony_ci	uint32_t i;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	p_rmw_hdr = (struct qla4_83xx_rmw *)
102362306a36Sopenharmony_ci		    ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
102462306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_entry *)
102562306a36Sopenharmony_ci		  ((char *)p_rmw_hdr + sizeof(struct qla4_83xx_rmw));
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	for (i = 0; i < p_hdr->count; i++, p_entry++) {
102862306a36Sopenharmony_ci		qla4_83xx_rmw_crb_reg(ha, p_entry->arg1, p_entry->arg2,
102962306a36Sopenharmony_ci				      p_rmw_hdr);
103062306a36Sopenharmony_ci		if (p_hdr->delay)
103162306a36Sopenharmony_ci			udelay((uint32_t)(p_hdr->delay));
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic void qla4_83xx_pause(struct scsi_qla_host *ha,
103662306a36Sopenharmony_ci			    struct qla4_83xx_reset_entry_hdr *p_hdr)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	if (p_hdr->delay)
103962306a36Sopenharmony_ci		mdelay((uint32_t)((long)p_hdr->delay));
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void qla4_83xx_poll_read_list(struct scsi_qla_host *ha,
104362306a36Sopenharmony_ci				     struct qla4_83xx_reset_entry_hdr *p_hdr)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	long delay;
104662306a36Sopenharmony_ci	int index;
104762306a36Sopenharmony_ci	struct qla4_83xx_quad_entry *p_entry;
104862306a36Sopenharmony_ci	struct qla4_83xx_poll *p_poll;
104962306a36Sopenharmony_ci	uint32_t i;
105062306a36Sopenharmony_ci	uint32_t value;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	p_poll = (struct qla4_83xx_poll *)
105362306a36Sopenharmony_ci		 ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
105462306a36Sopenharmony_ci	p_entry = (struct qla4_83xx_quad_entry *)
105562306a36Sopenharmony_ci		  ((char *)p_poll + sizeof(struct qla4_83xx_poll));
105662306a36Sopenharmony_ci	delay = (long)p_hdr->delay;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	for (i = 0; i < p_hdr->count; i++, p_entry++) {
105962306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, p_entry->ar_addr,
106062306a36Sopenharmony_ci					  p_entry->ar_value);
106162306a36Sopenharmony_ci		if (delay) {
106262306a36Sopenharmony_ci			if (qla4_83xx_poll_reg(ha, p_entry->ar_addr, delay,
106362306a36Sopenharmony_ci					       p_poll->test_mask,
106462306a36Sopenharmony_ci					       p_poll->test_value)) {
106562306a36Sopenharmony_ci				DEBUG2(ql4_printk(KERN_INFO, ha,
106662306a36Sopenharmony_ci						  "%s: Timeout Error: poll list, Item_num %d, entry_num %d\n",
106762306a36Sopenharmony_ci						  __func__, i,
106862306a36Sopenharmony_ci						  ha->reset_tmplt.seq_index));
106962306a36Sopenharmony_ci			} else {
107062306a36Sopenharmony_ci				index = ha->reset_tmplt.array_index;
107162306a36Sopenharmony_ci				qla4_83xx_rd_reg_indirect(ha, p_entry->dr_addr,
107262306a36Sopenharmony_ci							  &value);
107362306a36Sopenharmony_ci				ha->reset_tmplt.array[index++] = value;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci				if (index == QLA83XX_MAX_RESET_SEQ_ENTRIES)
107662306a36Sopenharmony_ci					ha->reset_tmplt.array_index = 1;
107762306a36Sopenharmony_ci			}
107862306a36Sopenharmony_ci		}
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic void qla4_83xx_seq_end(struct scsi_qla_host *ha,
108362306a36Sopenharmony_ci			      struct qla4_83xx_reset_entry_hdr *p_hdr)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	ha->reset_tmplt.seq_end = 1;
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic void qla4_83xx_template_end(struct scsi_qla_host *ha,
108962306a36Sopenharmony_ci				   struct qla4_83xx_reset_entry_hdr *p_hdr)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	ha->reset_tmplt.template_end = 1;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	if (ha->reset_tmplt.seq_error == 0) {
109462306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha,
109562306a36Sopenharmony_ci				  "%s: Reset sequence completed SUCCESSFULLY.\n",
109662306a36Sopenharmony_ci				  __func__));
109762306a36Sopenharmony_ci	} else {
109862306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Reset sequence completed with some timeout errors.\n",
109962306a36Sopenharmony_ci			   __func__);
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci/**
110462306a36Sopenharmony_ci * qla4_83xx_process_reset_template - Process reset template.
110562306a36Sopenharmony_ci *
110662306a36Sopenharmony_ci * Process all entries in reset template till entry with SEQ_END opcode,
110762306a36Sopenharmony_ci * which indicates end of the reset template processing. Each entry has a
110862306a36Sopenharmony_ci * Reset Entry header, entry opcode/command, with size of the entry, number
110962306a36Sopenharmony_ci * of entries in sub-sequence and delay in microsecs or timeout in millisecs.
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * @ha : Pointer to adapter structure
111262306a36Sopenharmony_ci * @p_buff : Common reset entry header.
111362306a36Sopenharmony_ci **/
111462306a36Sopenharmony_cistatic void qla4_83xx_process_reset_template(struct scsi_qla_host *ha,
111562306a36Sopenharmony_ci					     char *p_buff)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	int index, entries;
111862306a36Sopenharmony_ci	struct qla4_83xx_reset_entry_hdr *p_hdr;
111962306a36Sopenharmony_ci	char *p_entry = p_buff;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	ha->reset_tmplt.seq_end = 0;
112262306a36Sopenharmony_ci	ha->reset_tmplt.template_end = 0;
112362306a36Sopenharmony_ci	entries = ha->reset_tmplt.hdr->entries;
112462306a36Sopenharmony_ci	index = ha->reset_tmplt.seq_index;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	for (; (!ha->reset_tmplt.seq_end) && (index  < entries); index++) {
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		p_hdr = (struct qla4_83xx_reset_entry_hdr *)p_entry;
112962306a36Sopenharmony_ci		switch (p_hdr->cmd) {
113062306a36Sopenharmony_ci		case OPCODE_NOP:
113162306a36Sopenharmony_ci			break;
113262306a36Sopenharmony_ci		case OPCODE_WRITE_LIST:
113362306a36Sopenharmony_ci			qla4_83xx_write_list(ha, p_hdr);
113462306a36Sopenharmony_ci			break;
113562306a36Sopenharmony_ci		case OPCODE_READ_WRITE_LIST:
113662306a36Sopenharmony_ci			qla4_83xx_read_write_list(ha, p_hdr);
113762306a36Sopenharmony_ci			break;
113862306a36Sopenharmony_ci		case OPCODE_POLL_LIST:
113962306a36Sopenharmony_ci			qla4_83xx_poll_list(ha, p_hdr);
114062306a36Sopenharmony_ci			break;
114162306a36Sopenharmony_ci		case OPCODE_POLL_WRITE_LIST:
114262306a36Sopenharmony_ci			qla4_83xx_poll_write_list(ha, p_hdr);
114362306a36Sopenharmony_ci			break;
114462306a36Sopenharmony_ci		case OPCODE_READ_MODIFY_WRITE:
114562306a36Sopenharmony_ci			qla4_83xx_read_modify_write(ha, p_hdr);
114662306a36Sopenharmony_ci			break;
114762306a36Sopenharmony_ci		case OPCODE_SEQ_PAUSE:
114862306a36Sopenharmony_ci			qla4_83xx_pause(ha, p_hdr);
114962306a36Sopenharmony_ci			break;
115062306a36Sopenharmony_ci		case OPCODE_SEQ_END:
115162306a36Sopenharmony_ci			qla4_83xx_seq_end(ha, p_hdr);
115262306a36Sopenharmony_ci			break;
115362306a36Sopenharmony_ci		case OPCODE_TMPL_END:
115462306a36Sopenharmony_ci			qla4_83xx_template_end(ha, p_hdr);
115562306a36Sopenharmony_ci			break;
115662306a36Sopenharmony_ci		case OPCODE_POLL_READ_LIST:
115762306a36Sopenharmony_ci			qla4_83xx_poll_read_list(ha, p_hdr);
115862306a36Sopenharmony_ci			break;
115962306a36Sopenharmony_ci		default:
116062306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: Unknown command ==> 0x%04x on entry = %d\n",
116162306a36Sopenharmony_ci				   __func__, p_hdr->cmd, index);
116262306a36Sopenharmony_ci			break;
116362306a36Sopenharmony_ci		}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		/* Set pointer to next entry in the sequence. */
116662306a36Sopenharmony_ci		p_entry += p_hdr->size;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	ha->reset_tmplt.seq_index = index;
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic void qla4_83xx_process_stop_seq(struct scsi_qla_host *ha)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	ha->reset_tmplt.seq_index = 0;
117562306a36Sopenharmony_ci	qla4_83xx_process_reset_template(ha, ha->reset_tmplt.stop_offset);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (ha->reset_tmplt.seq_end != 1)
117862306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Abrupt STOP Sub-Sequence end.\n",
117962306a36Sopenharmony_ci			   __func__);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void qla4_83xx_process_start_seq(struct scsi_qla_host *ha)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	qla4_83xx_process_reset_template(ha, ha->reset_tmplt.start_offset);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (ha->reset_tmplt.template_end != 1)
118762306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Abrupt START Sub-Sequence end.\n",
118862306a36Sopenharmony_ci			   __func__);
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic void qla4_83xx_process_init_seq(struct scsi_qla_host *ha)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	qla4_83xx_process_reset_template(ha, ha->reset_tmplt.init_offset);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if (ha->reset_tmplt.seq_end != 1)
119662306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Abrupt INIT Sub-Sequence end.\n",
119762306a36Sopenharmony_ci			   __func__);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic int qla4_83xx_restart(struct scsi_qla_host *ha)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
120362306a36Sopenharmony_ci	uint32_t idc_ctrl;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	qla4_83xx_process_stop_seq(ha);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/*
120862306a36Sopenharmony_ci	 * Collect minidump.
120962306a36Sopenharmony_ci	 * If IDC_CTRL BIT1 is set, clear it on going to INIT state and
121062306a36Sopenharmony_ci	 * don't collect minidump
121162306a36Sopenharmony_ci	 */
121262306a36Sopenharmony_ci	idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
121362306a36Sopenharmony_ci	if (idc_ctrl & GRACEFUL_RESET_BIT1) {
121462306a36Sopenharmony_ci		qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL,
121562306a36Sopenharmony_ci				 (idc_ctrl & ~GRACEFUL_RESET_BIT1));
121662306a36Sopenharmony_ci		ql4_printk(KERN_INFO, ha, "%s: Graceful RESET: Not collecting minidump\n",
121762306a36Sopenharmony_ci			   __func__);
121862306a36Sopenharmony_ci	} else {
121962306a36Sopenharmony_ci		qla4_8xxx_get_minidump(ha);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	qla4_83xx_process_init_seq(ha);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (qla4_83xx_copy_bootloader(ha)) {
122562306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Copy bootloader, firmware restart failed!\n",
122662306a36Sopenharmony_ci			   __func__);
122762306a36Sopenharmony_ci		ret_val = QLA_ERROR;
122862306a36Sopenharmony_ci		goto exit_restart;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	qla4_83xx_wr_reg(ha, QLA83XX_FW_IMAGE_VALID, QLA83XX_BOOT_FROM_FLASH);
123262306a36Sopenharmony_ci	qla4_83xx_process_start_seq(ha);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ciexit_restart:
123562306a36Sopenharmony_ci	return ret_val;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ciint qla4_83xx_start_firmware(struct scsi_qla_host *ha)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	int ret_val = QLA_SUCCESS;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	ret_val = qla4_83xx_restart(ha);
124362306a36Sopenharmony_ci	if (ret_val == QLA_ERROR) {
124462306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Restart error\n", __func__);
124562306a36Sopenharmony_ci		goto exit_start_fw;
124662306a36Sopenharmony_ci	} else {
124762306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Restart done\n",
124862306a36Sopenharmony_ci				  __func__));
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	ret_val = qla4_83xx_check_cmd_peg_status(ha);
125262306a36Sopenharmony_ci	if (ret_val == QLA_ERROR)
125362306a36Sopenharmony_ci		ql4_printk(KERN_ERR, ha, "%s: Peg not initialized\n",
125462306a36Sopenharmony_ci			   __func__);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ciexit_start_fw:
125762306a36Sopenharmony_ci	return ret_val;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci/*----------------------Interrupt Related functions ---------------------*/
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic void qla4_83xx_disable_iocb_intrs(struct scsi_qla_host *ha)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	if (test_and_clear_bit(AF_83XX_IOCB_INTR_ON, &ha->flags))
126562306a36Sopenharmony_ci		qla4_8xxx_intr_disable(ha);
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic void qla4_83xx_disable_mbox_intrs(struct scsi_qla_host *ha)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	uint32_t mb_int, ret;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if (test_and_clear_bit(AF_83XX_MBOX_INTR_ON, &ha->flags)) {
127362306a36Sopenharmony_ci		ret = readl(&ha->qla4_83xx_reg->mbox_int);
127462306a36Sopenharmony_ci		mb_int = ret & ~INT_ENABLE_FW_MB;
127562306a36Sopenharmony_ci		writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
127662306a36Sopenharmony_ci		writel(1, &ha->qla4_83xx_reg->leg_int_mask);
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_civoid qla4_83xx_disable_intrs(struct scsi_qla_host *ha)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	qla4_83xx_disable_mbox_intrs(ha);
128362306a36Sopenharmony_ci	qla4_83xx_disable_iocb_intrs(ha);
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic void qla4_83xx_enable_iocb_intrs(struct scsi_qla_host *ha)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	if (!test_bit(AF_83XX_IOCB_INTR_ON, &ha->flags)) {
128962306a36Sopenharmony_ci		qla4_8xxx_intr_enable(ha);
129062306a36Sopenharmony_ci		set_bit(AF_83XX_IOCB_INTR_ON, &ha->flags);
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_civoid qla4_83xx_enable_mbox_intrs(struct scsi_qla_host *ha)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	uint32_t mb_int;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (!test_bit(AF_83XX_MBOX_INTR_ON, &ha->flags)) {
129962306a36Sopenharmony_ci		mb_int = INT_ENABLE_FW_MB;
130062306a36Sopenharmony_ci		writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
130162306a36Sopenharmony_ci		writel(0, &ha->qla4_83xx_reg->leg_int_mask);
130262306a36Sopenharmony_ci		set_bit(AF_83XX_MBOX_INTR_ON, &ha->flags);
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_civoid qla4_83xx_enable_intrs(struct scsi_qla_host *ha)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	qla4_83xx_enable_mbox_intrs(ha);
131062306a36Sopenharmony_ci	qla4_83xx_enable_iocb_intrs(ha);
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_civoid qla4_83xx_queue_mbox_cmd(struct scsi_qla_host *ha, uint32_t *mbx_cmd,
131562306a36Sopenharmony_ci			      int incount)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	int i;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/* Load all mailbox registers, except mailbox 0. */
132062306a36Sopenharmony_ci	for (i = 1; i < incount; i++)
132162306a36Sopenharmony_ci		writel(mbx_cmd[i], &ha->qla4_83xx_reg->mailbox_in[i]);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	writel(mbx_cmd[0], &ha->qla4_83xx_reg->mailbox_in[0]);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	/* Set Host Interrupt register to 1, to tell the firmware that
132662306a36Sopenharmony_ci	 * a mailbox command is pending. Firmware after reading the
132762306a36Sopenharmony_ci	 * mailbox command, clears the host interrupt register */
132862306a36Sopenharmony_ci	writel(HINT_MBX_INT_PENDING, &ha->qla4_83xx_reg->host_intr);
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_civoid qla4_83xx_process_mbox_intr(struct scsi_qla_host *ha, int outcount)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	int intr_status;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	intr_status = readl(&ha->qla4_83xx_reg->risc_intr);
133662306a36Sopenharmony_ci	if (intr_status) {
133762306a36Sopenharmony_ci		ha->mbox_status_count = outcount;
133862306a36Sopenharmony_ci		ha->isp_ops->interrupt_service_routine(ha, intr_status);
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/**
134362306a36Sopenharmony_ci * qla4_83xx_isp_reset - Resets ISP and aborts all outstanding commands.
134462306a36Sopenharmony_ci * @ha: pointer to host adapter structure.
134562306a36Sopenharmony_ci **/
134662306a36Sopenharmony_ciint qla4_83xx_isp_reset(struct scsi_qla_host *ha)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	int rval;
134962306a36Sopenharmony_ci	uint32_t dev_state;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	ha->isp_ops->idc_lock(ha);
135262306a36Sopenharmony_ci	dev_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DEV_STATE);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	if (ql4xdontresethba)
135562306a36Sopenharmony_ci		qla4_83xx_set_idc_dontreset(ha);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (dev_state == QLA8XXX_DEV_READY) {
135862306a36Sopenharmony_ci		/* If IDC_CTRL DONTRESETHBA_BIT0 is set dont do reset
135962306a36Sopenharmony_ci		 * recovery */
136062306a36Sopenharmony_ci		if (qla4_83xx_idc_dontreset(ha) == DONTRESET_BIT0) {
136162306a36Sopenharmony_ci			ql4_printk(KERN_ERR, ha, "%s: Reset recovery disabled\n",
136262306a36Sopenharmony_ci				   __func__);
136362306a36Sopenharmony_ci			rval = QLA_ERROR;
136462306a36Sopenharmony_ci			goto exit_isp_reset;
136562306a36Sopenharmony_ci		}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha, "%s: HW State: NEED RESET\n",
136862306a36Sopenharmony_ci				  __func__));
136962306a36Sopenharmony_ci		qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DEV_STATE,
137062306a36Sopenharmony_ci				    QLA8XXX_DEV_NEED_RESET);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	} else {
137362306a36Sopenharmony_ci		/* If device_state is NEED_RESET, go ahead with
137462306a36Sopenharmony_ci		 * Reset,irrespective of ql4xdontresethba. This is to allow a
137562306a36Sopenharmony_ci		 * non-reset-owner to force a reset. Non-reset-owner sets
137662306a36Sopenharmony_ci		 * the IDC_CTRL BIT0 to prevent Reset-owner from doing a Reset
137762306a36Sopenharmony_ci		 * and then forces a Reset by setting device_state to
137862306a36Sopenharmony_ci		 * NEED_RESET. */
137962306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha,
138062306a36Sopenharmony_ci				  "%s: HW state already set to NEED_RESET\n",
138162306a36Sopenharmony_ci				  __func__));
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	/* For ISP8324 and ISP8042, Reset owner is NIC, iSCSI or FCOE based on
138562306a36Sopenharmony_ci	 * priority and which drivers are present. Unlike ISP8022, the function
138662306a36Sopenharmony_ci	 * setting NEED_RESET, may not be the Reset owner. */
138762306a36Sopenharmony_ci	if (qla4_83xx_can_perform_reset(ha))
138862306a36Sopenharmony_ci		set_bit(AF_8XXX_RST_OWNER, &ha->flags);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	ha->isp_ops->idc_unlock(ha);
139162306a36Sopenharmony_ci	rval = qla4_8xxx_device_state_handler(ha);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	ha->isp_ops->idc_lock(ha);
139462306a36Sopenharmony_ci	qla4_8xxx_clear_rst_ready(ha);
139562306a36Sopenharmony_ciexit_isp_reset:
139662306a36Sopenharmony_ci	ha->isp_ops->idc_unlock(ha);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	if (rval == QLA_SUCCESS)
139962306a36Sopenharmony_ci		clear_bit(AF_FW_RECOVERY, &ha->flags);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	return rval;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic void qla4_83xx_dump_pause_control_regs(struct scsi_qla_host *ha)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	u32 val = 0, val1 = 0;
140762306a36Sopenharmony_ci	int i;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	qla4_83xx_rd_reg_indirect(ha, QLA83XX_SRE_SHIM_CONTROL, &val);
141062306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha, "SRE-Shim Ctrl:0x%x\n", val));
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* Port 0 Rx Buffer Pause Threshold Registers. */
141362306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
141462306a36Sopenharmony_ci		"Port 0 Rx Buffer Pause Threshold Registers[TC7..TC0]:"));
141562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
141662306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha,
141762306a36Sopenharmony_ci				QLA83XX_PORT0_RXB_PAUSE_THRS + (i * 0x4), &val);
141862306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x ", val));
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	/* Port 1 Rx Buffer Pause Threshold Registers. */
142462306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
142562306a36Sopenharmony_ci		"Port 1 Rx Buffer Pause Threshold Registers[TC7..TC0]:"));
142662306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
142762306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha,
142862306a36Sopenharmony_ci				QLA83XX_PORT1_RXB_PAUSE_THRS + (i * 0x4), &val);
142962306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x  ", val));
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	/* Port 0 RxB Traffic Class Max Cell Registers. */
143562306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
143662306a36Sopenharmony_ci		"Port 0 RxB Traffic Class Max Cell Registers[3..0]:"));
143762306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
143862306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha,
143962306a36Sopenharmony_ci			       QLA83XX_PORT0_RXB_TC_MAX_CELL + (i * 0x4), &val);
144062306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x  ", val));
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* Port 1 RxB Traffic Class Max Cell Registers. */
144662306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
144762306a36Sopenharmony_ci		"Port 1 RxB Traffic Class Max Cell Registers[3..0]:"));
144862306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
144962306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha,
145062306a36Sopenharmony_ci			       QLA83XX_PORT1_RXB_TC_MAX_CELL + (i * 0x4), &val);
145162306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x  ", val));
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Port 0 RxB Rx Traffic Class Stats. */
145762306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
145862306a36Sopenharmony_ci			  "Port 0 RxB Rx Traffic Class Stats [TC7..TC0]"));
145962306a36Sopenharmony_ci	for (i = 7; i >= 0; i--) {
146062306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT0_RXB_TC_STATS, &val);
146162306a36Sopenharmony_ci		val &= ~(0x7 << 29);    /* Reset bits 29 to 31 */
146262306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, QLA83XX_PORT0_RXB_TC_STATS,
146362306a36Sopenharmony_ci					  (val | (i << 29)));
146462306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT0_RXB_TC_STATS, &val);
146562306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x  ", val));
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	/* Port 1 RxB Rx Traffic Class Stats. */
147162306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
147262306a36Sopenharmony_ci			  "Port 1 RxB Rx Traffic Class Stats [TC7..TC0]"));
147362306a36Sopenharmony_ci	for (i = 7; i >= 0; i--) {
147462306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT1_RXB_TC_STATS, &val);
147562306a36Sopenharmony_ci		val &= ~(0x7 << 29);    /* Reset bits 29 to 31 */
147662306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha, QLA83XX_PORT1_RXB_TC_STATS,
147762306a36Sopenharmony_ci					  (val | (i << 29)));
147862306a36Sopenharmony_ci		qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT1_RXB_TC_STATS, &val);
147962306a36Sopenharmony_ci		DEBUG2(pr_info("0x%x  ", val));
148062306a36Sopenharmony_ci	}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	DEBUG2(pr_info("\n"));
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT2_IFB_PAUSE_THRS, &val);
148562306a36Sopenharmony_ci	qla4_83xx_rd_reg_indirect(ha, QLA83XX_PORT3_IFB_PAUSE_THRS, &val1);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	DEBUG2(ql4_printk(KERN_INFO, ha,
148862306a36Sopenharmony_ci			  "IFB-Pause Thresholds: Port 2:0x%x, Port 3:0x%x\n",
148962306a36Sopenharmony_ci			  val, val1));
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic void __qla4_83xx_disable_pause(struct scsi_qla_host *ha)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	int i;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* set SRE-Shim Control Register */
149762306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_SRE_SHIM_CONTROL,
149862306a36Sopenharmony_ci				  QLA83XX_SET_PAUSE_VAL);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
150162306a36Sopenharmony_ci		/* Port 0 Rx Buffer Pause Threshold Registers. */
150262306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha,
150362306a36Sopenharmony_ci				      QLA83XX_PORT0_RXB_PAUSE_THRS + (i * 0x4),
150462306a36Sopenharmony_ci				      QLA83XX_SET_PAUSE_VAL);
150562306a36Sopenharmony_ci		/* Port 1 Rx Buffer Pause Threshold Registers. */
150662306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha,
150762306a36Sopenharmony_ci				      QLA83XX_PORT1_RXB_PAUSE_THRS + (i * 0x4),
150862306a36Sopenharmony_ci				      QLA83XX_SET_PAUSE_VAL);
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
151262306a36Sopenharmony_ci		/* Port 0 RxB Traffic Class Max Cell Registers. */
151362306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha,
151462306a36Sopenharmony_ci				     QLA83XX_PORT0_RXB_TC_MAX_CELL + (i * 0x4),
151562306a36Sopenharmony_ci				     QLA83XX_SET_TC_MAX_CELL_VAL);
151662306a36Sopenharmony_ci		/* Port 1 RxB Traffic Class Max Cell Registers. */
151762306a36Sopenharmony_ci		qla4_83xx_wr_reg_indirect(ha,
151862306a36Sopenharmony_ci				     QLA83XX_PORT1_RXB_TC_MAX_CELL + (i * 0x4),
151962306a36Sopenharmony_ci				     QLA83XX_SET_TC_MAX_CELL_VAL);
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_PORT2_IFB_PAUSE_THRS,
152362306a36Sopenharmony_ci				  QLA83XX_SET_PAUSE_VAL);
152462306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_PORT3_IFB_PAUSE_THRS,
152562306a36Sopenharmony_ci				  QLA83XX_SET_PAUSE_VAL);
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	ql4_printk(KERN_INFO, ha, "Disabled pause frames successfully.\n");
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci/**
153162306a36Sopenharmony_ci * qla4_83xx_eport_init - Initialize EPort.
153262306a36Sopenharmony_ci * @ha: Pointer to host adapter structure.
153362306a36Sopenharmony_ci *
153462306a36Sopenharmony_ci * If EPort hardware is in reset state before disabling pause, there would be
153562306a36Sopenharmony_ci * serious hardware wedging issues. To prevent this perform eport init everytime
153662306a36Sopenharmony_ci * before disabling pause frames.
153762306a36Sopenharmony_ci **/
153862306a36Sopenharmony_cistatic void qla4_83xx_eport_init(struct scsi_qla_host *ha)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	/* Clear the 8 registers */
154162306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_REG, 0x0);
154262306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_PORT0, 0x0);
154362306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_PORT1, 0x0);
154462306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_PORT2, 0x0);
154562306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_PORT3, 0x0);
154662306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_SRE_SHIM, 0x0);
154762306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_EPG_SHIM, 0x0);
154862306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_ETHER_PCS, 0x0);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/* Write any value to Reset Control register */
155162306a36Sopenharmony_ci	qla4_83xx_wr_reg_indirect(ha, QLA83XX_RESET_CONTROL, 0xFF);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	ql4_printk(KERN_INFO, ha, "EPORT is out of reset.\n");
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_civoid qla4_83xx_disable_pause(struct scsi_qla_host *ha)
155762306a36Sopenharmony_ci{
155862306a36Sopenharmony_ci	ha->isp_ops->idc_lock(ha);
155962306a36Sopenharmony_ci	/* Before disabling pause frames, ensure that eport is not in reset */
156062306a36Sopenharmony_ci	qla4_83xx_eport_init(ha);
156162306a36Sopenharmony_ci	qla4_83xx_dump_pause_control_regs(ha);
156262306a36Sopenharmony_ci	__qla4_83xx_disable_pause(ha);
156362306a36Sopenharmony_ci	ha->isp_ops->idc_unlock(ha);
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci/**
156762306a36Sopenharmony_ci * qla4_83xx_is_detached - Check if we are marked invisible.
156862306a36Sopenharmony_ci * @ha: Pointer to host adapter structure.
156962306a36Sopenharmony_ci **/
157062306a36Sopenharmony_ciint qla4_83xx_is_detached(struct scsi_qla_host *ha)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	uint32_t drv_active;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (test_bit(AF_INIT_DONE, &ha->flags) &&
157762306a36Sopenharmony_ci	    !(drv_active & (1 << ha->func_num))) {
157862306a36Sopenharmony_ci		DEBUG2(ql4_printk(KERN_INFO, ha, "%s: drv_active = 0x%X\n",
157962306a36Sopenharmony_ci				  __func__, drv_active));
158062306a36Sopenharmony_ci		return QLA_SUCCESS;
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	return QLA_ERROR;
158462306a36Sopenharmony_ci}
1585