162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QLogic Fibre Channel HBA Driver
462306a36Sopenharmony_ci * Copyright (c)  2003-2014 QLogic Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "qla_def.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/vmalloc.h>
1162306a36Sopenharmony_ci#include <linux/uaccess.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * NVRAM support routines
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * qla2x00_lock_nvram_access() -
1962306a36Sopenharmony_ci * @ha: HA context
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistatic void
2262306a36Sopenharmony_ciqla2x00_lock_nvram_access(struct qla_hw_data *ha)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	uint16_t data;
2562306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
2862306a36Sopenharmony_ci		data = rd_reg_word(&reg->nvram);
2962306a36Sopenharmony_ci		while (data & NVR_BUSY) {
3062306a36Sopenharmony_ci			udelay(100);
3162306a36Sopenharmony_ci			data = rd_reg_word(&reg->nvram);
3262306a36Sopenharmony_ci		}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci		/* Lock resource */
3562306a36Sopenharmony_ci		wrt_reg_word(&reg->u.isp2300.host_semaphore, 0x1);
3662306a36Sopenharmony_ci		rd_reg_word(&reg->u.isp2300.host_semaphore);
3762306a36Sopenharmony_ci		udelay(5);
3862306a36Sopenharmony_ci		data = rd_reg_word(&reg->u.isp2300.host_semaphore);
3962306a36Sopenharmony_ci		while ((data & BIT_0) == 0) {
4062306a36Sopenharmony_ci			/* Lock failed */
4162306a36Sopenharmony_ci			udelay(100);
4262306a36Sopenharmony_ci			wrt_reg_word(&reg->u.isp2300.host_semaphore, 0x1);
4362306a36Sopenharmony_ci			rd_reg_word(&reg->u.isp2300.host_semaphore);
4462306a36Sopenharmony_ci			udelay(5);
4562306a36Sopenharmony_ci			data = rd_reg_word(&reg->u.isp2300.host_semaphore);
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/**
5162306a36Sopenharmony_ci * qla2x00_unlock_nvram_access() -
5262306a36Sopenharmony_ci * @ha: HA context
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic void
5562306a36Sopenharmony_ciqla2x00_unlock_nvram_access(struct qla_hw_data *ha)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
6062306a36Sopenharmony_ci		wrt_reg_word(&reg->u.isp2300.host_semaphore, 0);
6162306a36Sopenharmony_ci		rd_reg_word(&reg->u.isp2300.host_semaphore);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
6762306a36Sopenharmony_ci * @ha: HA context
6862306a36Sopenharmony_ci * @data: Serial interface selector
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic void
7162306a36Sopenharmony_ciqla2x00_nv_write(struct qla_hw_data *ha, uint16_t data)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
7662306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
7762306a36Sopenharmony_ci	NVRAM_DELAY();
7862306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_CLOCK |
7962306a36Sopenharmony_ci	    NVR_WRT_ENABLE);
8062306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
8162306a36Sopenharmony_ci	NVRAM_DELAY();
8262306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
8362306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
8462306a36Sopenharmony_ci	NVRAM_DELAY();
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/**
8862306a36Sopenharmony_ci * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
8962306a36Sopenharmony_ci *	NVRAM.
9062306a36Sopenharmony_ci * @ha: HA context
9162306a36Sopenharmony_ci * @nv_cmd: NVRAM command
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * Bit definitions for NVRAM command:
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci *	Bit 26     = start bit
9662306a36Sopenharmony_ci *	Bit 25, 24 = opcode
9762306a36Sopenharmony_ci *	Bit 23-16  = address
9862306a36Sopenharmony_ci *	Bit 15-0   = write data
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Returns the word read from nvram @addr.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic uint16_t
10362306a36Sopenharmony_ciqla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	uint8_t		cnt;
10662306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
10762306a36Sopenharmony_ci	uint16_t	data = 0;
10862306a36Sopenharmony_ci	uint16_t	reg_data;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* Send command to NVRAM. */
11162306a36Sopenharmony_ci	nv_cmd <<= 5;
11262306a36Sopenharmony_ci	for (cnt = 0; cnt < 11; cnt++) {
11362306a36Sopenharmony_ci		if (nv_cmd & BIT_31)
11462306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
11562306a36Sopenharmony_ci		else
11662306a36Sopenharmony_ci			qla2x00_nv_write(ha, 0);
11762306a36Sopenharmony_ci		nv_cmd <<= 1;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Read data from NVRAM. */
12162306a36Sopenharmony_ci	for (cnt = 0; cnt < 16; cnt++) {
12262306a36Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT | NVR_CLOCK);
12362306a36Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
12462306a36Sopenharmony_ci		NVRAM_DELAY();
12562306a36Sopenharmony_ci		data <<= 1;
12662306a36Sopenharmony_ci		reg_data = rd_reg_word(&reg->nvram);
12762306a36Sopenharmony_ci		if (reg_data & NVR_DATA_IN)
12862306a36Sopenharmony_ci			data |= BIT_0;
12962306a36Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT);
13062306a36Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
13162306a36Sopenharmony_ci		NVRAM_DELAY();
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Deselect chip. */
13562306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_DESELECT);
13662306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
13762306a36Sopenharmony_ci	NVRAM_DELAY();
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return data;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
14562306a36Sopenharmony_ci *	request routine to get the word from NVRAM.
14662306a36Sopenharmony_ci * @ha: HA context
14762306a36Sopenharmony_ci * @addr: Address in NVRAM to read
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * Returns the word read from nvram @addr.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic uint16_t
15262306a36Sopenharmony_ciqla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	uint16_t	data;
15562306a36Sopenharmony_ci	uint32_t	nv_cmd;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	nv_cmd = addr << 16;
15862306a36Sopenharmony_ci	nv_cmd |= NV_READ_OP;
15962306a36Sopenharmony_ci	data = qla2x00_nvram_request(ha, nv_cmd);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return (data);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/**
16562306a36Sopenharmony_ci * qla2x00_nv_deselect() - Deselect NVRAM operations.
16662306a36Sopenharmony_ci * @ha: HA context
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_cistatic void
16962306a36Sopenharmony_ciqla2x00_nv_deselect(struct qla_hw_data *ha)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_DESELECT);
17462306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
17562306a36Sopenharmony_ci	NVRAM_DELAY();
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * qla2x00_write_nvram_word() - Write NVRAM data.
18062306a36Sopenharmony_ci * @ha: HA context
18162306a36Sopenharmony_ci * @addr: Address in NVRAM to write
18262306a36Sopenharmony_ci * @data: word to program
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic void
18562306a36Sopenharmony_ciqla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, __le16 data)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int count;
18862306a36Sopenharmony_ci	uint16_t word;
18962306a36Sopenharmony_ci	uint32_t nv_cmd, wait_cnt;
19062306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
19162306a36Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
19462306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
19562306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	for (word = 0; word < 8; word++)
19862306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Write data */
20362306a36Sopenharmony_ci	nv_cmd = (addr << 16) | NV_WRITE_OP;
20462306a36Sopenharmony_ci	nv_cmd |= (__force u16)data;
20562306a36Sopenharmony_ci	nv_cmd <<= 5;
20662306a36Sopenharmony_ci	for (count = 0; count < 27; count++) {
20762306a36Sopenharmony_ci		if (nv_cmd & BIT_31)
20862306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
20962306a36Sopenharmony_ci		else
21062306a36Sopenharmony_ci			qla2x00_nv_write(ha, 0);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		nv_cmd <<= 1;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Wait for NVRAM to become ready */
21862306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
21962306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
22062306a36Sopenharmony_ci	wait_cnt = NVR_WAIT_CNT;
22162306a36Sopenharmony_ci	do {
22262306a36Sopenharmony_ci		if (!--wait_cnt) {
22362306a36Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x708d,
22462306a36Sopenharmony_ci			    "NVRAM didn't go ready...\n");
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci		NVRAM_DELAY();
22862306a36Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
22962306a36Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Disable writes */
23462306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
23562306a36Sopenharmony_ci	for (count = 0; count < 10; count++)
23662306a36Sopenharmony_ci		qla2x00_nv_write(ha, 0);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int
24262306a36Sopenharmony_ciqla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr,
24362306a36Sopenharmony_ci			     __le16 data, uint32_t tmo)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int ret, count;
24662306a36Sopenharmony_ci	uint16_t word;
24762306a36Sopenharmony_ci	uint32_t nv_cmd;
24862306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ret = QLA_SUCCESS;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
25362306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
25462306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	for (word = 0; word < 8; word++)
25762306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Write data */
26262306a36Sopenharmony_ci	nv_cmd = (addr << 16) | NV_WRITE_OP;
26362306a36Sopenharmony_ci	nv_cmd |= (__force u16)data;
26462306a36Sopenharmony_ci	nv_cmd <<= 5;
26562306a36Sopenharmony_ci	for (count = 0; count < 27; count++) {
26662306a36Sopenharmony_ci		if (nv_cmd & BIT_31)
26762306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
26862306a36Sopenharmony_ci		else
26962306a36Sopenharmony_ci			qla2x00_nv_write(ha, 0);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		nv_cmd <<= 1;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Wait for NVRAM to become ready */
27762306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
27862306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
27962306a36Sopenharmony_ci	do {
28062306a36Sopenharmony_ci		NVRAM_DELAY();
28162306a36Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
28262306a36Sopenharmony_ci		if (!--tmo) {
28362306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
28462306a36Sopenharmony_ci			break;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Disable writes */
29162306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
29262306a36Sopenharmony_ci	for (count = 0; count < 10; count++)
29362306a36Sopenharmony_ci		qla2x00_nv_write(ha, 0);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return ret;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/**
30162306a36Sopenharmony_ci * qla2x00_clear_nvram_protection() -
30262306a36Sopenharmony_ci * @ha: HA context
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic int
30562306a36Sopenharmony_ciqla2x00_clear_nvram_protection(struct qla_hw_data *ha)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	int ret, stat;
30862306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
30962306a36Sopenharmony_ci	uint32_t word, wait_cnt;
31062306a36Sopenharmony_ci	__le16 wprot, wprot_old;
31162306a36Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* Clear NVRAM write protection. */
31462306a36Sopenharmony_ci	ret = QLA_FUNCTION_FAILED;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	wprot_old = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
31762306a36Sopenharmony_ci	stat = qla2x00_write_nvram_word_tmo(ha, ha->nvram_base,
31862306a36Sopenharmony_ci					    cpu_to_le16(0x1234), 100000);
31962306a36Sopenharmony_ci	wprot = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
32062306a36Sopenharmony_ci	if (stat != QLA_SUCCESS || wprot != cpu_to_le16(0x1234)) {
32162306a36Sopenharmony_ci		/* Write enable. */
32262306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
32362306a36Sopenharmony_ci		qla2x00_nv_write(ha, 0);
32462306a36Sopenharmony_ci		qla2x00_nv_write(ha, 0);
32562306a36Sopenharmony_ci		for (word = 0; word < 8; word++)
32662306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		qla2x00_nv_deselect(ha);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		/* Enable protection register. */
33162306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
33262306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
33362306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
33462306a36Sopenharmony_ci		for (word = 0; word < 8; word++)
33562306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		qla2x00_nv_deselect(ha);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		/* Clear protection register (ffff is cleared). */
34062306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
34162306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
34262306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
34362306a36Sopenharmony_ci		for (word = 0; word < 8; word++)
34462306a36Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		qla2x00_nv_deselect(ha);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		/* Wait for NVRAM to become ready. */
34962306a36Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT);
35062306a36Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
35162306a36Sopenharmony_ci		wait_cnt = NVR_WAIT_CNT;
35262306a36Sopenharmony_ci		do {
35362306a36Sopenharmony_ci			if (!--wait_cnt) {
35462306a36Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x708e,
35562306a36Sopenharmony_ci				    "NVRAM didn't go ready...\n");
35662306a36Sopenharmony_ci				break;
35762306a36Sopenharmony_ci			}
35862306a36Sopenharmony_ci			NVRAM_DELAY();
35962306a36Sopenharmony_ci			word = rd_reg_word(&reg->nvram);
36062306a36Sopenharmony_ci		} while ((word & NVR_DATA_IN) == 0);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		if (wait_cnt)
36362306a36Sopenharmony_ci			ret = QLA_SUCCESS;
36462306a36Sopenharmony_ci	} else
36562306a36Sopenharmony_ci		qla2x00_write_nvram_word(ha, ha->nvram_base, wprot_old);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return ret;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void
37162306a36Sopenharmony_ciqla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
37462306a36Sopenharmony_ci	uint32_t word, wait_cnt;
37562306a36Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (stat != QLA_SUCCESS)
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Set NVRAM write protection. */
38162306a36Sopenharmony_ci	/* Write enable. */
38262306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
38362306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
38462306a36Sopenharmony_ci	qla2x00_nv_write(ha, 0);
38562306a36Sopenharmony_ci	for (word = 0; word < 8; word++)
38662306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* Enable protection register. */
39162306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
39262306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
39362306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
39462306a36Sopenharmony_ci	for (word = 0; word < 8; word++)
39562306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Enable protection register. */
40062306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
40162306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
40262306a36Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
40362306a36Sopenharmony_ci	for (word = 0; word < 8; word++)
40462306a36Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	qla2x00_nv_deselect(ha);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* Wait for NVRAM to become ready. */
40962306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
41062306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
41162306a36Sopenharmony_ci	wait_cnt = NVR_WAIT_CNT;
41262306a36Sopenharmony_ci	do {
41362306a36Sopenharmony_ci		if (!--wait_cnt) {
41462306a36Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x708f,
41562306a36Sopenharmony_ci			    "NVRAM didn't go ready...\n");
41662306a36Sopenharmony_ci			break;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci		NVRAM_DELAY();
41962306a36Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
42062306a36Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/*****************************************************************************/
42562306a36Sopenharmony_ci/* Flash Manipulation Routines                                               */
42662306a36Sopenharmony_ci/*****************************************************************************/
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic inline uint32_t
42962306a36Sopenharmony_ciflash_conf_addr(struct qla_hw_data *ha, uint32_t faddr)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	return ha->flash_conf_off + faddr;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic inline uint32_t
43562306a36Sopenharmony_ciflash_data_addr(struct qla_hw_data *ha, uint32_t faddr)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	return ha->flash_data_off + faddr;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic inline uint32_t
44162306a36Sopenharmony_cinvram_conf_addr(struct qla_hw_data *ha, uint32_t naddr)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	return ha->nvram_conf_off + naddr;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic inline uint32_t
44762306a36Sopenharmony_cinvram_data_addr(struct qla_hw_data *ha, uint32_t naddr)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	return ha->nvram_data_off + naddr;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int
45362306a36Sopenharmony_ciqla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t *data)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
45662306a36Sopenharmony_ci	ulong cnt = 30000;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	wrt_reg_dword(&reg->flash_addr, addr & ~FARX_DATA_FLAG);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	while (cnt--) {
46162306a36Sopenharmony_ci		if (rd_reg_dword(&reg->flash_addr) & FARX_DATA_FLAG) {
46262306a36Sopenharmony_ci			*data = rd_reg_dword(&reg->flash_data);
46362306a36Sopenharmony_ci			return QLA_SUCCESS;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci		udelay(10);
46662306a36Sopenharmony_ci		cond_resched();
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ql_log(ql_log_warn, pci_get_drvdata(ha->pdev), 0x7090,
47062306a36Sopenharmony_ci	    "Flash read dword at %x timeout.\n", addr);
47162306a36Sopenharmony_ci	*data = 0xDEADDEAD;
47262306a36Sopenharmony_ci	return QLA_FUNCTION_TIMEOUT;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint
47662306a36Sopenharmony_ciqla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
47762306a36Sopenharmony_ci    uint32_t dwords)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	ulong i;
48062306a36Sopenharmony_ci	int ret = QLA_SUCCESS;
48162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Dword reads to flash. */
48462306a36Sopenharmony_ci	faddr =  flash_data_addr(ha, faddr);
48562306a36Sopenharmony_ci	for (i = 0; i < dwords; i++, faddr++, dwptr++) {
48662306a36Sopenharmony_ci		ret = qla24xx_read_flash_dword(ha, faddr, dwptr);
48762306a36Sopenharmony_ci		if (ret != QLA_SUCCESS)
48862306a36Sopenharmony_ci			break;
48962306a36Sopenharmony_ci		cpu_to_le32s(dwptr);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return ret;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int
49662306a36Sopenharmony_ciqla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
49962306a36Sopenharmony_ci	ulong cnt = 500000;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	wrt_reg_dword(&reg->flash_data, data);
50262306a36Sopenharmony_ci	wrt_reg_dword(&reg->flash_addr, addr | FARX_DATA_FLAG);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	while (cnt--) {
50562306a36Sopenharmony_ci		if (!(rd_reg_dword(&reg->flash_addr) & FARX_DATA_FLAG))
50662306a36Sopenharmony_ci			return QLA_SUCCESS;
50762306a36Sopenharmony_ci		udelay(10);
50862306a36Sopenharmony_ci		cond_resched();
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	ql_log(ql_log_warn, pci_get_drvdata(ha->pdev), 0x7090,
51262306a36Sopenharmony_ci	    "Flash write dword at %x timeout.\n", addr);
51362306a36Sopenharmony_ci	return QLA_FUNCTION_TIMEOUT;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void
51762306a36Sopenharmony_ciqla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
51862306a36Sopenharmony_ci    uint8_t *flash_id)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	uint32_t faddr, ids = 0;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	*man_id = *flash_id = 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x03ab);
52562306a36Sopenharmony_ci	if (!qla24xx_read_flash_dword(ha, faddr, &ids)) {
52662306a36Sopenharmony_ci		*man_id = LSB(ids);
52762306a36Sopenharmony_ci		*flash_id = MSB(ids);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Check if man_id and flash_id are valid. */
53162306a36Sopenharmony_ci	if (ids != 0xDEADDEAD && (*man_id == 0 || *flash_id == 0)) {
53262306a36Sopenharmony_ci		/* Read information using 0x9f opcode
53362306a36Sopenharmony_ci		 * Device ID, Mfg ID would be read in the format:
53462306a36Sopenharmony_ci		 *   <Ext Dev Info><Device ID Part2><Device ID Part 1><Mfg ID>
53562306a36Sopenharmony_ci		 * Example: ATMEL 0x00 01 45 1F
53662306a36Sopenharmony_ci		 * Extract MFG and Dev ID from last two bytes.
53762306a36Sopenharmony_ci		 */
53862306a36Sopenharmony_ci		faddr = flash_conf_addr(ha, 0x009f);
53962306a36Sopenharmony_ci		if (!qla24xx_read_flash_dword(ha, faddr, &ids)) {
54062306a36Sopenharmony_ci			*man_id = LSB(ids);
54162306a36Sopenharmony_ci			*flash_id = MSB(ids);
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int
54762306a36Sopenharmony_ciqla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	const char *loc, *locations[] = { "DEF", "PCI" };
55062306a36Sopenharmony_ci	uint32_t pcihdr, pcids;
55162306a36Sopenharmony_ci	uint16_t cnt, chksum;
55262306a36Sopenharmony_ci	__le16 *wptr;
55362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
55462306a36Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
55562306a36Sopenharmony_ci	struct qla_flt_location *fltl = (void *)req->ring;
55662306a36Sopenharmony_ci	uint32_t *dcode = (uint32_t *)req->ring;
55762306a36Sopenharmony_ci	uint8_t *buf = (void *)req->ring, *bcode,  last_image;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/*
56062306a36Sopenharmony_ci	 * FLT-location structure resides after the last PCI region.
56162306a36Sopenharmony_ci	 */
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/* Begin with sane defaults. */
56462306a36Sopenharmony_ci	loc = locations[0];
56562306a36Sopenharmony_ci	*start = 0;
56662306a36Sopenharmony_ci	if (IS_QLA24XX_TYPE(ha))
56762306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_24;
56862306a36Sopenharmony_ci	else if (IS_QLA25XX(ha))
56962306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR;
57062306a36Sopenharmony_ci	else if (IS_QLA81XX(ha))
57162306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_81;
57262306a36Sopenharmony_ci	else if (IS_P3P_TYPE(ha)) {
57362306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_82;
57462306a36Sopenharmony_ci		goto end;
57562306a36Sopenharmony_ci	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) {
57662306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_83;
57762306a36Sopenharmony_ci		goto end;
57862306a36Sopenharmony_ci	} else if (IS_QLA28XX(ha)) {
57962306a36Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_28;
58062306a36Sopenharmony_ci		goto end;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
58462306a36Sopenharmony_ci	pcihdr = 0;
58562306a36Sopenharmony_ci	do {
58662306a36Sopenharmony_ci		/* Verify PCI expansion ROM header. */
58762306a36Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
58862306a36Sopenharmony_ci		bcode = buf + (pcihdr % 4);
58962306a36Sopenharmony_ci		if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa)
59062306a36Sopenharmony_ci			goto end;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		/* Locate PCI data structure. */
59362306a36Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
59462306a36Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
59562306a36Sopenharmony_ci		bcode = buf + (pcihdr % 4);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		/* Validate signature of PCI data structure. */
59862306a36Sopenharmony_ci		if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
59962306a36Sopenharmony_ci		    bcode[0x2] != 'I' || bcode[0x3] != 'R')
60062306a36Sopenharmony_ci			goto end;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		/* Locate next PCI expansion ROM. */
60562306a36Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
60662306a36Sopenharmony_ci	} while (!last_image);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* Now verify FLT-location structure. */
60962306a36Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, sizeof(*fltl) >> 2);
61062306a36Sopenharmony_ci	if (memcmp(fltl->sig, "QFLT", 4))
61162306a36Sopenharmony_ci		goto end;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	wptr = (__force __le16 *)req->ring;
61462306a36Sopenharmony_ci	cnt = sizeof(*fltl) / sizeof(*wptr);
61562306a36Sopenharmony_ci	for (chksum = 0; cnt--; wptr++)
61662306a36Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
61762306a36Sopenharmony_ci	if (chksum) {
61862306a36Sopenharmony_ci		ql_log(ql_log_fatal, vha, 0x0045,
61962306a36Sopenharmony_ci		    "Inconsistent FLTL detected: checksum=0x%x.\n", chksum);
62062306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e,
62162306a36Sopenharmony_ci		    fltl, sizeof(*fltl));
62262306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* Good data.  Use specified location. */
62662306a36Sopenharmony_ci	loc = locations[1];
62762306a36Sopenharmony_ci	*start = (le16_to_cpu(fltl->start_hi) << 16 |
62862306a36Sopenharmony_ci	    le16_to_cpu(fltl->start_lo)) >> 2;
62962306a36Sopenharmony_ciend:
63062306a36Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x0046,
63162306a36Sopenharmony_ci	    "FLTL[%s] = 0x%x.\n",
63262306a36Sopenharmony_ci	    loc, *start);
63362306a36Sopenharmony_ci	return QLA_SUCCESS;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic void
63762306a36Sopenharmony_ciqla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	const char *locations[] = { "DEF", "FLT" }, *loc = locations[1];
64062306a36Sopenharmony_ci	const uint32_t def_fw[] =
64162306a36Sopenharmony_ci		{ FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 };
64262306a36Sopenharmony_ci	const uint32_t def_boot[] =
64362306a36Sopenharmony_ci		{ FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR_81 };
64462306a36Sopenharmony_ci	const uint32_t def_vpd_nvram[] =
64562306a36Sopenharmony_ci		{ FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR_81 };
64662306a36Sopenharmony_ci	const uint32_t def_vpd0[] =
64762306a36Sopenharmony_ci		{ 0, 0, FA_VPD0_ADDR_81 };
64862306a36Sopenharmony_ci	const uint32_t def_vpd1[] =
64962306a36Sopenharmony_ci		{ 0, 0, FA_VPD1_ADDR_81 };
65062306a36Sopenharmony_ci	const uint32_t def_nvram0[] =
65162306a36Sopenharmony_ci		{ 0, 0, FA_NVRAM0_ADDR_81 };
65262306a36Sopenharmony_ci	const uint32_t def_nvram1[] =
65362306a36Sopenharmony_ci		{ 0, 0, FA_NVRAM1_ADDR_81 };
65462306a36Sopenharmony_ci	const uint32_t def_fdt[] =
65562306a36Sopenharmony_ci		{ FA_FLASH_DESCR_ADDR_24, FA_FLASH_DESCR_ADDR,
65662306a36Sopenharmony_ci			FA_FLASH_DESCR_ADDR_81 };
65762306a36Sopenharmony_ci	const uint32_t def_npiv_conf0[] =
65862306a36Sopenharmony_ci		{ FA_NPIV_CONF0_ADDR_24, FA_NPIV_CONF0_ADDR,
65962306a36Sopenharmony_ci			FA_NPIV_CONF0_ADDR_81 };
66062306a36Sopenharmony_ci	const uint32_t def_npiv_conf1[] =
66162306a36Sopenharmony_ci		{ FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR,
66262306a36Sopenharmony_ci			FA_NPIV_CONF1_ADDR_81 };
66362306a36Sopenharmony_ci	const uint32_t fcp_prio_cfg0[] =
66462306a36Sopenharmony_ci		{ FA_FCP_PRIO0_ADDR, FA_FCP_PRIO0_ADDR_25,
66562306a36Sopenharmony_ci			0 };
66662306a36Sopenharmony_ci	const uint32_t fcp_prio_cfg1[] =
66762306a36Sopenharmony_ci		{ FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25,
66862306a36Sopenharmony_ci			0 };
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
67162306a36Sopenharmony_ci	uint32_t def = IS_QLA81XX(ha) ? 2 : IS_QLA25XX(ha) ? 1 : 0;
67262306a36Sopenharmony_ci	struct qla_flt_header *flt = ha->flt;
67362306a36Sopenharmony_ci	struct qla_flt_region *region = &flt->region[0];
67462306a36Sopenharmony_ci	__le16 *wptr;
67562306a36Sopenharmony_ci	uint16_t cnt, chksum;
67662306a36Sopenharmony_ci	uint32_t start;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* Assign FCP prio region since older adapters may not have FLT, or
67962306a36Sopenharmony_ci	   FCP prio region in it's FLT.
68062306a36Sopenharmony_ci	 */
68162306a36Sopenharmony_ci	ha->flt_region_fcp_prio = (ha->port_no == 0) ?
68262306a36Sopenharmony_ci	    fcp_prio_cfg0[def] : fcp_prio_cfg1[def];
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ha->flt_region_flt = flt_addr;
68562306a36Sopenharmony_ci	wptr = (__force __le16 *)ha->flt;
68662306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, flt, flt_addr << 2,
68762306a36Sopenharmony_ci	    (sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE));
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (le16_to_cpu(*wptr) == 0xffff)
69062306a36Sopenharmony_ci		goto no_flash_data;
69162306a36Sopenharmony_ci	if (flt->version != cpu_to_le16(1)) {
69262306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x0047,
69362306a36Sopenharmony_ci		    "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
69462306a36Sopenharmony_ci		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
69562306a36Sopenharmony_ci		    le16_to_cpu(flt->checksum));
69662306a36Sopenharmony_ci		goto no_flash_data;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	cnt = (sizeof(*flt) + le16_to_cpu(flt->length)) / sizeof(*wptr);
70062306a36Sopenharmony_ci	for (chksum = 0; cnt--; wptr++)
70162306a36Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
70262306a36Sopenharmony_ci	if (chksum) {
70362306a36Sopenharmony_ci		ql_log(ql_log_fatal, vha, 0x0048,
70462306a36Sopenharmony_ci		    "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
70562306a36Sopenharmony_ci		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
70662306a36Sopenharmony_ci		    le16_to_cpu(flt->checksum));
70762306a36Sopenharmony_ci		goto no_flash_data;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	cnt = le16_to_cpu(flt->length) / sizeof(*region);
71162306a36Sopenharmony_ci	for ( ; cnt; cnt--, region++) {
71262306a36Sopenharmony_ci		/* Store addresses as DWORD offsets. */
71362306a36Sopenharmony_ci		start = le32_to_cpu(region->start) >> 2;
71462306a36Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0049,
71562306a36Sopenharmony_ci		    "FLT[%#x]: start=%#x end=%#x size=%#x.\n",
71662306a36Sopenharmony_ci		    le16_to_cpu(region->code), start,
71762306a36Sopenharmony_ci		    le32_to_cpu(region->end) >> 2,
71862306a36Sopenharmony_ci		    le32_to_cpu(region->size) >> 2);
71962306a36Sopenharmony_ci		if (region->attribute)
72062306a36Sopenharmony_ci			ql_log(ql_dbg_init, vha, 0xffff,
72162306a36Sopenharmony_ci			    "Region %x is secure\n", region->code);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		switch (le16_to_cpu(region->code)) {
72462306a36Sopenharmony_ci		case FLT_REG_FCOE_FW:
72562306a36Sopenharmony_ci			if (!IS_QLA8031(ha))
72662306a36Sopenharmony_ci				break;
72762306a36Sopenharmony_ci			ha->flt_region_fw = start;
72862306a36Sopenharmony_ci			break;
72962306a36Sopenharmony_ci		case FLT_REG_FW:
73062306a36Sopenharmony_ci			if (IS_QLA8031(ha))
73162306a36Sopenharmony_ci				break;
73262306a36Sopenharmony_ci			ha->flt_region_fw = start;
73362306a36Sopenharmony_ci			break;
73462306a36Sopenharmony_ci		case FLT_REG_BOOT_CODE:
73562306a36Sopenharmony_ci			ha->flt_region_boot = start;
73662306a36Sopenharmony_ci			break;
73762306a36Sopenharmony_ci		case FLT_REG_VPD_0:
73862306a36Sopenharmony_ci			if (IS_QLA8031(ha))
73962306a36Sopenharmony_ci				break;
74062306a36Sopenharmony_ci			ha->flt_region_vpd_nvram = start;
74162306a36Sopenharmony_ci			if (IS_P3P_TYPE(ha))
74262306a36Sopenharmony_ci				break;
74362306a36Sopenharmony_ci			if (ha->port_no == 0)
74462306a36Sopenharmony_ci				ha->flt_region_vpd = start;
74562306a36Sopenharmony_ci			break;
74662306a36Sopenharmony_ci		case FLT_REG_VPD_1:
74762306a36Sopenharmony_ci			if (IS_P3P_TYPE(ha) || IS_QLA8031(ha))
74862306a36Sopenharmony_ci				break;
74962306a36Sopenharmony_ci			if (ha->port_no == 1)
75062306a36Sopenharmony_ci				ha->flt_region_vpd = start;
75162306a36Sopenharmony_ci			break;
75262306a36Sopenharmony_ci		case FLT_REG_VPD_2:
75362306a36Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
75462306a36Sopenharmony_ci				break;
75562306a36Sopenharmony_ci			if (ha->port_no == 2)
75662306a36Sopenharmony_ci				ha->flt_region_vpd = start;
75762306a36Sopenharmony_ci			break;
75862306a36Sopenharmony_ci		case FLT_REG_VPD_3:
75962306a36Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
76062306a36Sopenharmony_ci				break;
76162306a36Sopenharmony_ci			if (ha->port_no == 3)
76262306a36Sopenharmony_ci				ha->flt_region_vpd = start;
76362306a36Sopenharmony_ci			break;
76462306a36Sopenharmony_ci		case FLT_REG_NVRAM_0:
76562306a36Sopenharmony_ci			if (IS_QLA8031(ha))
76662306a36Sopenharmony_ci				break;
76762306a36Sopenharmony_ci			if (ha->port_no == 0)
76862306a36Sopenharmony_ci				ha->flt_region_nvram = start;
76962306a36Sopenharmony_ci			break;
77062306a36Sopenharmony_ci		case FLT_REG_NVRAM_1:
77162306a36Sopenharmony_ci			if (IS_QLA8031(ha))
77262306a36Sopenharmony_ci				break;
77362306a36Sopenharmony_ci			if (ha->port_no == 1)
77462306a36Sopenharmony_ci				ha->flt_region_nvram = start;
77562306a36Sopenharmony_ci			break;
77662306a36Sopenharmony_ci		case FLT_REG_NVRAM_2:
77762306a36Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
77862306a36Sopenharmony_ci				break;
77962306a36Sopenharmony_ci			if (ha->port_no == 2)
78062306a36Sopenharmony_ci				ha->flt_region_nvram = start;
78162306a36Sopenharmony_ci			break;
78262306a36Sopenharmony_ci		case FLT_REG_NVRAM_3:
78362306a36Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
78462306a36Sopenharmony_ci				break;
78562306a36Sopenharmony_ci			if (ha->port_no == 3)
78662306a36Sopenharmony_ci				ha->flt_region_nvram = start;
78762306a36Sopenharmony_ci			break;
78862306a36Sopenharmony_ci		case FLT_REG_FDT:
78962306a36Sopenharmony_ci			ha->flt_region_fdt = start;
79062306a36Sopenharmony_ci			break;
79162306a36Sopenharmony_ci		case FLT_REG_NPIV_CONF_0:
79262306a36Sopenharmony_ci			if (ha->port_no == 0)
79362306a36Sopenharmony_ci				ha->flt_region_npiv_conf = start;
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci		case FLT_REG_NPIV_CONF_1:
79662306a36Sopenharmony_ci			if (ha->port_no == 1)
79762306a36Sopenharmony_ci				ha->flt_region_npiv_conf = start;
79862306a36Sopenharmony_ci			break;
79962306a36Sopenharmony_ci		case FLT_REG_GOLD_FW:
80062306a36Sopenharmony_ci			ha->flt_region_gold_fw = start;
80162306a36Sopenharmony_ci			break;
80262306a36Sopenharmony_ci		case FLT_REG_FCP_PRIO_0:
80362306a36Sopenharmony_ci			if (ha->port_no == 0)
80462306a36Sopenharmony_ci				ha->flt_region_fcp_prio = start;
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		case FLT_REG_FCP_PRIO_1:
80762306a36Sopenharmony_ci			if (ha->port_no == 1)
80862306a36Sopenharmony_ci				ha->flt_region_fcp_prio = start;
80962306a36Sopenharmony_ci			break;
81062306a36Sopenharmony_ci		case FLT_REG_BOOT_CODE_82XX:
81162306a36Sopenharmony_ci			ha->flt_region_boot = start;
81262306a36Sopenharmony_ci			break;
81362306a36Sopenharmony_ci		case FLT_REG_BOOT_CODE_8044:
81462306a36Sopenharmony_ci			if (IS_QLA8044(ha))
81562306a36Sopenharmony_ci				ha->flt_region_boot = start;
81662306a36Sopenharmony_ci			break;
81762306a36Sopenharmony_ci		case FLT_REG_FW_82XX:
81862306a36Sopenharmony_ci			ha->flt_region_fw = start;
81962306a36Sopenharmony_ci			break;
82062306a36Sopenharmony_ci		case FLT_REG_CNA_FW:
82162306a36Sopenharmony_ci			if (IS_CNA_CAPABLE(ha))
82262306a36Sopenharmony_ci				ha->flt_region_fw = start;
82362306a36Sopenharmony_ci			break;
82462306a36Sopenharmony_ci		case FLT_REG_GOLD_FW_82XX:
82562306a36Sopenharmony_ci			ha->flt_region_gold_fw = start;
82662306a36Sopenharmony_ci			break;
82762306a36Sopenharmony_ci		case FLT_REG_BOOTLOAD_82XX:
82862306a36Sopenharmony_ci			ha->flt_region_bootload = start;
82962306a36Sopenharmony_ci			break;
83062306a36Sopenharmony_ci		case FLT_REG_VPD_8XXX:
83162306a36Sopenharmony_ci			if (IS_CNA_CAPABLE(ha))
83262306a36Sopenharmony_ci				ha->flt_region_vpd = start;
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci		case FLT_REG_FCOE_NVRAM_0:
83562306a36Sopenharmony_ci			if (!(IS_QLA8031(ha) || IS_QLA8044(ha)))
83662306a36Sopenharmony_ci				break;
83762306a36Sopenharmony_ci			if (ha->port_no == 0)
83862306a36Sopenharmony_ci				ha->flt_region_nvram = start;
83962306a36Sopenharmony_ci			break;
84062306a36Sopenharmony_ci		case FLT_REG_FCOE_NVRAM_1:
84162306a36Sopenharmony_ci			if (!(IS_QLA8031(ha) || IS_QLA8044(ha)))
84262306a36Sopenharmony_ci				break;
84362306a36Sopenharmony_ci			if (ha->port_no == 1)
84462306a36Sopenharmony_ci				ha->flt_region_nvram = start;
84562306a36Sopenharmony_ci			break;
84662306a36Sopenharmony_ci		case FLT_REG_IMG_PRI_27XX:
84762306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
84862306a36Sopenharmony_ci				ha->flt_region_img_status_pri = start;
84962306a36Sopenharmony_ci			break;
85062306a36Sopenharmony_ci		case FLT_REG_IMG_SEC_27XX:
85162306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
85262306a36Sopenharmony_ci				ha->flt_region_img_status_sec = start;
85362306a36Sopenharmony_ci			break;
85462306a36Sopenharmony_ci		case FLT_REG_FW_SEC_27XX:
85562306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
85662306a36Sopenharmony_ci				ha->flt_region_fw_sec = start;
85762306a36Sopenharmony_ci			break;
85862306a36Sopenharmony_ci		case FLT_REG_BOOTLOAD_SEC_27XX:
85962306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
86062306a36Sopenharmony_ci				ha->flt_region_boot_sec = start;
86162306a36Sopenharmony_ci			break;
86262306a36Sopenharmony_ci		case FLT_REG_AUX_IMG_PRI_28XX:
86362306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
86462306a36Sopenharmony_ci				ha->flt_region_aux_img_status_pri = start;
86562306a36Sopenharmony_ci			break;
86662306a36Sopenharmony_ci		case FLT_REG_AUX_IMG_SEC_28XX:
86762306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
86862306a36Sopenharmony_ci				ha->flt_region_aux_img_status_sec = start;
86962306a36Sopenharmony_ci			break;
87062306a36Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_0:
87162306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
87262306a36Sopenharmony_ci				if (ha->port_no == 0)
87362306a36Sopenharmony_ci					ha->flt_region_nvram_sec = start;
87462306a36Sopenharmony_ci			break;
87562306a36Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_1:
87662306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
87762306a36Sopenharmony_ci				if (ha->port_no == 1)
87862306a36Sopenharmony_ci					ha->flt_region_nvram_sec = start;
87962306a36Sopenharmony_ci			break;
88062306a36Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_2:
88162306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
88262306a36Sopenharmony_ci				if (ha->port_no == 2)
88362306a36Sopenharmony_ci					ha->flt_region_nvram_sec = start;
88462306a36Sopenharmony_ci			break;
88562306a36Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_3:
88662306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
88762306a36Sopenharmony_ci				if (ha->port_no == 3)
88862306a36Sopenharmony_ci					ha->flt_region_nvram_sec = start;
88962306a36Sopenharmony_ci			break;
89062306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_0:
89162306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_0:
89262306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
89362306a36Sopenharmony_ci				ha->flt_region_vpd_nvram_sec = start;
89462306a36Sopenharmony_ci				if (ha->port_no == 0)
89562306a36Sopenharmony_ci					ha->flt_region_vpd_sec = start;
89662306a36Sopenharmony_ci			}
89762306a36Sopenharmony_ci			break;
89862306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_1:
89962306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_1:
90062306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
90162306a36Sopenharmony_ci				if (ha->port_no == 1)
90262306a36Sopenharmony_ci					ha->flt_region_vpd_sec = start;
90362306a36Sopenharmony_ci			break;
90462306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_2:
90562306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_2:
90662306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
90762306a36Sopenharmony_ci				if (ha->port_no == 2)
90862306a36Sopenharmony_ci					ha->flt_region_vpd_sec = start;
90962306a36Sopenharmony_ci			break;
91062306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_3:
91162306a36Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_3:
91262306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
91362306a36Sopenharmony_ci				if (ha->port_no == 3)
91462306a36Sopenharmony_ci					ha->flt_region_vpd_sec = start;
91562306a36Sopenharmony_ci			break;
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci	goto done;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cino_flash_data:
92162306a36Sopenharmony_ci	/* Use hardcoded defaults. */
92262306a36Sopenharmony_ci	loc = locations[0];
92362306a36Sopenharmony_ci	ha->flt_region_fw = def_fw[def];
92462306a36Sopenharmony_ci	ha->flt_region_boot = def_boot[def];
92562306a36Sopenharmony_ci	ha->flt_region_vpd_nvram = def_vpd_nvram[def];
92662306a36Sopenharmony_ci	ha->flt_region_vpd = (ha->port_no == 0) ?
92762306a36Sopenharmony_ci	    def_vpd0[def] : def_vpd1[def];
92862306a36Sopenharmony_ci	ha->flt_region_nvram = (ha->port_no == 0) ?
92962306a36Sopenharmony_ci	    def_nvram0[def] : def_nvram1[def];
93062306a36Sopenharmony_ci	ha->flt_region_fdt = def_fdt[def];
93162306a36Sopenharmony_ci	ha->flt_region_npiv_conf = (ha->port_no == 0) ?
93262306a36Sopenharmony_ci	    def_npiv_conf0[def] : def_npiv_conf1[def];
93362306a36Sopenharmony_cidone:
93462306a36Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004a,
93562306a36Sopenharmony_ci	    "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x "
93662306a36Sopenharmony_ci	    "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
93762306a36Sopenharmony_ci	    loc, ha->flt_region_boot, ha->flt_region_fw,
93862306a36Sopenharmony_ci	    ha->flt_region_vpd_nvram, ha->flt_region_vpd, ha->flt_region_nvram,
93962306a36Sopenharmony_ci	    ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf,
94062306a36Sopenharmony_ci	    ha->flt_region_fcp_prio);
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic void
94462306a36Sopenharmony_ciqla2xxx_get_fdt_info(scsi_qla_host_t *vha)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci#define FLASH_BLK_SIZE_4K	0x1000
94762306a36Sopenharmony_ci#define FLASH_BLK_SIZE_32K	0x8000
94862306a36Sopenharmony_ci#define FLASH_BLK_SIZE_64K	0x10000
94962306a36Sopenharmony_ci	const char *loc, *locations[] = { "MID", "FDT" };
95062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
95162306a36Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
95262306a36Sopenharmony_ci	uint16_t cnt, chksum;
95362306a36Sopenharmony_ci	__le16 *wptr = (__force __le16 *)req->ring;
95462306a36Sopenharmony_ci	struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring;
95562306a36Sopenharmony_ci	uint8_t	man_id, flash_id;
95662306a36Sopenharmony_ci	uint16_t mid = 0, fid = 0;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, fdt, ha->flt_region_fdt << 2,
95962306a36Sopenharmony_ci	    OPTROM_BURST_DWORDS);
96062306a36Sopenharmony_ci	if (le16_to_cpu(*wptr) == 0xffff)
96162306a36Sopenharmony_ci		goto no_flash_data;
96262306a36Sopenharmony_ci	if (memcmp(fdt->sig, "QLID", 4))
96362306a36Sopenharmony_ci		goto no_flash_data;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++)
96662306a36Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
96762306a36Sopenharmony_ci	if (chksum) {
96862306a36Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x004c,
96962306a36Sopenharmony_ci		    "Inconsistent FDT detected:"
97062306a36Sopenharmony_ci		    " checksum=0x%x id=%c version0x%x.\n", chksum,
97162306a36Sopenharmony_ci		    fdt->sig[0], le16_to_cpu(fdt->version));
97262306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
97362306a36Sopenharmony_ci		    fdt, sizeof(*fdt));
97462306a36Sopenharmony_ci		goto no_flash_data;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	loc = locations[1];
97862306a36Sopenharmony_ci	mid = le16_to_cpu(fdt->man_id);
97962306a36Sopenharmony_ci	fid = le16_to_cpu(fdt->id);
98062306a36Sopenharmony_ci	ha->fdt_wrt_disable = fdt->wrt_disable_bits;
98162306a36Sopenharmony_ci	ha->fdt_wrt_enable = fdt->wrt_enable_bits;
98262306a36Sopenharmony_ci	ha->fdt_wrt_sts_reg_cmd = fdt->wrt_sts_reg_cmd;
98362306a36Sopenharmony_ci	if (IS_QLA8044(ha))
98462306a36Sopenharmony_ci		ha->fdt_erase_cmd = fdt->erase_cmd;
98562306a36Sopenharmony_ci	else
98662306a36Sopenharmony_ci		ha->fdt_erase_cmd =
98762306a36Sopenharmony_ci		    flash_conf_addr(ha, 0x0300 | fdt->erase_cmd);
98862306a36Sopenharmony_ci	ha->fdt_block_size = le32_to_cpu(fdt->block_size);
98962306a36Sopenharmony_ci	if (fdt->unprotect_sec_cmd) {
99062306a36Sopenharmony_ci		ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0300 |
99162306a36Sopenharmony_ci		    fdt->unprotect_sec_cmd);
99262306a36Sopenharmony_ci		ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ?
99362306a36Sopenharmony_ci		    flash_conf_addr(ha, 0x0300 | fdt->protect_sec_cmd) :
99462306a36Sopenharmony_ci		    flash_conf_addr(ha, 0x0336);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	goto done;
99762306a36Sopenharmony_cino_flash_data:
99862306a36Sopenharmony_ci	loc = locations[0];
99962306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha)) {
100062306a36Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
100162306a36Sopenharmony_ci		goto done;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci	qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id);
100462306a36Sopenharmony_ci	mid = man_id;
100562306a36Sopenharmony_ci	fid = flash_id;
100662306a36Sopenharmony_ci	ha->fdt_wrt_disable = 0x9c;
100762306a36Sopenharmony_ci	ha->fdt_erase_cmd = flash_conf_addr(ha, 0x03d8);
100862306a36Sopenharmony_ci	switch (man_id) {
100962306a36Sopenharmony_ci	case 0xbf: /* STT flash. */
101062306a36Sopenharmony_ci		if (flash_id == 0x8e)
101162306a36Sopenharmony_ci			ha->fdt_block_size = FLASH_BLK_SIZE_64K;
101262306a36Sopenharmony_ci		else
101362306a36Sopenharmony_ci			ha->fdt_block_size = FLASH_BLK_SIZE_32K;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		if (flash_id == 0x80)
101662306a36Sopenharmony_ci			ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0352);
101762306a36Sopenharmony_ci		break;
101862306a36Sopenharmony_ci	case 0x13: /* ST M25P80. */
101962306a36Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
102062306a36Sopenharmony_ci		break;
102162306a36Sopenharmony_ci	case 0x1f: /* Atmel 26DF081A. */
102262306a36Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_4K;
102362306a36Sopenharmony_ci		ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0320);
102462306a36Sopenharmony_ci		ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0339);
102562306a36Sopenharmony_ci		ha->fdt_protect_sec_cmd = flash_conf_addr(ha, 0x0336);
102662306a36Sopenharmony_ci		break;
102762306a36Sopenharmony_ci	default:
102862306a36Sopenharmony_ci		/* Default to 64 kb sector size. */
102962306a36Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_cidone:
103362306a36Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004d,
103462306a36Sopenharmony_ci	    "FDT[%s]: (0x%x/0x%x) erase=0x%x "
103562306a36Sopenharmony_ci	    "pr=%x wrtd=0x%x blk=0x%x.\n",
103662306a36Sopenharmony_ci	    loc, mid, fid,
103762306a36Sopenharmony_ci	    ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd,
103862306a36Sopenharmony_ci	    ha->fdt_wrt_disable, ha->fdt_block_size);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void
104362306a36Sopenharmony_ciqla2xxx_get_idc_param(scsi_qla_host_t *vha)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci#define QLA82XX_IDC_PARAM_ADDR       0x003e885c
104662306a36Sopenharmony_ci	__le32 *wptr;
104762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
104862306a36Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (!(IS_P3P_TYPE(ha)))
105162306a36Sopenharmony_ci		return;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	wptr = (__force __le32 *)req->ring;
105462306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, req->ring, QLA82XX_IDC_PARAM_ADDR, 8);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (*wptr == cpu_to_le32(0xffffffff)) {
105762306a36Sopenharmony_ci		ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT;
105862306a36Sopenharmony_ci		ha->fcoe_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT;
105962306a36Sopenharmony_ci	} else {
106062306a36Sopenharmony_ci		ha->fcoe_dev_init_timeout = le32_to_cpu(*wptr);
106162306a36Sopenharmony_ci		wptr++;
106262306a36Sopenharmony_ci		ha->fcoe_reset_timeout = le32_to_cpu(*wptr);
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004e,
106562306a36Sopenharmony_ci	    "fcoe_dev_init_timeout=%d "
106662306a36Sopenharmony_ci	    "fcoe_reset_timeout=%d.\n", ha->fcoe_dev_init_timeout,
106762306a36Sopenharmony_ci	    ha->fcoe_reset_timeout);
106862306a36Sopenharmony_ci	return;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciint
107262306a36Sopenharmony_ciqla2xxx_get_flash_info(scsi_qla_host_t *vha)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	int ret;
107562306a36Sopenharmony_ci	uint32_t flt_addr;
107662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
107962306a36Sopenharmony_ci	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
108062306a36Sopenharmony_ci	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
108162306a36Sopenharmony_ci		return QLA_SUCCESS;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	ret = qla2xxx_find_flt_start(vha, &flt_addr);
108462306a36Sopenharmony_ci	if (ret != QLA_SUCCESS)
108562306a36Sopenharmony_ci		return ret;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	qla2xxx_get_flt_info(vha, flt_addr);
108862306a36Sopenharmony_ci	qla2xxx_get_fdt_info(vha);
108962306a36Sopenharmony_ci	qla2xxx_get_idc_param(vha);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	return QLA_SUCCESS;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_civoid
109562306a36Sopenharmony_ciqla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci#define NPIV_CONFIG_SIZE	(16*1024)
109862306a36Sopenharmony_ci	void *data;
109962306a36Sopenharmony_ci	__le16 *wptr;
110062306a36Sopenharmony_ci	uint16_t cnt, chksum;
110162306a36Sopenharmony_ci	int i;
110262306a36Sopenharmony_ci	struct qla_npiv_header hdr;
110362306a36Sopenharmony_ci	struct qla_npiv_entry *entry;
110462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
110762306a36Sopenharmony_ci	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
110862306a36Sopenharmony_ci		return;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (ha->flags.nic_core_reset_hdlr_active)
111162306a36Sopenharmony_ci		return;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (IS_QLA8044(ha))
111462306a36Sopenharmony_ci		return;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, &hdr, ha->flt_region_npiv_conf << 2,
111762306a36Sopenharmony_ci	    sizeof(struct qla_npiv_header));
111862306a36Sopenharmony_ci	if (hdr.version == cpu_to_le16(0xffff))
111962306a36Sopenharmony_ci		return;
112062306a36Sopenharmony_ci	if (hdr.version != cpu_to_le16(1)) {
112162306a36Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7090,
112262306a36Sopenharmony_ci		    "Unsupported NPIV-Config "
112362306a36Sopenharmony_ci		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
112462306a36Sopenharmony_ci		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
112562306a36Sopenharmony_ci		    le16_to_cpu(hdr.checksum));
112662306a36Sopenharmony_ci		return;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL);
113062306a36Sopenharmony_ci	if (!data) {
113162306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7091,
113262306a36Sopenharmony_ci		    "Unable to allocate memory for data.\n");
113362306a36Sopenharmony_ci		return;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, data, ha->flt_region_npiv_conf << 2,
113762306a36Sopenharmony_ci	    NPIV_CONFIG_SIZE);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	cnt = (sizeof(hdr) + le16_to_cpu(hdr.entries) * sizeof(*entry)) >> 1;
114062306a36Sopenharmony_ci	for (wptr = data, chksum = 0; cnt--; wptr++)
114162306a36Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
114262306a36Sopenharmony_ci	if (chksum) {
114362306a36Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7092,
114462306a36Sopenharmony_ci		    "Inconsistent NPIV-Config "
114562306a36Sopenharmony_ci		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
114662306a36Sopenharmony_ci		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
114762306a36Sopenharmony_ci		    le16_to_cpu(hdr.checksum));
114862306a36Sopenharmony_ci		goto done;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	entry = data + sizeof(struct qla_npiv_header);
115262306a36Sopenharmony_ci	cnt = le16_to_cpu(hdr.entries);
115362306a36Sopenharmony_ci	for (i = 0; cnt; cnt--, entry++, i++) {
115462306a36Sopenharmony_ci		uint16_t flags;
115562306a36Sopenharmony_ci		struct fc_vport_identifiers vid;
115662306a36Sopenharmony_ci		struct fc_vport *vport;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci		flags = le16_to_cpu(entry->flags);
116162306a36Sopenharmony_ci		if (flags == 0xffff)
116262306a36Sopenharmony_ci			continue;
116362306a36Sopenharmony_ci		if ((flags & BIT_0) == 0)
116462306a36Sopenharmony_ci			continue;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		memset(&vid, 0, sizeof(vid));
116762306a36Sopenharmony_ci		vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
116862306a36Sopenharmony_ci		vid.vport_type = FC_PORTTYPE_NPIV;
116962306a36Sopenharmony_ci		vid.disable = false;
117062306a36Sopenharmony_ci		vid.port_name = wwn_to_u64(entry->port_name);
117162306a36Sopenharmony_ci		vid.node_name = wwn_to_u64(entry->node_name);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7093,
117462306a36Sopenharmony_ci		    "NPIV[%02x]: wwpn=%llx wwnn=%llx vf_id=%#x Q_qos=%#x F_qos=%#x.\n",
117562306a36Sopenharmony_ci		    cnt, vid.port_name, vid.node_name,
117662306a36Sopenharmony_ci		    le16_to_cpu(entry->vf_id),
117762306a36Sopenharmony_ci		    entry->q_qos, entry->f_qos);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		if (i < QLA_PRECONFIG_VPORTS) {
118062306a36Sopenharmony_ci			vport = fc_vport_create(vha->host, 0, &vid);
118162306a36Sopenharmony_ci			if (!vport)
118262306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0x7094,
118362306a36Sopenharmony_ci				    "NPIV-Config Failed to create vport [%02x]: wwpn=%llx wwnn=%llx.\n",
118462306a36Sopenharmony_ci				    cnt, vid.port_name, vid.node_name);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_cidone:
118862306a36Sopenharmony_ci	kfree(data);
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic int
119262306a36Sopenharmony_ciqla24xx_unprotect_flash(scsi_qla_host_t *vha)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
119562306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (ha->flags.fac_supported)
119862306a36Sopenharmony_ci		return qla81xx_fac_do_write_enable(vha, 1);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* Enable flash write. */
120162306a36Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
120262306a36Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
120362306a36Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (!ha->fdt_wrt_disable)
120662306a36Sopenharmony_ci		goto done;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	/* Disable flash write-protection, first clear SR protection bit */
120962306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
121062306a36Sopenharmony_ci	/* Then write zero again to clear remaining SR bits.*/
121162306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
121262306a36Sopenharmony_cidone:
121362306a36Sopenharmony_ci	return QLA_SUCCESS;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic int
121762306a36Sopenharmony_ciqla24xx_protect_flash(scsi_qla_host_t *vha)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
122062306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
122162306a36Sopenharmony_ci	ulong cnt = 300;
122262306a36Sopenharmony_ci	uint32_t faddr, dword;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (ha->flags.fac_supported)
122562306a36Sopenharmony_ci		return qla81xx_fac_do_write_enable(vha, 0);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (!ha->fdt_wrt_disable)
122862306a36Sopenharmony_ci		goto skip_wrt_protect;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* Enable flash write-protection and wait for completion. */
123162306a36Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x101);
123262306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, faddr, ha->fdt_wrt_disable);
123362306a36Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x5);
123462306a36Sopenharmony_ci	while (cnt--) {
123562306a36Sopenharmony_ci		if (!qla24xx_read_flash_dword(ha, faddr, &dword)) {
123662306a36Sopenharmony_ci			if (!(dword & BIT_0))
123762306a36Sopenharmony_ci				break;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		udelay(10);
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ciskip_wrt_protect:
124362306a36Sopenharmony_ci	/* Disable flash write. */
124462306a36Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
124562306a36Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return QLA_SUCCESS;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic int
125162306a36Sopenharmony_ciqla24xx_erase_sector(scsi_qla_host_t *vha, uint32_t fdata)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
125462306a36Sopenharmony_ci	uint32_t start, finish;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (ha->flags.fac_supported) {
125762306a36Sopenharmony_ci		start = fdata >> 2;
125862306a36Sopenharmony_ci		finish = start + (ha->fdt_block_size >> 2) - 1;
125962306a36Sopenharmony_ci		return qla81xx_fac_erase_sector(vha, flash_data_addr(ha,
126062306a36Sopenharmony_ci		    start), flash_data_addr(ha, finish));
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	return qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd,
126462306a36Sopenharmony_ci	    (fdata & 0xff00) | ((fdata << 16) & 0xff0000) |
126562306a36Sopenharmony_ci	    ((fdata >> 16) & 0xff));
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic int
126962306a36Sopenharmony_ciqla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr,
127062306a36Sopenharmony_ci    uint32_t dwords)
127162306a36Sopenharmony_ci{
127262306a36Sopenharmony_ci	int ret;
127362306a36Sopenharmony_ci	ulong liter;
127462306a36Sopenharmony_ci	ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
127562306a36Sopenharmony_ci	uint32_t sec_mask, rest_addr, fdata;
127662306a36Sopenharmony_ci	dma_addr_t optrom_dma;
127762306a36Sopenharmony_ci	void *optrom = NULL;
127862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
128162306a36Sopenharmony_ci	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
128262306a36Sopenharmony_ci		goto next;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/* Allocate dma buffer for burst write */
128562306a36Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
128662306a36Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
128762306a36Sopenharmony_ci	if (!optrom) {
128862306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7095,
128962306a36Sopenharmony_ci		    "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cinext:
129362306a36Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
129462306a36Sopenharmony_ci	    "Unprotect flash...\n");
129562306a36Sopenharmony_ci	ret = qla24xx_unprotect_flash(vha);
129662306a36Sopenharmony_ci	if (ret) {
129762306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7096,
129862306a36Sopenharmony_ci		    "Failed to unprotect flash.\n");
129962306a36Sopenharmony_ci		goto done;
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	rest_addr = (ha->fdt_block_size >> 2) - 1;
130362306a36Sopenharmony_ci	sec_mask = ~rest_addr;
130462306a36Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
130562306a36Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		/* Are we at the beginning of a sector? */
130862306a36Sopenharmony_ci		if (!(faddr & rest_addr)) {
130962306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
131062306a36Sopenharmony_ci			    "Erase sector %#x...\n", faddr);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci			ret = qla24xx_erase_sector(vha, fdata);
131362306a36Sopenharmony_ci			if (ret) {
131462306a36Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x7007,
131562306a36Sopenharmony_ci				    "Failed to erase sector %x.\n", faddr);
131662306a36Sopenharmony_ci				break;
131762306a36Sopenharmony_ci			}
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci		if (optrom) {
132162306a36Sopenharmony_ci			/* If smaller than a burst remaining */
132262306a36Sopenharmony_ci			if (dwords - liter < dburst)
132362306a36Sopenharmony_ci				dburst = dwords - liter;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci			/* Copy to dma buffer */
132662306a36Sopenharmony_ci			memcpy(optrom, dwptr, dburst << 2);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci			/* Burst write */
132962306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
133062306a36Sopenharmony_ci			    "Write burst (%#lx dwords)...\n", dburst);
133162306a36Sopenharmony_ci			ret = qla2x00_load_ram(vha, optrom_dma,
133262306a36Sopenharmony_ci			    flash_data_addr(ha, faddr), dburst);
133362306a36Sopenharmony_ci			if (!ret) {
133462306a36Sopenharmony_ci				liter += dburst - 1;
133562306a36Sopenharmony_ci				faddr += dburst - 1;
133662306a36Sopenharmony_ci				dwptr += dburst - 1;
133762306a36Sopenharmony_ci				continue;
133862306a36Sopenharmony_ci			}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7097,
134162306a36Sopenharmony_ci			    "Failed burst-write at %x (%p/%#llx)....\n",
134262306a36Sopenharmony_ci			    flash_data_addr(ha, faddr), optrom,
134362306a36Sopenharmony_ci			    (u64)optrom_dma);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci			dma_free_coherent(&ha->pdev->dev,
134662306a36Sopenharmony_ci			    OPTROM_BURST_SIZE, optrom, optrom_dma);
134762306a36Sopenharmony_ci			optrom = NULL;
134862306a36Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
134962306a36Sopenharmony_ci				break;
135062306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7098,
135162306a36Sopenharmony_ci			    "Reverting to slow write...\n");
135262306a36Sopenharmony_ci		}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		/* Slow write */
135562306a36Sopenharmony_ci		ret = qla24xx_write_flash_dword(ha,
135662306a36Sopenharmony_ci		    flash_data_addr(ha, faddr), le32_to_cpu(*dwptr));
135762306a36Sopenharmony_ci		if (ret) {
135862306a36Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x7006,
135962306a36Sopenharmony_ci			    "Failed slow write %x (%x)\n", faddr, *dwptr);
136062306a36Sopenharmony_ci			break;
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci	}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
136562306a36Sopenharmony_ci	    "Protect flash...\n");
136662306a36Sopenharmony_ci	ret = qla24xx_protect_flash(vha);
136762306a36Sopenharmony_ci	if (ret)
136862306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7099,
136962306a36Sopenharmony_ci		    "Failed to protect flash\n");
137062306a36Sopenharmony_cidone:
137162306a36Sopenharmony_ci	if (optrom)
137262306a36Sopenharmony_ci		dma_free_coherent(&ha->pdev->dev,
137362306a36Sopenharmony_ci		    OPTROM_BURST_SIZE, optrom, optrom_dma);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	return ret;
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ciuint8_t *
137962306a36Sopenharmony_ciqla2x00_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
138062306a36Sopenharmony_ci    uint32_t bytes)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	uint32_t i;
138362306a36Sopenharmony_ci	__le16 *wptr;
138462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	/* Word reads to NVRAM via registers. */
138762306a36Sopenharmony_ci	wptr = buf;
138862306a36Sopenharmony_ci	qla2x00_lock_nvram_access(ha);
138962306a36Sopenharmony_ci	for (i = 0; i < bytes >> 1; i++, naddr++)
139062306a36Sopenharmony_ci		wptr[i] = cpu_to_le16(qla2x00_get_nvram_word(ha,
139162306a36Sopenharmony_ci		    naddr));
139262306a36Sopenharmony_ci	qla2x00_unlock_nvram_access(ha);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	return buf;
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ciuint8_t *
139862306a36Sopenharmony_ciqla24xx_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
139962306a36Sopenharmony_ci    uint32_t bytes)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
140262306a36Sopenharmony_ci	uint32_t *dwptr = buf;
140362306a36Sopenharmony_ci	uint32_t i;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
140662306a36Sopenharmony_ci		return  buf;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	/* Dword reads to flash. */
140962306a36Sopenharmony_ci	naddr = nvram_data_addr(ha, naddr);
141062306a36Sopenharmony_ci	bytes >>= 2;
141162306a36Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
141262306a36Sopenharmony_ci		if (qla24xx_read_flash_dword(ha, naddr, dwptr))
141362306a36Sopenharmony_ci			break;
141462306a36Sopenharmony_ci		cpu_to_le32s(dwptr);
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	return buf;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ciint
142162306a36Sopenharmony_ciqla2x00_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
142262306a36Sopenharmony_ci    uint32_t bytes)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	int ret, stat;
142562306a36Sopenharmony_ci	uint32_t i;
142662306a36Sopenharmony_ci	uint16_t *wptr;
142762306a36Sopenharmony_ci	unsigned long flags;
142862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	ret = QLA_SUCCESS;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
143362306a36Sopenharmony_ci	qla2x00_lock_nvram_access(ha);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	/* Disable NVRAM write-protection. */
143662306a36Sopenharmony_ci	stat = qla2x00_clear_nvram_protection(ha);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	wptr = (uint16_t *)buf;
143962306a36Sopenharmony_ci	for (i = 0; i < bytes >> 1; i++, naddr++) {
144062306a36Sopenharmony_ci		qla2x00_write_nvram_word(ha, naddr,
144162306a36Sopenharmony_ci		    cpu_to_le16(*wptr));
144262306a36Sopenharmony_ci		wptr++;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* Enable NVRAM write-protection. */
144662306a36Sopenharmony_ci	qla2x00_set_nvram_protection(ha, stat);
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	qla2x00_unlock_nvram_access(ha);
144962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	return ret;
145262306a36Sopenharmony_ci}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ciint
145562306a36Sopenharmony_ciqla24xx_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
145662306a36Sopenharmony_ci    uint32_t bytes)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
145962306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
146062306a36Sopenharmony_ci	__le32 *dwptr = buf;
146162306a36Sopenharmony_ci	uint32_t i;
146262306a36Sopenharmony_ci	int ret;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	ret = QLA_SUCCESS;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
146762306a36Sopenharmony_ci		return ret;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* Enable flash write. */
147062306a36Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
147162306a36Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
147262306a36Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	/* Disable NVRAM write-protection. */
147562306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
147662306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* Dword writes to flash. */
147962306a36Sopenharmony_ci	naddr = nvram_data_addr(ha, naddr);
148062306a36Sopenharmony_ci	bytes >>= 2;
148162306a36Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
148262306a36Sopenharmony_ci		if (qla24xx_write_flash_dword(ha, naddr, le32_to_cpu(*dwptr))) {
148362306a36Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x709a,
148462306a36Sopenharmony_ci			    "Unable to program nvram address=%x data=%x.\n",
148562306a36Sopenharmony_ci			    naddr, *dwptr);
148662306a36Sopenharmony_ci			break;
148762306a36Sopenharmony_ci		}
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	/* Enable NVRAM write-protection. */
149162306a36Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0x8c);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	/* Disable flash write. */
149462306a36Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
149562306a36Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
149662306a36Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	return ret;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ciuint8_t *
150262306a36Sopenharmony_ciqla25xx_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
150362306a36Sopenharmony_ci    uint32_t bytes)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
150662306a36Sopenharmony_ci	uint32_t *dwptr = buf;
150762306a36Sopenharmony_ci	uint32_t i;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	/* Dword reads to flash. */
151062306a36Sopenharmony_ci	naddr = flash_data_addr(ha, ha->flt_region_vpd_nvram | naddr);
151162306a36Sopenharmony_ci	bytes >>= 2;
151262306a36Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
151362306a36Sopenharmony_ci		if (qla24xx_read_flash_dword(ha, naddr, dwptr))
151462306a36Sopenharmony_ci			break;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci		cpu_to_le32s(dwptr);
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	return buf;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci#define RMW_BUFFER_SIZE	(64 * 1024)
152362306a36Sopenharmony_ciint
152462306a36Sopenharmony_ciqla25xx_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
152562306a36Sopenharmony_ci    uint32_t bytes)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
152862306a36Sopenharmony_ci	uint8_t *dbuf = vmalloc(RMW_BUFFER_SIZE);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (!dbuf)
153162306a36Sopenharmony_ci		return QLA_MEMORY_ALLOC_FAILED;
153262306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
153362306a36Sopenharmony_ci	    RMW_BUFFER_SIZE);
153462306a36Sopenharmony_ci	memcpy(dbuf + (naddr << 2), buf, bytes);
153562306a36Sopenharmony_ci	ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
153662306a36Sopenharmony_ci	    RMW_BUFFER_SIZE);
153762306a36Sopenharmony_ci	vfree(dbuf);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	return QLA_SUCCESS;
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_cistatic inline void
154362306a36Sopenharmony_ciqla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	if (IS_QLA2322(ha)) {
154662306a36Sopenharmony_ci		/* Flip all colors. */
154762306a36Sopenharmony_ci		if (ha->beacon_color_state == QLA_LED_ALL_ON) {
154862306a36Sopenharmony_ci			/* Turn off. */
154962306a36Sopenharmony_ci			ha->beacon_color_state = 0;
155062306a36Sopenharmony_ci			*pflags = GPIO_LED_ALL_OFF;
155162306a36Sopenharmony_ci		} else {
155262306a36Sopenharmony_ci			/* Turn on. */
155362306a36Sopenharmony_ci			ha->beacon_color_state = QLA_LED_ALL_ON;
155462306a36Sopenharmony_ci			*pflags = GPIO_LED_RGA_ON;
155562306a36Sopenharmony_ci		}
155662306a36Sopenharmony_ci	} else {
155762306a36Sopenharmony_ci		/* Flip green led only. */
155862306a36Sopenharmony_ci		if (ha->beacon_color_state == QLA_LED_GRN_ON) {
155962306a36Sopenharmony_ci			/* Turn off. */
156062306a36Sopenharmony_ci			ha->beacon_color_state = 0;
156162306a36Sopenharmony_ci			*pflags = GPIO_LED_GREEN_OFF_AMBER_OFF;
156262306a36Sopenharmony_ci		} else {
156362306a36Sopenharmony_ci			/* Turn on. */
156462306a36Sopenharmony_ci			ha->beacon_color_state = QLA_LED_GRN_ON;
156562306a36Sopenharmony_ci			*pflags = GPIO_LED_GREEN_ON_AMBER_OFF;
156662306a36Sopenharmony_ci		}
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci#define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r))
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_civoid
157362306a36Sopenharmony_ciqla2x00_beacon_blink(struct scsi_qla_host *vha)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	uint16_t gpio_enable;
157662306a36Sopenharmony_ci	uint16_t gpio_data;
157762306a36Sopenharmony_ci	uint16_t led_color = 0;
157862306a36Sopenharmony_ci	unsigned long flags;
157962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
158062306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
158362306a36Sopenharmony_ci		return;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/* Save the Original GPIOE. */
158862306a36Sopenharmony_ci	if (ha->pio_address) {
158962306a36Sopenharmony_ci		gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
159062306a36Sopenharmony_ci		gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
159162306a36Sopenharmony_ci	} else {
159262306a36Sopenharmony_ci		gpio_enable = rd_reg_word(&reg->gpioe);
159362306a36Sopenharmony_ci		gpio_data = rd_reg_word(&reg->gpiod);
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	/* Set the modified gpio_enable values */
159762306a36Sopenharmony_ci	gpio_enable |= GPIO_LED_MASK;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (ha->pio_address) {
160062306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
160162306a36Sopenharmony_ci	} else {
160262306a36Sopenharmony_ci		wrt_reg_word(&reg->gpioe, gpio_enable);
160362306a36Sopenharmony_ci		rd_reg_word(&reg->gpioe);
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	qla2x00_flip_colors(ha, &led_color);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	/* Clear out any previously set LED color. */
160962306a36Sopenharmony_ci	gpio_data &= ~GPIO_LED_MASK;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/* Set the new input LED color to GPIOD. */
161262306a36Sopenharmony_ci	gpio_data |= led_color;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* Set the modified gpio_data values */
161562306a36Sopenharmony_ci	if (ha->pio_address) {
161662306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
161762306a36Sopenharmony_ci	} else {
161862306a36Sopenharmony_ci		wrt_reg_word(&reg->gpiod, gpio_data);
161962306a36Sopenharmony_ci		rd_reg_word(&reg->gpiod);
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ciint
162662306a36Sopenharmony_ciqla2x00_beacon_on(struct scsi_qla_host *vha)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	uint16_t gpio_enable;
162962306a36Sopenharmony_ci	uint16_t gpio_data;
163062306a36Sopenharmony_ci	unsigned long flags;
163162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
163262306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
163562306a36Sopenharmony_ci	ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
163862306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x709b,
163962306a36Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
164062306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	/* Turn off LEDs. */
164462306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
164562306a36Sopenharmony_ci	if (ha->pio_address) {
164662306a36Sopenharmony_ci		gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
164762306a36Sopenharmony_ci		gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
164862306a36Sopenharmony_ci	} else {
164962306a36Sopenharmony_ci		gpio_enable = rd_reg_word(&reg->gpioe);
165062306a36Sopenharmony_ci		gpio_data = rd_reg_word(&reg->gpiod);
165162306a36Sopenharmony_ci	}
165262306a36Sopenharmony_ci	gpio_enable |= GPIO_LED_MASK;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	/* Set the modified gpio_enable values. */
165562306a36Sopenharmony_ci	if (ha->pio_address) {
165662306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
165762306a36Sopenharmony_ci	} else {
165862306a36Sopenharmony_ci		wrt_reg_word(&reg->gpioe, gpio_enable);
165962306a36Sopenharmony_ci		rd_reg_word(&reg->gpioe);
166062306a36Sopenharmony_ci	}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	/* Clear out previously set LED colour. */
166362306a36Sopenharmony_ci	gpio_data &= ~GPIO_LED_MASK;
166462306a36Sopenharmony_ci	if (ha->pio_address) {
166562306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
166662306a36Sopenharmony_ci	} else {
166762306a36Sopenharmony_ci		wrt_reg_word(&reg->gpiod, gpio_data);
166862306a36Sopenharmony_ci		rd_reg_word(&reg->gpiod);
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/*
167362306a36Sopenharmony_ci	 * Let the per HBA timer kick off the blinking process based on
167462306a36Sopenharmony_ci	 * the following flags. No need to do anything else now.
167562306a36Sopenharmony_ci	 */
167662306a36Sopenharmony_ci	ha->beacon_blink_led = 1;
167762306a36Sopenharmony_ci	ha->beacon_color_state = 0;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	return QLA_SUCCESS;
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ciint
168362306a36Sopenharmony_ciqla2x00_beacon_off(struct scsi_qla_host *vha)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	int rval = QLA_SUCCESS;
168662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	ha->beacon_blink_led = 0;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* Set the on flag so when it gets flipped it will be off. */
169162306a36Sopenharmony_ci	if (IS_QLA2322(ha))
169262306a36Sopenharmony_ci		ha->beacon_color_state = QLA_LED_ALL_ON;
169362306a36Sopenharmony_ci	else
169462306a36Sopenharmony_ci		ha->beacon_color_state = QLA_LED_GRN_ON;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	ha->isp_ops->beacon_blink(vha);	/* This turns green LED off */
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
169962306a36Sopenharmony_ci	ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	rval = qla2x00_set_fw_options(vha, ha->fw_options);
170262306a36Sopenharmony_ci	if (rval != QLA_SUCCESS)
170362306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x709c,
170462306a36Sopenharmony_ci		    "Unable to update fw options (beacon off).\n");
170562306a36Sopenharmony_ci	return rval;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic inline void
171062306a36Sopenharmony_ciqla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
171162306a36Sopenharmony_ci{
171262306a36Sopenharmony_ci	/* Flip all colors. */
171362306a36Sopenharmony_ci	if (ha->beacon_color_state == QLA_LED_ALL_ON) {
171462306a36Sopenharmony_ci		/* Turn off. */
171562306a36Sopenharmony_ci		ha->beacon_color_state = 0;
171662306a36Sopenharmony_ci		*pflags = 0;
171762306a36Sopenharmony_ci	} else {
171862306a36Sopenharmony_ci		/* Turn on. */
171962306a36Sopenharmony_ci		ha->beacon_color_state = QLA_LED_ALL_ON;
172062306a36Sopenharmony_ci		*pflags = GPDX_LED_YELLOW_ON | GPDX_LED_AMBER_ON;
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_civoid
172562306a36Sopenharmony_ciqla24xx_beacon_blink(struct scsi_qla_host *vha)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	uint16_t led_color = 0;
172862306a36Sopenharmony_ci	uint32_t gpio_data;
172962306a36Sopenharmony_ci	unsigned long flags;
173062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
173162306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	/* Save the Original GPIOD. */
173462306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
173562306a36Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	/* Enable the gpio_data reg for update. */
173862306a36Sopenharmony_ci	gpio_data |= GPDX_LED_UPDATE_MASK;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
174162306a36Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* Set the color bits. */
174462306a36Sopenharmony_ci	qla24xx_flip_colors(ha, &led_color);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	/* Clear out any previously set LED color. */
174762306a36Sopenharmony_ci	gpio_data &= ~GPDX_LED_COLOR_MASK;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	/* Set the new input LED color to GPIOD. */
175062306a36Sopenharmony_ci	gpio_data |= led_color;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	/* Set the modified gpio_data values. */
175362306a36Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
175462306a36Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
175562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic uint32_t
175962306a36Sopenharmony_ciqla83xx_select_led_port(struct qla_hw_data *ha)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	uint32_t led_select_value = 0;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
176462306a36Sopenharmony_ci		goto out;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	if (ha->port_no == 0)
176762306a36Sopenharmony_ci		led_select_value = QLA83XX_LED_PORT0;
176862306a36Sopenharmony_ci	else
176962306a36Sopenharmony_ci		led_select_value = QLA83XX_LED_PORT1;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ciout:
177262306a36Sopenharmony_ci	return led_select_value;
177362306a36Sopenharmony_ci}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_civoid
177662306a36Sopenharmony_ciqla83xx_beacon_blink(struct scsi_qla_host *vha)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	uint32_t led_select_value;
177962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
178062306a36Sopenharmony_ci	uint16_t led_cfg[6];
178162306a36Sopenharmony_ci	uint16_t orig_led_cfg[6];
178262306a36Sopenharmony_ci	uint32_t led_10_value, led_43_value;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha) && !IS_QLA27XX(ha) &&
178562306a36Sopenharmony_ci	    !IS_QLA28XX(ha))
178662306a36Sopenharmony_ci		return;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	if (!ha->beacon_blink_led)
178962306a36Sopenharmony_ci		return;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
179262306a36Sopenharmony_ci		qla2x00_write_ram_word(vha, 0x1003, 0x40000230);
179362306a36Sopenharmony_ci		qla2x00_write_ram_word(vha, 0x1004, 0x40000230);
179462306a36Sopenharmony_ci	} else if (IS_QLA2031(ha)) {
179562306a36Sopenharmony_ci		led_select_value = qla83xx_select_led_port(ha);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x40000230);
179862306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value + 4, 0x40000230);
179962306a36Sopenharmony_ci	} else if (IS_QLA8031(ha)) {
180062306a36Sopenharmony_ci		led_select_value = qla83xx_select_led_port(ha);
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		qla83xx_rd_reg(vha, led_select_value, &led_10_value);
180362306a36Sopenharmony_ci		qla83xx_rd_reg(vha, led_select_value + 0x10, &led_43_value);
180462306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x01f44000);
180562306a36Sopenharmony_ci		msleep(500);
180662306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x400001f4);
180762306a36Sopenharmony_ci		msleep(1000);
180862306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, led_10_value);
180962306a36Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value + 0x10, led_43_value);
181062306a36Sopenharmony_ci	} else if (IS_QLA81XX(ha)) {
181162306a36Sopenharmony_ci		int rval;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		/* Save Current */
181462306a36Sopenharmony_ci		rval = qla81xx_get_led_config(vha, orig_led_cfg);
181562306a36Sopenharmony_ci		/* Do the blink */
181662306a36Sopenharmony_ci		if (rval == QLA_SUCCESS) {
181762306a36Sopenharmony_ci			if (IS_QLA81XX(ha)) {
181862306a36Sopenharmony_ci				led_cfg[0] = 0x4000;
181962306a36Sopenharmony_ci				led_cfg[1] = 0x2000;
182062306a36Sopenharmony_ci				led_cfg[2] = 0;
182162306a36Sopenharmony_ci				led_cfg[3] = 0;
182262306a36Sopenharmony_ci				led_cfg[4] = 0;
182362306a36Sopenharmony_ci				led_cfg[5] = 0;
182462306a36Sopenharmony_ci			} else {
182562306a36Sopenharmony_ci				led_cfg[0] = 0x4000;
182662306a36Sopenharmony_ci				led_cfg[1] = 0x4000;
182762306a36Sopenharmony_ci				led_cfg[2] = 0x4000;
182862306a36Sopenharmony_ci				led_cfg[3] = 0x2000;
182962306a36Sopenharmony_ci				led_cfg[4] = 0;
183062306a36Sopenharmony_ci				led_cfg[5] = 0x2000;
183162306a36Sopenharmony_ci			}
183262306a36Sopenharmony_ci			rval = qla81xx_set_led_config(vha, led_cfg);
183362306a36Sopenharmony_ci			msleep(1000);
183462306a36Sopenharmony_ci			if (IS_QLA81XX(ha)) {
183562306a36Sopenharmony_ci				led_cfg[0] = 0x4000;
183662306a36Sopenharmony_ci				led_cfg[1] = 0x2000;
183762306a36Sopenharmony_ci				led_cfg[2] = 0;
183862306a36Sopenharmony_ci			} else {
183962306a36Sopenharmony_ci				led_cfg[0] = 0x4000;
184062306a36Sopenharmony_ci				led_cfg[1] = 0x2000;
184162306a36Sopenharmony_ci				led_cfg[2] = 0x4000;
184262306a36Sopenharmony_ci				led_cfg[3] = 0x4000;
184362306a36Sopenharmony_ci				led_cfg[4] = 0;
184462306a36Sopenharmony_ci				led_cfg[5] = 0x2000;
184562306a36Sopenharmony_ci			}
184662306a36Sopenharmony_ci			rval = qla81xx_set_led_config(vha, led_cfg);
184762306a36Sopenharmony_ci		}
184862306a36Sopenharmony_ci		/* On exit, restore original (presumes no status change) */
184962306a36Sopenharmony_ci		qla81xx_set_led_config(vha, orig_led_cfg);
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ciint
185462306a36Sopenharmony_ciqla24xx_beacon_on(struct scsi_qla_host *vha)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	uint32_t gpio_data;
185762306a36Sopenharmony_ci	unsigned long flags;
185862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
185962306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
186262306a36Sopenharmony_ci		return QLA_SUCCESS;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (IS_QLA8031(ha) || IS_QLA81XX(ha))
186562306a36Sopenharmony_ci		goto skip_gpio; /* let blink handle it */
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (ha->beacon_blink_led == 0) {
186862306a36Sopenharmony_ci		/* Enable firmware for update */
186962306a36Sopenharmony_ci		ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci		if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS)
187262306a36Sopenharmony_ci			return QLA_FUNCTION_FAILED;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci		if (qla2x00_get_fw_options(vha, ha->fw_options) !=
187562306a36Sopenharmony_ci		    QLA_SUCCESS) {
187662306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7009,
187762306a36Sopenharmony_ci			    "Unable to update fw options (beacon on).\n");
187862306a36Sopenharmony_ci			return QLA_FUNCTION_FAILED;
187962306a36Sopenharmony_ci		}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci		if (IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
188262306a36Sopenharmony_ci			goto skip_gpio;
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
188562306a36Sopenharmony_ci		gpio_data = rd_reg_dword(&reg->gpiod);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci		/* Enable the gpio_data reg for update. */
188862306a36Sopenharmony_ci		gpio_data |= GPDX_LED_UPDATE_MASK;
188962306a36Sopenharmony_ci		wrt_reg_dword(&reg->gpiod, gpio_data);
189062306a36Sopenharmony_ci		rd_reg_dword(&reg->gpiod);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	/* So all colors blink together. */
189662306a36Sopenharmony_ci	ha->beacon_color_state = 0;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ciskip_gpio:
189962306a36Sopenharmony_ci	/* Let the per HBA timer kick off the blinking process. */
190062306a36Sopenharmony_ci	ha->beacon_blink_led = 1;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	return QLA_SUCCESS;
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ciint
190662306a36Sopenharmony_ciqla24xx_beacon_off(struct scsi_qla_host *vha)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	uint32_t gpio_data;
190962306a36Sopenharmony_ci	unsigned long flags;
191062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
191162306a36Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
191462306a36Sopenharmony_ci		return QLA_SUCCESS;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (!ha->flags.fw_started)
191762306a36Sopenharmony_ci		return QLA_SUCCESS;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	ha->beacon_blink_led = 0;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	if (IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
192262306a36Sopenharmony_ci		goto set_fw_options;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	if (IS_QLA8031(ha) || IS_QLA81XX(ha))
192562306a36Sopenharmony_ci		return QLA_SUCCESS;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	ha->beacon_color_state = QLA_LED_ALL_ON;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	ha->isp_ops->beacon_blink(vha);	/* Will flip to all off. */
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	/* Give control back to firmware. */
193262306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
193362306a36Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	/* Disable the gpio_data reg for update. */
193662306a36Sopenharmony_ci	gpio_data &= ~GPDX_LED_UPDATE_MASK;
193762306a36Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
193862306a36Sopenharmony_ci	rd_reg_dword(&reg->gpiod);
193962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ciset_fw_options:
194262306a36Sopenharmony_ci	ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
194562306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x704d,
194662306a36Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
194762306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
195162306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x704e,
195262306a36Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
195362306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
195462306a36Sopenharmony_ci	}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	return QLA_SUCCESS;
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci/*
196162306a36Sopenharmony_ci * Flash support routines
196262306a36Sopenharmony_ci */
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci/**
196562306a36Sopenharmony_ci * qla2x00_flash_enable() - Setup flash for reading and writing.
196662306a36Sopenharmony_ci * @ha: HA context
196762306a36Sopenharmony_ci */
196862306a36Sopenharmony_cistatic void
196962306a36Sopenharmony_ciqla2x00_flash_enable(struct qla_hw_data *ha)
197062306a36Sopenharmony_ci{
197162306a36Sopenharmony_ci	uint16_t data;
197262306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	data = rd_reg_word(&reg->ctrl_status);
197562306a36Sopenharmony_ci	data |= CSR_FLASH_ENABLE;
197662306a36Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, data);
197762306a36Sopenharmony_ci	rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
197862306a36Sopenharmony_ci}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci/**
198162306a36Sopenharmony_ci * qla2x00_flash_disable() - Disable flash and allow RISC to run.
198262306a36Sopenharmony_ci * @ha: HA context
198362306a36Sopenharmony_ci */
198462306a36Sopenharmony_cistatic void
198562306a36Sopenharmony_ciqla2x00_flash_disable(struct qla_hw_data *ha)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	uint16_t data;
198862306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	data = rd_reg_word(&reg->ctrl_status);
199162306a36Sopenharmony_ci	data &= ~(CSR_FLASH_ENABLE);
199262306a36Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, data);
199362306a36Sopenharmony_ci	rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
199462306a36Sopenharmony_ci}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci/**
199762306a36Sopenharmony_ci * qla2x00_read_flash_byte() - Reads a byte from flash
199862306a36Sopenharmony_ci * @ha: HA context
199962306a36Sopenharmony_ci * @addr: Address in flash to read
200062306a36Sopenharmony_ci *
200162306a36Sopenharmony_ci * A word is read from the chip, but, only the lower byte is valid.
200262306a36Sopenharmony_ci *
200362306a36Sopenharmony_ci * Returns the byte read from flash @addr.
200462306a36Sopenharmony_ci */
200562306a36Sopenharmony_cistatic uint8_t
200662306a36Sopenharmony_ciqla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr)
200762306a36Sopenharmony_ci{
200862306a36Sopenharmony_ci	uint16_t data;
200962306a36Sopenharmony_ci	uint16_t bank_select;
201062306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	bank_select = rd_reg_word(&reg->ctrl_status);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
201562306a36Sopenharmony_ci		/* Specify 64K address range: */
201662306a36Sopenharmony_ci		/*  clear out Module Select and Flash Address bits [19:16]. */
201762306a36Sopenharmony_ci		bank_select &= ~0xf8;
201862306a36Sopenharmony_ci		bank_select |= addr >> 12 & 0xf0;
201962306a36Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
202062306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
202162306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
202462306a36Sopenharmony_ci		data = rd_reg_word(&reg->flash_data);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci		return (uint8_t)data;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	/* Setup bit 16 of flash address. */
203062306a36Sopenharmony_ci	if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
203162306a36Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
203262306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
203362306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
203462306a36Sopenharmony_ci	} else if (((addr & BIT_16) == 0) &&
203562306a36Sopenharmony_ci	    (bank_select & CSR_FLASH_64K_BANK)) {
203662306a36Sopenharmony_ci		bank_select &= ~(CSR_FLASH_64K_BANK);
203762306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
203862306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
203962306a36Sopenharmony_ci	}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	/* Always perform IO mapped accesses to the FLASH registers. */
204262306a36Sopenharmony_ci	if (ha->pio_address) {
204362306a36Sopenharmony_ci		uint16_t data2;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
204662306a36Sopenharmony_ci		do {
204762306a36Sopenharmony_ci			data = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
204862306a36Sopenharmony_ci			barrier();
204962306a36Sopenharmony_ci			cpu_relax();
205062306a36Sopenharmony_ci			data2 = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
205162306a36Sopenharmony_ci		} while (data != data2);
205262306a36Sopenharmony_ci	} else {
205362306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
205462306a36Sopenharmony_ci		data = qla2x00_debounce_register(&reg->flash_data);
205562306a36Sopenharmony_ci	}
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	return (uint8_t)data;
205862306a36Sopenharmony_ci}
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci/**
206162306a36Sopenharmony_ci * qla2x00_write_flash_byte() - Write a byte to flash
206262306a36Sopenharmony_ci * @ha: HA context
206362306a36Sopenharmony_ci * @addr: Address in flash to write
206462306a36Sopenharmony_ci * @data: Data to write
206562306a36Sopenharmony_ci */
206662306a36Sopenharmony_cistatic void
206762306a36Sopenharmony_ciqla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data)
206862306a36Sopenharmony_ci{
206962306a36Sopenharmony_ci	uint16_t bank_select;
207062306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	bank_select = rd_reg_word(&reg->ctrl_status);
207362306a36Sopenharmony_ci	if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
207462306a36Sopenharmony_ci		/* Specify 64K address range: */
207562306a36Sopenharmony_ci		/*  clear out Module Select and Flash Address bits [19:16]. */
207662306a36Sopenharmony_ci		bank_select &= ~0xf8;
207762306a36Sopenharmony_ci		bank_select |= addr >> 12 & 0xf0;
207862306a36Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
207962306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
208062306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
208362306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
208462306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_data, (uint16_t)data);
208562306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci		return;
208862306a36Sopenharmony_ci	}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	/* Setup bit 16 of flash address. */
209162306a36Sopenharmony_ci	if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
209262306a36Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
209362306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
209462306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
209562306a36Sopenharmony_ci	} else if (((addr & BIT_16) == 0) &&
209662306a36Sopenharmony_ci	    (bank_select & CSR_FLASH_64K_BANK)) {
209762306a36Sopenharmony_ci		bank_select &= ~(CSR_FLASH_64K_BANK);
209862306a36Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
209962306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
210062306a36Sopenharmony_ci	}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	/* Always perform IO mapped accesses to the FLASH registers. */
210362306a36Sopenharmony_ci	if (ha->pio_address) {
210462306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
210562306a36Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_data), (uint16_t)data);
210662306a36Sopenharmony_ci	} else {
210762306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
210862306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
210962306a36Sopenharmony_ci		wrt_reg_word(&reg->flash_data, (uint16_t)data);
211062306a36Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
211162306a36Sopenharmony_ci	}
211262306a36Sopenharmony_ci}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci/**
211562306a36Sopenharmony_ci * qla2x00_poll_flash() - Polls flash for completion.
211662306a36Sopenharmony_ci * @ha: HA context
211762306a36Sopenharmony_ci * @addr: Address in flash to poll
211862306a36Sopenharmony_ci * @poll_data: Data to be polled
211962306a36Sopenharmony_ci * @man_id: Flash manufacturer ID
212062306a36Sopenharmony_ci * @flash_id: Flash ID
212162306a36Sopenharmony_ci *
212262306a36Sopenharmony_ci * This function polls the device until bit 7 of what is read matches data
212362306a36Sopenharmony_ci * bit 7 or until data bit 5 becomes a 1.  If that hapens, the flash ROM timed
212462306a36Sopenharmony_ci * out (a fatal error).  The flash book recommeds reading bit 7 again after
212562306a36Sopenharmony_ci * reading bit 5 as a 1.
212662306a36Sopenharmony_ci *
212762306a36Sopenharmony_ci * Returns 0 on success, else non-zero.
212862306a36Sopenharmony_ci */
212962306a36Sopenharmony_cistatic int
213062306a36Sopenharmony_ciqla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data,
213162306a36Sopenharmony_ci    uint8_t man_id, uint8_t flash_id)
213262306a36Sopenharmony_ci{
213362306a36Sopenharmony_ci	int status;
213462306a36Sopenharmony_ci	uint8_t flash_data;
213562306a36Sopenharmony_ci	uint32_t cnt;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	status = 1;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	/* Wait for 30 seconds for command to finish. */
214062306a36Sopenharmony_ci	poll_data &= BIT_7;
214162306a36Sopenharmony_ci	for (cnt = 3000000; cnt; cnt--) {
214262306a36Sopenharmony_ci		flash_data = qla2x00_read_flash_byte(ha, addr);
214362306a36Sopenharmony_ci		if ((flash_data & BIT_7) == poll_data) {
214462306a36Sopenharmony_ci			status = 0;
214562306a36Sopenharmony_ci			break;
214662306a36Sopenharmony_ci		}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci		if (man_id != 0x40 && man_id != 0xda) {
214962306a36Sopenharmony_ci			if ((flash_data & BIT_5) && cnt > 2)
215062306a36Sopenharmony_ci				cnt = 2;
215162306a36Sopenharmony_ci		}
215262306a36Sopenharmony_ci		udelay(10);
215362306a36Sopenharmony_ci		barrier();
215462306a36Sopenharmony_ci		cond_resched();
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci	return status;
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci/**
216062306a36Sopenharmony_ci * qla2x00_program_flash_address() - Programs a flash address
216162306a36Sopenharmony_ci * @ha: HA context
216262306a36Sopenharmony_ci * @addr: Address in flash to program
216362306a36Sopenharmony_ci * @data: Data to be written in flash
216462306a36Sopenharmony_ci * @man_id: Flash manufacturer ID
216562306a36Sopenharmony_ci * @flash_id: Flash ID
216662306a36Sopenharmony_ci *
216762306a36Sopenharmony_ci * Returns 0 on success, else non-zero.
216862306a36Sopenharmony_ci */
216962306a36Sopenharmony_cistatic int
217062306a36Sopenharmony_ciqla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr,
217162306a36Sopenharmony_ci    uint8_t data, uint8_t man_id, uint8_t flash_id)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	/* Write Program Command Sequence. */
217462306a36Sopenharmony_ci	if (IS_OEM_001(ha)) {
217562306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
217662306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
217762306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xa0);
217862306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr, data);
217962306a36Sopenharmony_ci	} else {
218062306a36Sopenharmony_ci		if (man_id == 0xda && flash_id == 0xc1) {
218162306a36Sopenharmony_ci			qla2x00_write_flash_byte(ha, addr, data);
218262306a36Sopenharmony_ci			if (addr & 0x7e)
218362306a36Sopenharmony_ci				return 0;
218462306a36Sopenharmony_ci		} else {
218562306a36Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
218662306a36Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
218762306a36Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x5555, 0xa0);
218862306a36Sopenharmony_ci			qla2x00_write_flash_byte(ha, addr, data);
218962306a36Sopenharmony_ci		}
219062306a36Sopenharmony_ci	}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	udelay(150);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	/* Wait for write to complete. */
219562306a36Sopenharmony_ci	return qla2x00_poll_flash(ha, addr, data, man_id, flash_id);
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci/**
219962306a36Sopenharmony_ci * qla2x00_erase_flash() - Erase the flash.
220062306a36Sopenharmony_ci * @ha: HA context
220162306a36Sopenharmony_ci * @man_id: Flash manufacturer ID
220262306a36Sopenharmony_ci * @flash_id: Flash ID
220362306a36Sopenharmony_ci *
220462306a36Sopenharmony_ci * Returns 0 on success, else non-zero.
220562306a36Sopenharmony_ci */
220662306a36Sopenharmony_cistatic int
220762306a36Sopenharmony_ciqla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id)
220862306a36Sopenharmony_ci{
220962306a36Sopenharmony_ci	/* Individual Sector Erase Command Sequence */
221062306a36Sopenharmony_ci	if (IS_OEM_001(ha)) {
221162306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
221262306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
221362306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0x80);
221462306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
221562306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
221662306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0x10);
221762306a36Sopenharmony_ci	} else {
221862306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
221962306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
222062306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0x80);
222162306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
222262306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
222362306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0x10);
222462306a36Sopenharmony_ci	}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	udelay(150);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	/* Wait for erase to complete. */
222962306a36Sopenharmony_ci	return qla2x00_poll_flash(ha, 0x00, 0x80, man_id, flash_id);
223062306a36Sopenharmony_ci}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci/**
223362306a36Sopenharmony_ci * qla2x00_erase_flash_sector() - Erase a flash sector.
223462306a36Sopenharmony_ci * @ha: HA context
223562306a36Sopenharmony_ci * @addr: Flash sector to erase
223662306a36Sopenharmony_ci * @sec_mask: Sector address mask
223762306a36Sopenharmony_ci * @man_id: Flash manufacturer ID
223862306a36Sopenharmony_ci * @flash_id: Flash ID
223962306a36Sopenharmony_ci *
224062306a36Sopenharmony_ci * Returns 0 on success, else non-zero.
224162306a36Sopenharmony_ci */
224262306a36Sopenharmony_cistatic int
224362306a36Sopenharmony_ciqla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
224462306a36Sopenharmony_ci    uint32_t sec_mask, uint8_t man_id, uint8_t flash_id)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	/* Individual Sector Erase Command Sequence */
224762306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
224862306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
224962306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0x80);
225062306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
225162306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
225262306a36Sopenharmony_ci	if (man_id == 0x1f && flash_id == 0x13)
225362306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x10);
225462306a36Sopenharmony_ci	else
225562306a36Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x30);
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	udelay(150);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/* Wait for erase to complete. */
226062306a36Sopenharmony_ci	return qla2x00_poll_flash(ha, addr, 0x80, man_id, flash_id);
226162306a36Sopenharmony_ci}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci/**
226462306a36Sopenharmony_ci * qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip.
226562306a36Sopenharmony_ci * @ha: host adapter
226662306a36Sopenharmony_ci * @man_id: Flash manufacturer ID
226762306a36Sopenharmony_ci * @flash_id: Flash ID
226862306a36Sopenharmony_ci */
226962306a36Sopenharmony_cistatic void
227062306a36Sopenharmony_ciqla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
227162306a36Sopenharmony_ci    uint8_t *flash_id)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
227462306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
227562306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0x90);
227662306a36Sopenharmony_ci	*man_id = qla2x00_read_flash_byte(ha, 0x0000);
227762306a36Sopenharmony_ci	*flash_id = qla2x00_read_flash_byte(ha, 0x0001);
227862306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
227962306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
228062306a36Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xf0);
228162306a36Sopenharmony_ci}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_cistatic void
228462306a36Sopenharmony_ciqla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf,
228562306a36Sopenharmony_ci	uint32_t saddr, uint32_t length)
228662306a36Sopenharmony_ci{
228762306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
228862306a36Sopenharmony_ci	uint32_t midpoint, ilength;
228962306a36Sopenharmony_ci	uint8_t data;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	midpoint = length / 2;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, 0);
229462306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);
229562306a36Sopenharmony_ci	for (ilength = 0; ilength < length; saddr++, ilength++, tmp_buf++) {
229662306a36Sopenharmony_ci		if (ilength == midpoint) {
229762306a36Sopenharmony_ci			wrt_reg_word(&reg->nvram, NVR_SELECT);
229862306a36Sopenharmony_ci			rd_reg_word(&reg->nvram);
229962306a36Sopenharmony_ci		}
230062306a36Sopenharmony_ci		data = qla2x00_read_flash_byte(ha, saddr);
230162306a36Sopenharmony_ci		if (saddr % 100)
230262306a36Sopenharmony_ci			udelay(10);
230362306a36Sopenharmony_ci		*tmp_buf = data;
230462306a36Sopenharmony_ci		cond_resched();
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_cistatic inline void
230962306a36Sopenharmony_ciqla2x00_suspend_hba(struct scsi_qla_host *vha)
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	int cnt;
231262306a36Sopenharmony_ci	unsigned long flags;
231362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
231462306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	/* Suspend HBA. */
231762306a36Sopenharmony_ci	scsi_block_requests(vha->host);
231862306a36Sopenharmony_ci	ha->isp_ops->disable_intrs(ha);
231962306a36Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	/* Pause RISC. */
232262306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
232362306a36Sopenharmony_ci	wrt_reg_word(&reg->hccr, HCCR_PAUSE_RISC);
232462306a36Sopenharmony_ci	rd_reg_word(&reg->hccr);
232562306a36Sopenharmony_ci	if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
232662306a36Sopenharmony_ci		for (cnt = 0; cnt < 30000; cnt++) {
232762306a36Sopenharmony_ci			if ((rd_reg_word(&reg->hccr) & HCCR_RISC_PAUSE) != 0)
232862306a36Sopenharmony_ci				break;
232962306a36Sopenharmony_ci			udelay(100);
233062306a36Sopenharmony_ci		}
233162306a36Sopenharmony_ci	} else {
233262306a36Sopenharmony_ci		udelay(10);
233362306a36Sopenharmony_ci	}
233462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_cistatic inline void
233862306a36Sopenharmony_ciqla2x00_resume_hba(struct scsi_qla_host *vha)
233962306a36Sopenharmony_ci{
234062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	/* Resume HBA. */
234362306a36Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
234462306a36Sopenharmony_ci	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
234562306a36Sopenharmony_ci	qla2xxx_wake_dpc(vha);
234662306a36Sopenharmony_ci	qla2x00_wait_for_chip_reset(vha);
234762306a36Sopenharmony_ci	scsi_unblock_requests(vha->host);
234862306a36Sopenharmony_ci}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_civoid *
235162306a36Sopenharmony_ciqla2x00_read_optrom_data(struct scsi_qla_host *vha, void *buf,
235262306a36Sopenharmony_ci    uint32_t offset, uint32_t length)
235362306a36Sopenharmony_ci{
235462306a36Sopenharmony_ci	uint32_t addr, midpoint;
235562306a36Sopenharmony_ci	uint8_t *data;
235662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
235762306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	/* Suspend HBA. */
236062306a36Sopenharmony_ci	qla2x00_suspend_hba(vha);
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	/* Go with read. */
236362306a36Sopenharmony_ci	midpoint = ha->optrom_size / 2;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	qla2x00_flash_enable(ha);
236662306a36Sopenharmony_ci	wrt_reg_word(&reg->nvram, 0);
236762306a36Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
236862306a36Sopenharmony_ci	for (addr = offset, data = buf; addr < length; addr++, data++) {
236962306a36Sopenharmony_ci		if (addr == midpoint) {
237062306a36Sopenharmony_ci			wrt_reg_word(&reg->nvram, NVR_SELECT);
237162306a36Sopenharmony_ci			rd_reg_word(&reg->nvram);	/* PCI Posting. */
237262306a36Sopenharmony_ci		}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci		*data = qla2x00_read_flash_byte(ha, addr);
237562306a36Sopenharmony_ci	}
237662306a36Sopenharmony_ci	qla2x00_flash_disable(ha);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	/* Resume HBA. */
237962306a36Sopenharmony_ci	qla2x00_resume_hba(vha);
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	return buf;
238262306a36Sopenharmony_ci}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ciint
238562306a36Sopenharmony_ciqla2x00_write_optrom_data(struct scsi_qla_host *vha, void *buf,
238662306a36Sopenharmony_ci    uint32_t offset, uint32_t length)
238762306a36Sopenharmony_ci{
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	int rval;
239062306a36Sopenharmony_ci	uint8_t man_id, flash_id, sec_number, *data;
239162306a36Sopenharmony_ci	uint16_t wd;
239262306a36Sopenharmony_ci	uint32_t addr, liter, sec_mask, rest_addr;
239362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
239462306a36Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	/* Suspend HBA. */
239762306a36Sopenharmony_ci	qla2x00_suspend_hba(vha);
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	rval = QLA_SUCCESS;
240062306a36Sopenharmony_ci	sec_number = 0;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	/* Reset ISP chip. */
240362306a36Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
240462306a36Sopenharmony_ci	pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	/* Go with write. */
240762306a36Sopenharmony_ci	qla2x00_flash_enable(ha);
240862306a36Sopenharmony_ci	do {	/* Loop once to provide quick error exit */
240962306a36Sopenharmony_ci		/* Structure of flash memory based on manufacturer */
241062306a36Sopenharmony_ci		if (IS_OEM_001(ha)) {
241162306a36Sopenharmony_ci			/* OEM variant with special flash part. */
241262306a36Sopenharmony_ci			man_id = flash_id = 0;
241362306a36Sopenharmony_ci			rest_addr = 0xffff;
241462306a36Sopenharmony_ci			sec_mask   = 0x10000;
241562306a36Sopenharmony_ci			goto update_flash;
241662306a36Sopenharmony_ci		}
241762306a36Sopenharmony_ci		qla2x00_get_flash_manufacturer(ha, &man_id, &flash_id);
241862306a36Sopenharmony_ci		switch (man_id) {
241962306a36Sopenharmony_ci		case 0x20: /* ST flash. */
242062306a36Sopenharmony_ci			if (flash_id == 0xd2 || flash_id == 0xe3) {
242162306a36Sopenharmony_ci				/*
242262306a36Sopenharmony_ci				 * ST m29w008at part - 64kb sector size with
242362306a36Sopenharmony_ci				 * 32kb,8kb,8kb,16kb sectors at memory address
242462306a36Sopenharmony_ci				 * 0xf0000.
242562306a36Sopenharmony_ci				 */
242662306a36Sopenharmony_ci				rest_addr = 0xffff;
242762306a36Sopenharmony_ci				sec_mask = 0x10000;
242862306a36Sopenharmony_ci				break;
242962306a36Sopenharmony_ci			}
243062306a36Sopenharmony_ci			/*
243162306a36Sopenharmony_ci			 * ST m29w010b part - 16kb sector size
243262306a36Sopenharmony_ci			 * Default to 16kb sectors
243362306a36Sopenharmony_ci			 */
243462306a36Sopenharmony_ci			rest_addr = 0x3fff;
243562306a36Sopenharmony_ci			sec_mask = 0x1c000;
243662306a36Sopenharmony_ci			break;
243762306a36Sopenharmony_ci		case 0x40: /* Mostel flash. */
243862306a36Sopenharmony_ci			/* Mostel v29c51001 part - 512 byte sector size. */
243962306a36Sopenharmony_ci			rest_addr = 0x1ff;
244062306a36Sopenharmony_ci			sec_mask = 0x1fe00;
244162306a36Sopenharmony_ci			break;
244262306a36Sopenharmony_ci		case 0xbf: /* SST flash. */
244362306a36Sopenharmony_ci			/* SST39sf10 part - 4kb sector size. */
244462306a36Sopenharmony_ci			rest_addr = 0xfff;
244562306a36Sopenharmony_ci			sec_mask = 0x1f000;
244662306a36Sopenharmony_ci			break;
244762306a36Sopenharmony_ci		case 0xda: /* Winbond flash. */
244862306a36Sopenharmony_ci			/* Winbond W29EE011 part - 256 byte sector size. */
244962306a36Sopenharmony_ci			rest_addr = 0x7f;
245062306a36Sopenharmony_ci			sec_mask = 0x1ff80;
245162306a36Sopenharmony_ci			break;
245262306a36Sopenharmony_ci		case 0xc2: /* Macronix flash. */
245362306a36Sopenharmony_ci			/* 64k sector size. */
245462306a36Sopenharmony_ci			if (flash_id == 0x38 || flash_id == 0x4f) {
245562306a36Sopenharmony_ci				rest_addr = 0xffff;
245662306a36Sopenharmony_ci				sec_mask = 0x10000;
245762306a36Sopenharmony_ci				break;
245862306a36Sopenharmony_ci			}
245962306a36Sopenharmony_ci			fallthrough;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci		case 0x1f: /* Atmel flash. */
246262306a36Sopenharmony_ci			/* 512k sector size. */
246362306a36Sopenharmony_ci			if (flash_id == 0x13) {
246462306a36Sopenharmony_ci				rest_addr = 0x7fffffff;
246562306a36Sopenharmony_ci				sec_mask =   0x80000000;
246662306a36Sopenharmony_ci				break;
246762306a36Sopenharmony_ci			}
246862306a36Sopenharmony_ci			fallthrough;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		case 0x01: /* AMD flash. */
247162306a36Sopenharmony_ci			if (flash_id == 0x38 || flash_id == 0x40 ||
247262306a36Sopenharmony_ci			    flash_id == 0x4f) {
247362306a36Sopenharmony_ci				/* Am29LV081 part - 64kb sector size. */
247462306a36Sopenharmony_ci				/* Am29LV002BT part - 64kb sector size. */
247562306a36Sopenharmony_ci				rest_addr = 0xffff;
247662306a36Sopenharmony_ci				sec_mask = 0x10000;
247762306a36Sopenharmony_ci				break;
247862306a36Sopenharmony_ci			} else if (flash_id == 0x3e) {
247962306a36Sopenharmony_ci				/*
248062306a36Sopenharmony_ci				 * Am29LV008b part - 64kb sector size with
248162306a36Sopenharmony_ci				 * 32kb,8kb,8kb,16kb sector at memory address
248262306a36Sopenharmony_ci				 * h0xf0000.
248362306a36Sopenharmony_ci				 */
248462306a36Sopenharmony_ci				rest_addr = 0xffff;
248562306a36Sopenharmony_ci				sec_mask = 0x10000;
248662306a36Sopenharmony_ci				break;
248762306a36Sopenharmony_ci			} else if (flash_id == 0x20 || flash_id == 0x6e) {
248862306a36Sopenharmony_ci				/*
248962306a36Sopenharmony_ci				 * Am29LV010 part or AM29f010 - 16kb sector
249062306a36Sopenharmony_ci				 * size.
249162306a36Sopenharmony_ci				 */
249262306a36Sopenharmony_ci				rest_addr = 0x3fff;
249362306a36Sopenharmony_ci				sec_mask = 0x1c000;
249462306a36Sopenharmony_ci				break;
249562306a36Sopenharmony_ci			} else if (flash_id == 0x6d) {
249662306a36Sopenharmony_ci				/* Am29LV001 part - 8kb sector size. */
249762306a36Sopenharmony_ci				rest_addr = 0x1fff;
249862306a36Sopenharmony_ci				sec_mask = 0x1e000;
249962306a36Sopenharmony_ci				break;
250062306a36Sopenharmony_ci			}
250162306a36Sopenharmony_ci			fallthrough;
250262306a36Sopenharmony_ci		default:
250362306a36Sopenharmony_ci			/* Default to 16 kb sector size. */
250462306a36Sopenharmony_ci			rest_addr = 0x3fff;
250562306a36Sopenharmony_ci			sec_mask = 0x1c000;
250662306a36Sopenharmony_ci			break;
250762306a36Sopenharmony_ci		}
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ciupdate_flash:
251062306a36Sopenharmony_ci		if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
251162306a36Sopenharmony_ci			if (qla2x00_erase_flash(ha, man_id, flash_id)) {
251262306a36Sopenharmony_ci				rval = QLA_FUNCTION_FAILED;
251362306a36Sopenharmony_ci				break;
251462306a36Sopenharmony_ci			}
251562306a36Sopenharmony_ci		}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci		for (addr = offset, liter = 0; liter < length; liter++,
251862306a36Sopenharmony_ci		    addr++) {
251962306a36Sopenharmony_ci			data = buf + liter;
252062306a36Sopenharmony_ci			/* Are we at the beginning of a sector? */
252162306a36Sopenharmony_ci			if ((addr & rest_addr) == 0) {
252262306a36Sopenharmony_ci				if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
252362306a36Sopenharmony_ci					if (addr >= 0x10000UL) {
252462306a36Sopenharmony_ci						if (((addr >> 12) & 0xf0) &&
252562306a36Sopenharmony_ci						    ((man_id == 0x01 &&
252662306a36Sopenharmony_ci							flash_id == 0x3e) ||
252762306a36Sopenharmony_ci						     (man_id == 0x20 &&
252862306a36Sopenharmony_ci							 flash_id == 0xd2))) {
252962306a36Sopenharmony_ci							sec_number++;
253062306a36Sopenharmony_ci							if (sec_number == 1) {
253162306a36Sopenharmony_ci								rest_addr =
253262306a36Sopenharmony_ci								    0x7fff;
253362306a36Sopenharmony_ci								sec_mask =
253462306a36Sopenharmony_ci								    0x18000;
253562306a36Sopenharmony_ci							} else if (
253662306a36Sopenharmony_ci							    sec_number == 2 ||
253762306a36Sopenharmony_ci							    sec_number == 3) {
253862306a36Sopenharmony_ci								rest_addr =
253962306a36Sopenharmony_ci								    0x1fff;
254062306a36Sopenharmony_ci								sec_mask =
254162306a36Sopenharmony_ci								    0x1e000;
254262306a36Sopenharmony_ci							} else if (
254362306a36Sopenharmony_ci							    sec_number == 4) {
254462306a36Sopenharmony_ci								rest_addr =
254562306a36Sopenharmony_ci								    0x3fff;
254662306a36Sopenharmony_ci								sec_mask =
254762306a36Sopenharmony_ci								    0x1c000;
254862306a36Sopenharmony_ci							}
254962306a36Sopenharmony_ci						}
255062306a36Sopenharmony_ci					}
255162306a36Sopenharmony_ci				} else if (addr == ha->optrom_size / 2) {
255262306a36Sopenharmony_ci					wrt_reg_word(&reg->nvram, NVR_SELECT);
255362306a36Sopenharmony_ci					rd_reg_word(&reg->nvram);
255462306a36Sopenharmony_ci				}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci				if (flash_id == 0xda && man_id == 0xc1) {
255762306a36Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x5555,
255862306a36Sopenharmony_ci					    0xaa);
255962306a36Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x2aaa,
256062306a36Sopenharmony_ci					    0x55);
256162306a36Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x5555,
256262306a36Sopenharmony_ci					    0xa0);
256362306a36Sopenharmony_ci				} else if (!IS_QLA2322(ha) && !IS_QLA6322(ha)) {
256462306a36Sopenharmony_ci					/* Then erase it */
256562306a36Sopenharmony_ci					if (qla2x00_erase_flash_sector(ha,
256662306a36Sopenharmony_ci					    addr, sec_mask, man_id,
256762306a36Sopenharmony_ci					    flash_id)) {
256862306a36Sopenharmony_ci						rval = QLA_FUNCTION_FAILED;
256962306a36Sopenharmony_ci						break;
257062306a36Sopenharmony_ci					}
257162306a36Sopenharmony_ci					if (man_id == 0x01 && flash_id == 0x6d)
257262306a36Sopenharmony_ci						sec_number++;
257362306a36Sopenharmony_ci				}
257462306a36Sopenharmony_ci			}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci			if (man_id == 0x01 && flash_id == 0x6d) {
257762306a36Sopenharmony_ci				if (sec_number == 1 &&
257862306a36Sopenharmony_ci				    addr == (rest_addr - 1)) {
257962306a36Sopenharmony_ci					rest_addr = 0x0fff;
258062306a36Sopenharmony_ci					sec_mask   = 0x1f000;
258162306a36Sopenharmony_ci				} else if (sec_number == 3 && (addr & 0x7ffe)) {
258262306a36Sopenharmony_ci					rest_addr = 0x3fff;
258362306a36Sopenharmony_ci					sec_mask   = 0x1c000;
258462306a36Sopenharmony_ci				}
258562306a36Sopenharmony_ci			}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci			if (qla2x00_program_flash_address(ha, addr, *data,
258862306a36Sopenharmony_ci			    man_id, flash_id)) {
258962306a36Sopenharmony_ci				rval = QLA_FUNCTION_FAILED;
259062306a36Sopenharmony_ci				break;
259162306a36Sopenharmony_ci			}
259262306a36Sopenharmony_ci			cond_resched();
259362306a36Sopenharmony_ci		}
259462306a36Sopenharmony_ci	} while (0);
259562306a36Sopenharmony_ci	qla2x00_flash_disable(ha);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	/* Resume HBA. */
259862306a36Sopenharmony_ci	qla2x00_resume_hba(vha);
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	return rval;
260162306a36Sopenharmony_ci}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_civoid *
260462306a36Sopenharmony_ciqla24xx_read_optrom_data(struct scsi_qla_host *vha, void *buf,
260562306a36Sopenharmony_ci    uint32_t offset, uint32_t length)
260662306a36Sopenharmony_ci{
260762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	/* Suspend HBA. */
261062306a36Sopenharmony_ci	scsi_block_requests(vha->host);
261162306a36Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	/* Go with read. */
261462306a36Sopenharmony_ci	qla24xx_read_flash_data(vha, buf, offset >> 2, length >> 2);
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	/* Resume HBA. */
261762306a36Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
261862306a36Sopenharmony_ci	scsi_unblock_requests(vha->host);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	return buf;
262162306a36Sopenharmony_ci}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic int
262462306a36Sopenharmony_ciqla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, __le32 *buf,
262562306a36Sopenharmony_ci    uint32_t len, uint32_t buf_size_without_sfub, uint8_t *sfub_buf)
262662306a36Sopenharmony_ci{
262762306a36Sopenharmony_ci	uint32_t check_sum = 0;
262862306a36Sopenharmony_ci	__le32 *p;
262962306a36Sopenharmony_ci	int i;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	p = buf + buf_size_without_sfub;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	/* Extract SFUB from end of file */
263462306a36Sopenharmony_ci	memcpy(sfub_buf, (uint8_t *)p,
263562306a36Sopenharmony_ci	    sizeof(struct secure_flash_update_block));
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci	for (i = 0; i < (sizeof(struct secure_flash_update_block) >> 2); i++)
263862306a36Sopenharmony_ci		check_sum += le32_to_cpu(p[i]);
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci	check_sum = (~check_sum) + 1;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	if (check_sum != le32_to_cpu(p[i])) {
264362306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7097,
264462306a36Sopenharmony_ci		    "SFUB checksum failed, 0x%x, 0x%x\n",
264562306a36Sopenharmony_ci		    check_sum, le32_to_cpu(p[i]));
264662306a36Sopenharmony_ci		return QLA_COMMAND_ERROR;
264762306a36Sopenharmony_ci	}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	return QLA_SUCCESS;
265062306a36Sopenharmony_ci}
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_cistatic int
265362306a36Sopenharmony_ciqla28xx_get_flash_region(struct scsi_qla_host *vha, uint32_t start,
265462306a36Sopenharmony_ci    struct qla_flt_region *region)
265562306a36Sopenharmony_ci{
265662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
265762306a36Sopenharmony_ci	struct qla_flt_header *flt = ha->flt;
265862306a36Sopenharmony_ci	struct qla_flt_region *flt_reg = &flt->region[0];
265962306a36Sopenharmony_ci	uint16_t cnt;
266062306a36Sopenharmony_ci	int rval = QLA_FUNCTION_FAILED;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	if (!ha->flt)
266362306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
266662306a36Sopenharmony_ci	for (; cnt; cnt--, flt_reg++) {
266762306a36Sopenharmony_ci		if (le32_to_cpu(flt_reg->start) == start) {
266862306a36Sopenharmony_ci			memcpy((uint8_t *)region, flt_reg,
266962306a36Sopenharmony_ci			    sizeof(struct qla_flt_region));
267062306a36Sopenharmony_ci			rval = QLA_SUCCESS;
267162306a36Sopenharmony_ci			break;
267262306a36Sopenharmony_ci		}
267362306a36Sopenharmony_ci	}
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	return rval;
267662306a36Sopenharmony_ci}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_cistatic int
267962306a36Sopenharmony_ciqla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
268062306a36Sopenharmony_ci    uint32_t dwords)
268162306a36Sopenharmony_ci{
268262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
268362306a36Sopenharmony_ci	ulong liter;
268462306a36Sopenharmony_ci	ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
268562306a36Sopenharmony_ci	uint32_t sec_mask, rest_addr, fdata;
268662306a36Sopenharmony_ci	void *optrom = NULL;
268762306a36Sopenharmony_ci	dma_addr_t optrom_dma;
268862306a36Sopenharmony_ci	int rval, ret;
268962306a36Sopenharmony_ci	struct secure_flash_update_block *sfub;
269062306a36Sopenharmony_ci	dma_addr_t sfub_dma;
269162306a36Sopenharmony_ci	uint32_t offset = faddr << 2;
269262306a36Sopenharmony_ci	uint32_t buf_size_without_sfub = 0;
269362306a36Sopenharmony_ci	struct qla_flt_region region;
269462306a36Sopenharmony_ci	bool reset_to_rom = false;
269562306a36Sopenharmony_ci	uint32_t risc_size, risc_attr = 0;
269662306a36Sopenharmony_ci	__be32 *fw_array = NULL;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	/* Retrieve region info - must be a start address passed in */
269962306a36Sopenharmony_ci	rval = qla28xx_get_flash_region(vha, offset, &region);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	if (rval != QLA_SUCCESS) {
270262306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0xffff,
270362306a36Sopenharmony_ci		    "Invalid address %x - not a region start address\n",
270462306a36Sopenharmony_ci		    offset);
270562306a36Sopenharmony_ci		goto done;
270662306a36Sopenharmony_ci	}
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	/* Allocate dma buffer for burst write */
270962306a36Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
271062306a36Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
271162306a36Sopenharmony_ci	if (!optrom) {
271262306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7095,
271362306a36Sopenharmony_ci		    "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
271462306a36Sopenharmony_ci		rval = QLA_COMMAND_ERROR;
271562306a36Sopenharmony_ci		goto done;
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	/*
271962306a36Sopenharmony_ci	 * If adapter supports secure flash and region is secure
272062306a36Sopenharmony_ci	 * extract secure flash update block (SFUB) and verify
272162306a36Sopenharmony_ci	 */
272262306a36Sopenharmony_ci	if (ha->flags.secure_adapter && region.attribute) {
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
272562306a36Sopenharmony_ci		    "Region %x is secure\n", le16_to_cpu(region.code));
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci		switch (le16_to_cpu(region.code)) {
272862306a36Sopenharmony_ci		case FLT_REG_FW:
272962306a36Sopenharmony_ci		case FLT_REG_FW_SEC_27XX:
273062306a36Sopenharmony_ci		case FLT_REG_MPI_PRI_28XX:
273162306a36Sopenharmony_ci		case FLT_REG_MPI_SEC_28XX:
273262306a36Sopenharmony_ci			fw_array = (__force __be32 *)dwptr;
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci			/* 1st fw array */
273562306a36Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
273662306a36Sopenharmony_ci			risc_attr = be32_to_cpu(fw_array[9]);
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci			buf_size_without_sfub = risc_size;
273962306a36Sopenharmony_ci			fw_array += risc_size;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci			/* 2nd fw array */
274262306a36Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci			buf_size_without_sfub += risc_size;
274562306a36Sopenharmony_ci			fw_array += risc_size;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci			/* 1st dump template */
274862306a36Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[2]);
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci			/* skip header and ignore checksum */
275162306a36Sopenharmony_ci			buf_size_without_sfub += risc_size;
275262306a36Sopenharmony_ci			fw_array += risc_size;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci			if (risc_attr & BIT_9) {
275562306a36Sopenharmony_ci				/* 2nd dump template */
275662306a36Sopenharmony_ci				risc_size = be32_to_cpu(fw_array[2]);
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci				/* skip header and ignore checksum */
275962306a36Sopenharmony_ci				buf_size_without_sfub += risc_size;
276062306a36Sopenharmony_ci				fw_array += risc_size;
276162306a36Sopenharmony_ci			}
276262306a36Sopenharmony_ci			break;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci		case FLT_REG_PEP_PRI_28XX:
276562306a36Sopenharmony_ci		case FLT_REG_PEP_SEC_28XX:
276662306a36Sopenharmony_ci			fw_array = (__force __be32 *)dwptr;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci			/* 1st fw array */
276962306a36Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
277062306a36Sopenharmony_ci			risc_attr = be32_to_cpu(fw_array[9]);
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci			buf_size_without_sfub = risc_size;
277362306a36Sopenharmony_ci			fw_array += risc_size;
277462306a36Sopenharmony_ci			break;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci		default:
277762306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha,
277862306a36Sopenharmony_ci			    0xffff, "Secure region %x not supported\n",
277962306a36Sopenharmony_ci			    le16_to_cpu(region.code));
278062306a36Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
278162306a36Sopenharmony_ci			goto done;
278262306a36Sopenharmony_ci		}
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci		sfub = dma_alloc_coherent(&ha->pdev->dev,
278562306a36Sopenharmony_ci			sizeof(struct secure_flash_update_block), &sfub_dma,
278662306a36Sopenharmony_ci			GFP_KERNEL);
278762306a36Sopenharmony_ci		if (!sfub) {
278862306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xffff,
278962306a36Sopenharmony_ci			    "Unable to allocate memory for SFUB\n");
279062306a36Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
279162306a36Sopenharmony_ci			goto done;
279262306a36Sopenharmony_ci		}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci		rval = qla28xx_extract_sfub_and_verify(vha, (__le32 *)dwptr,
279562306a36Sopenharmony_ci			dwords, buf_size_without_sfub, (uint8_t *)sfub);
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci		if (rval != QLA_SUCCESS)
279862306a36Sopenharmony_ci			goto done;
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
280162306a36Sopenharmony_ci		    "SFUB extract and verify successful\n");
280262306a36Sopenharmony_ci	}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	rest_addr = (ha->fdt_block_size >> 2) - 1;
280562306a36Sopenharmony_ci	sec_mask = ~rest_addr;
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	/* Lock semaphore */
280862306a36Sopenharmony_ci	rval = qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_LOCK);
280962306a36Sopenharmony_ci	if (rval != QLA_SUCCESS) {
281062306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0xffff,
281162306a36Sopenharmony_ci		    "Unable to lock flash semaphore.");
281262306a36Sopenharmony_ci		goto done;
281362306a36Sopenharmony_ci	}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
281662306a36Sopenharmony_ci	    "Unprotect flash...\n");
281762306a36Sopenharmony_ci	rval = qla24xx_unprotect_flash(vha);
281862306a36Sopenharmony_ci	if (rval) {
281962306a36Sopenharmony_ci		qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
282062306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7096, "Failed unprotect flash\n");
282162306a36Sopenharmony_ci		goto done;
282262306a36Sopenharmony_ci	}
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++) {
282562306a36Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci		/* If start of sector */
282862306a36Sopenharmony_ci		if (!(faddr & rest_addr)) {
282962306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
283062306a36Sopenharmony_ci			    "Erase sector %#x...\n", faddr);
283162306a36Sopenharmony_ci			rval = qla24xx_erase_sector(vha, fdata);
283262306a36Sopenharmony_ci			if (rval) {
283362306a36Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x7007,
283462306a36Sopenharmony_ci				    "Failed erase sector %#x\n", faddr);
283562306a36Sopenharmony_ci				goto write_protect;
283662306a36Sopenharmony_ci			}
283762306a36Sopenharmony_ci		}
283862306a36Sopenharmony_ci	}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	if (ha->flags.secure_adapter) {
284162306a36Sopenharmony_ci		/*
284262306a36Sopenharmony_ci		 * If adapter supports secure flash but FW doesn't,
284362306a36Sopenharmony_ci		 * disable write protect, release semaphore and reset
284462306a36Sopenharmony_ci		 * chip to execute ROM code in order to update region securely
284562306a36Sopenharmony_ci		 */
284662306a36Sopenharmony_ci		if (!ha->flags.secure_fw) {
284762306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
284862306a36Sopenharmony_ci			    "Disable Write and Release Semaphore.");
284962306a36Sopenharmony_ci			rval = qla24xx_protect_flash(vha);
285062306a36Sopenharmony_ci			if (rval != QLA_SUCCESS) {
285162306a36Sopenharmony_ci				qla81xx_fac_semaphore_access(vha,
285262306a36Sopenharmony_ci					FAC_SEMAPHORE_UNLOCK);
285362306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
285462306a36Sopenharmony_ci				    "Unable to protect flash.");
285562306a36Sopenharmony_ci				goto done;
285662306a36Sopenharmony_ci			}
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
285962306a36Sopenharmony_ci			    "Reset chip to ROM.");
286062306a36Sopenharmony_ci			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
286162306a36Sopenharmony_ci			set_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags);
286262306a36Sopenharmony_ci			qla2xxx_wake_dpc(vha);
286362306a36Sopenharmony_ci			rval = qla2x00_wait_for_chip_reset(vha);
286462306a36Sopenharmony_ci			if (rval != QLA_SUCCESS) {
286562306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
286662306a36Sopenharmony_ci				    "Unable to reset to ROM code.");
286762306a36Sopenharmony_ci				goto done;
286862306a36Sopenharmony_ci			}
286962306a36Sopenharmony_ci			reset_to_rom = true;
287062306a36Sopenharmony_ci			ha->flags.fac_supported = 0;
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
287362306a36Sopenharmony_ci			    "Lock Semaphore");
287462306a36Sopenharmony_ci			rval = qla2xxx_write_remote_register(vha,
287562306a36Sopenharmony_ci			    FLASH_SEMAPHORE_REGISTER_ADDR, 0x00020002);
287662306a36Sopenharmony_ci			if (rval != QLA_SUCCESS) {
287762306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
287862306a36Sopenharmony_ci				    "Unable to lock flash semaphore.");
287962306a36Sopenharmony_ci				goto done;
288062306a36Sopenharmony_ci			}
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci			/* Unprotect flash */
288362306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
288462306a36Sopenharmony_ci			    "Enable Write.");
288562306a36Sopenharmony_ci			rval = qla2x00_write_ram_word(vha, 0x7ffd0101, 0);
288662306a36Sopenharmony_ci			if (rval) {
288762306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0x7096,
288862306a36Sopenharmony_ci				    "Failed unprotect flash\n");
288962306a36Sopenharmony_ci				goto done;
289062306a36Sopenharmony_ci			}
289162306a36Sopenharmony_ci		}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci		/* If region is secure, send Secure Flash MB Cmd */
289462306a36Sopenharmony_ci		if (region.attribute && buf_size_without_sfub) {
289562306a36Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
289662306a36Sopenharmony_ci			    "Sending Secure Flash MB Cmd\n");
289762306a36Sopenharmony_ci			rval = qla28xx_secure_flash_update(vha, 0,
289862306a36Sopenharmony_ci				le16_to_cpu(region.code),
289962306a36Sopenharmony_ci				buf_size_without_sfub, sfub_dma,
290062306a36Sopenharmony_ci				sizeof(struct secure_flash_update_block) >> 2);
290162306a36Sopenharmony_ci			if (rval != QLA_SUCCESS) {
290262306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
290362306a36Sopenharmony_ci				    "Secure Flash MB Cmd failed %x.", rval);
290462306a36Sopenharmony_ci				goto write_protect;
290562306a36Sopenharmony_ci			}
290662306a36Sopenharmony_ci		}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	/* re-init flash offset */
291162306a36Sopenharmony_ci	faddr = offset >> 2;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
291462306a36Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci		/* If smaller than a burst remaining */
291762306a36Sopenharmony_ci		if (dwords - liter < dburst)
291862306a36Sopenharmony_ci			dburst = dwords - liter;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci		/* Copy to dma buffer */
292162306a36Sopenharmony_ci		memcpy(optrom, dwptr, dburst << 2);
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci		/* Burst write */
292462306a36Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
292562306a36Sopenharmony_ci		    "Write burst (%#lx dwords)...\n", dburst);
292662306a36Sopenharmony_ci		rval = qla2x00_load_ram(vha, optrom_dma,
292762306a36Sopenharmony_ci		    flash_data_addr(ha, faddr), dburst);
292862306a36Sopenharmony_ci		if (rval != QLA_SUCCESS) {
292962306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7097,
293062306a36Sopenharmony_ci			    "Failed burst write at %x (%p/%#llx)...\n",
293162306a36Sopenharmony_ci			    flash_data_addr(ha, faddr), optrom,
293262306a36Sopenharmony_ci			    (u64)optrom_dma);
293362306a36Sopenharmony_ci			break;
293462306a36Sopenharmony_ci		}
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci		liter += dburst - 1;
293762306a36Sopenharmony_ci		faddr += dburst - 1;
293862306a36Sopenharmony_ci		dwptr += dburst - 1;
293962306a36Sopenharmony_ci	}
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ciwrite_protect:
294262306a36Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
294362306a36Sopenharmony_ci	    "Protect flash...\n");
294462306a36Sopenharmony_ci	ret = qla24xx_protect_flash(vha);
294562306a36Sopenharmony_ci	if (ret) {
294662306a36Sopenharmony_ci		qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
294762306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7099,
294862306a36Sopenharmony_ci		    "Failed protect flash\n");
294962306a36Sopenharmony_ci		rval = QLA_COMMAND_ERROR;
295062306a36Sopenharmony_ci	}
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	if (reset_to_rom == true) {
295362306a36Sopenharmony_ci		/* Schedule DPC to restart the RISC */
295462306a36Sopenharmony_ci		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
295562306a36Sopenharmony_ci		qla2xxx_wake_dpc(vha);
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci		ret = qla2x00_wait_for_hba_online(vha);
295862306a36Sopenharmony_ci		if (ret != QLA_SUCCESS) {
295962306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xffff,
296062306a36Sopenharmony_ci			    "Adapter did not come out of reset\n");
296162306a36Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
296262306a36Sopenharmony_ci		}
296362306a36Sopenharmony_ci	}
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_cidone:
296662306a36Sopenharmony_ci	if (optrom)
296762306a36Sopenharmony_ci		dma_free_coherent(&ha->pdev->dev,
296862306a36Sopenharmony_ci		    OPTROM_BURST_SIZE, optrom, optrom_dma);
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci	return rval;
297162306a36Sopenharmony_ci}
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ciint
297462306a36Sopenharmony_ciqla24xx_write_optrom_data(struct scsi_qla_host *vha, void *buf,
297562306a36Sopenharmony_ci    uint32_t offset, uint32_t length)
297662306a36Sopenharmony_ci{
297762306a36Sopenharmony_ci	int rval;
297862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	/* Suspend HBA. */
298162306a36Sopenharmony_ci	scsi_block_requests(vha->host);
298262306a36Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	/* Go with write. */
298562306a36Sopenharmony_ci	if (IS_QLA28XX(ha))
298662306a36Sopenharmony_ci		rval = qla28xx_write_flash_data(vha, buf, offset >> 2,
298762306a36Sopenharmony_ci						length >> 2);
298862306a36Sopenharmony_ci	else
298962306a36Sopenharmony_ci		rval = qla24xx_write_flash_data(vha, buf, offset >> 2,
299062306a36Sopenharmony_ci						length >> 2);
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
299362306a36Sopenharmony_ci	scsi_unblock_requests(vha->host);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	return rval;
299662306a36Sopenharmony_ci}
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_civoid *
299962306a36Sopenharmony_ciqla25xx_read_optrom_data(struct scsi_qla_host *vha, void *buf,
300062306a36Sopenharmony_ci    uint32_t offset, uint32_t length)
300162306a36Sopenharmony_ci{
300262306a36Sopenharmony_ci	int rval;
300362306a36Sopenharmony_ci	dma_addr_t optrom_dma;
300462306a36Sopenharmony_ci	void *optrom;
300562306a36Sopenharmony_ci	uint8_t *pbuf;
300662306a36Sopenharmony_ci	uint32_t faddr, left, burst;
300762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
301062306a36Sopenharmony_ci	    IS_QLA27XX(ha) || IS_QLA28XX(ha))
301162306a36Sopenharmony_ci		goto try_fast;
301262306a36Sopenharmony_ci	if (offset & 0xfff)
301362306a36Sopenharmony_ci		goto slow_read;
301462306a36Sopenharmony_ci	if (length < OPTROM_BURST_SIZE)
301562306a36Sopenharmony_ci		goto slow_read;
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_citry_fast:
301862306a36Sopenharmony_ci	if (offset & 0xff)
301962306a36Sopenharmony_ci		goto slow_read;
302062306a36Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
302162306a36Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
302262306a36Sopenharmony_ci	if (!optrom) {
302362306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x00cc,
302462306a36Sopenharmony_ci		    "Unable to allocate memory for optrom burst read (%x KB).\n",
302562306a36Sopenharmony_ci		    OPTROM_BURST_SIZE / 1024);
302662306a36Sopenharmony_ci		goto slow_read;
302762306a36Sopenharmony_ci	}
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	pbuf = buf;
303062306a36Sopenharmony_ci	faddr = offset >> 2;
303162306a36Sopenharmony_ci	left = length >> 2;
303262306a36Sopenharmony_ci	burst = OPTROM_BURST_DWORDS;
303362306a36Sopenharmony_ci	while (left != 0) {
303462306a36Sopenharmony_ci		if (burst > left)
303562306a36Sopenharmony_ci			burst = left;
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci		rval = qla2x00_dump_ram(vha, optrom_dma,
303862306a36Sopenharmony_ci		    flash_data_addr(ha, faddr), burst);
303962306a36Sopenharmony_ci		if (rval) {
304062306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00f5,
304162306a36Sopenharmony_ci			    "Unable to burst-read optrom segment (%x/%x/%llx).\n",
304262306a36Sopenharmony_ci			    rval, flash_data_addr(ha, faddr),
304362306a36Sopenharmony_ci			    (unsigned long long)optrom_dma);
304462306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00f6,
304562306a36Sopenharmony_ci			    "Reverting to slow-read.\n");
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci			dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
304862306a36Sopenharmony_ci			    optrom, optrom_dma);
304962306a36Sopenharmony_ci			goto slow_read;
305062306a36Sopenharmony_ci		}
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci		memcpy(pbuf, optrom, burst * 4);
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci		left -= burst;
305562306a36Sopenharmony_ci		faddr += burst;
305662306a36Sopenharmony_ci		pbuf += burst * 4;
305762306a36Sopenharmony_ci	}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
306062306a36Sopenharmony_ci	    optrom_dma);
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	return buf;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_cislow_read:
306562306a36Sopenharmony_ci    return qla24xx_read_optrom_data(vha, buf, offset, length);
306662306a36Sopenharmony_ci}
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci/**
306962306a36Sopenharmony_ci * qla2x00_get_fcode_version() - Determine an FCODE image's version.
307062306a36Sopenharmony_ci * @ha: HA context
307162306a36Sopenharmony_ci * @pcids: Pointer to the FCODE PCI data structure
307262306a36Sopenharmony_ci *
307362306a36Sopenharmony_ci * The process of retrieving the FCODE version information is at best
307462306a36Sopenharmony_ci * described as interesting.
307562306a36Sopenharmony_ci *
307662306a36Sopenharmony_ci * Within the first 100h bytes of the image an ASCII string is present
307762306a36Sopenharmony_ci * which contains several pieces of information including the FCODE
307862306a36Sopenharmony_ci * version.  Unfortunately it seems the only reliable way to retrieve
307962306a36Sopenharmony_ci * the version is by scanning for another sentinel within the string,
308062306a36Sopenharmony_ci * the FCODE build date:
308162306a36Sopenharmony_ci *
308262306a36Sopenharmony_ci *	... 2.00.02 10/17/02 ...
308362306a36Sopenharmony_ci *
308462306a36Sopenharmony_ci * Returns QLA_SUCCESS on successful retrieval of version.
308562306a36Sopenharmony_ci */
308662306a36Sopenharmony_cistatic void
308762306a36Sopenharmony_ciqla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids)
308862306a36Sopenharmony_ci{
308962306a36Sopenharmony_ci	int ret = QLA_FUNCTION_FAILED;
309062306a36Sopenharmony_ci	uint32_t istart, iend, iter, vend;
309162306a36Sopenharmony_ci	uint8_t do_next, rbyte, *vbyte;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	/* Skip the PCI data structure. */
309662306a36Sopenharmony_ci	istart = pcids +
309762306a36Sopenharmony_ci	    ((qla2x00_read_flash_byte(ha, pcids + 0x0B) << 8) |
309862306a36Sopenharmony_ci		qla2x00_read_flash_byte(ha, pcids + 0x0A));
309962306a36Sopenharmony_ci	iend = istart + 0x100;
310062306a36Sopenharmony_ci	do {
310162306a36Sopenharmony_ci		/* Scan for the sentinel date string...eeewww. */
310262306a36Sopenharmony_ci		do_next = 0;
310362306a36Sopenharmony_ci		iter = istart;
310462306a36Sopenharmony_ci		while ((iter < iend) && !do_next) {
310562306a36Sopenharmony_ci			iter++;
310662306a36Sopenharmony_ci			if (qla2x00_read_flash_byte(ha, iter) == '/') {
310762306a36Sopenharmony_ci				if (qla2x00_read_flash_byte(ha, iter + 2) ==
310862306a36Sopenharmony_ci				    '/')
310962306a36Sopenharmony_ci					do_next++;
311062306a36Sopenharmony_ci				else if (qla2x00_read_flash_byte(ha,
311162306a36Sopenharmony_ci				    iter + 3) == '/')
311262306a36Sopenharmony_ci					do_next++;
311362306a36Sopenharmony_ci			}
311462306a36Sopenharmony_ci		}
311562306a36Sopenharmony_ci		if (!do_next)
311662306a36Sopenharmony_ci			break;
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci		/* Backtrack to previous ' ' (space). */
311962306a36Sopenharmony_ci		do_next = 0;
312062306a36Sopenharmony_ci		while ((iter > istart) && !do_next) {
312162306a36Sopenharmony_ci			iter--;
312262306a36Sopenharmony_ci			if (qla2x00_read_flash_byte(ha, iter) == ' ')
312362306a36Sopenharmony_ci				do_next++;
312462306a36Sopenharmony_ci		}
312562306a36Sopenharmony_ci		if (!do_next)
312662306a36Sopenharmony_ci			break;
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci		/*
312962306a36Sopenharmony_ci		 * Mark end of version tag, and find previous ' ' (space) or
313062306a36Sopenharmony_ci		 * string length (recent FCODE images -- major hack ahead!!!).
313162306a36Sopenharmony_ci		 */
313262306a36Sopenharmony_ci		vend = iter - 1;
313362306a36Sopenharmony_ci		do_next = 0;
313462306a36Sopenharmony_ci		while ((iter > istart) && !do_next) {
313562306a36Sopenharmony_ci			iter--;
313662306a36Sopenharmony_ci			rbyte = qla2x00_read_flash_byte(ha, iter);
313762306a36Sopenharmony_ci			if (rbyte == ' ' || rbyte == 0xd || rbyte == 0x10)
313862306a36Sopenharmony_ci				do_next++;
313962306a36Sopenharmony_ci		}
314062306a36Sopenharmony_ci		if (!do_next)
314162306a36Sopenharmony_ci			break;
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci		/* Mark beginning of version tag, and copy data. */
314462306a36Sopenharmony_ci		iter++;
314562306a36Sopenharmony_ci		if ((vend - iter) &&
314662306a36Sopenharmony_ci		    ((vend - iter) < sizeof(ha->fcode_revision))) {
314762306a36Sopenharmony_ci			vbyte = ha->fcode_revision;
314862306a36Sopenharmony_ci			while (iter <= vend) {
314962306a36Sopenharmony_ci				*vbyte++ = qla2x00_read_flash_byte(ha, iter);
315062306a36Sopenharmony_ci				iter++;
315162306a36Sopenharmony_ci			}
315262306a36Sopenharmony_ci			ret = QLA_SUCCESS;
315362306a36Sopenharmony_ci		}
315462306a36Sopenharmony_ci	} while (0);
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	if (ret != QLA_SUCCESS)
315762306a36Sopenharmony_ci		memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
315862306a36Sopenharmony_ci}
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ciint
316162306a36Sopenharmony_ciqla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
316262306a36Sopenharmony_ci{
316362306a36Sopenharmony_ci	int ret = QLA_SUCCESS;
316462306a36Sopenharmony_ci	uint8_t code_type, last_image;
316562306a36Sopenharmony_ci	uint32_t pcihdr, pcids;
316662306a36Sopenharmony_ci	uint8_t *dbyte;
316762306a36Sopenharmony_ci	uint16_t *dcode;
316862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	if (!ha->pio_address || !mbuf)
317162306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
317462306a36Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
317562306a36Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
317662306a36Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	qla2x00_flash_enable(ha);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
318162306a36Sopenharmony_ci	pcihdr = 0;
318262306a36Sopenharmony_ci	last_image = 1;
318362306a36Sopenharmony_ci	do {
318462306a36Sopenharmony_ci		/* Verify PCI expansion ROM header. */
318562306a36Sopenharmony_ci		if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
318662306a36Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
318762306a36Sopenharmony_ci			/* No signature */
318862306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0050,
318962306a36Sopenharmony_ci			    "No matching ROM signature.\n");
319062306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
319162306a36Sopenharmony_ci			break;
319262306a36Sopenharmony_ci		}
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci		/* Locate PCI data structure. */
319562306a36Sopenharmony_ci		pcids = pcihdr +
319662306a36Sopenharmony_ci		    ((qla2x00_read_flash_byte(ha, pcihdr + 0x19) << 8) |
319762306a36Sopenharmony_ci			qla2x00_read_flash_byte(ha, pcihdr + 0x18));
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci		/* Validate signature of PCI data structure. */
320062306a36Sopenharmony_ci		if (qla2x00_read_flash_byte(ha, pcids) != 'P' ||
320162306a36Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x1) != 'C' ||
320262306a36Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
320362306a36Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
320462306a36Sopenharmony_ci			/* Incorrect header. */
320562306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0051,
320662306a36Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
320762306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
320862306a36Sopenharmony_ci			break;
320962306a36Sopenharmony_ci		}
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci		/* Read version */
321262306a36Sopenharmony_ci		code_type = qla2x00_read_flash_byte(ha, pcids + 0x14);
321362306a36Sopenharmony_ci		switch (code_type) {
321462306a36Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
321562306a36Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
321662306a36Sopenharmony_ci			ha->bios_revision[0] =
321762306a36Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x12);
321862306a36Sopenharmony_ci			ha->bios_revision[1] =
321962306a36Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x13);
322062306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0052,
322162306a36Sopenharmony_ci			    "Read BIOS %d.%d.\n",
322262306a36Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
322362306a36Sopenharmony_ci			break;
322462306a36Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
322562306a36Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
322662306a36Sopenharmony_ci			/* Eeeewww... */
322762306a36Sopenharmony_ci			qla2x00_get_fcode_version(ha, pcids);
322862306a36Sopenharmony_ci			break;
322962306a36Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
323062306a36Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
323162306a36Sopenharmony_ci			ha->efi_revision[0] =
323262306a36Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x12);
323362306a36Sopenharmony_ci			ha->efi_revision[1] =
323462306a36Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x13);
323562306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0053,
323662306a36Sopenharmony_ci			    "Read EFI %d.%d.\n",
323762306a36Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
323862306a36Sopenharmony_ci			break;
323962306a36Sopenharmony_ci		default:
324062306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0054,
324162306a36Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
324262306a36Sopenharmony_ci			    code_type, pcids);
324362306a36Sopenharmony_ci			break;
324462306a36Sopenharmony_ci		}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci		last_image = qla2x00_read_flash_byte(ha, pcids + 0x15) & BIT_7;
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci		/* Locate next PCI expansion ROM. */
324962306a36Sopenharmony_ci		pcihdr += ((qla2x00_read_flash_byte(ha, pcids + 0x11) << 8) |
325062306a36Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x10)) * 512;
325162306a36Sopenharmony_ci	} while (!last_image);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	if (IS_QLA2322(ha)) {
325462306a36Sopenharmony_ci		/* Read firmware image information. */
325562306a36Sopenharmony_ci		memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
325662306a36Sopenharmony_ci		dbyte = mbuf;
325762306a36Sopenharmony_ci		memset(dbyte, 0, 8);
325862306a36Sopenharmony_ci		dcode = (uint16_t *)dbyte;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci		qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
326162306a36Sopenharmony_ci		    8);
326262306a36Sopenharmony_ci		ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010a,
326362306a36Sopenharmony_ci		    "Dumping fw "
326462306a36Sopenharmony_ci		    "ver from flash:.\n");
326562306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010b,
326662306a36Sopenharmony_ci		    dbyte, 32);
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci		if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
326962306a36Sopenharmony_ci		    dcode[2] == 0xffff && dcode[3] == 0xffff) ||
327062306a36Sopenharmony_ci		    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
327162306a36Sopenharmony_ci		    dcode[3] == 0)) {
327262306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0057,
327362306a36Sopenharmony_ci			    "Unrecognized fw revision at %x.\n",
327462306a36Sopenharmony_ci			    ha->flt_region_fw * 4);
327562306a36Sopenharmony_ci		} else {
327662306a36Sopenharmony_ci			/* values are in big endian */
327762306a36Sopenharmony_ci			ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
327862306a36Sopenharmony_ci			ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3];
327962306a36Sopenharmony_ci			ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5];
328062306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0058,
328162306a36Sopenharmony_ci			    "FW Version: "
328262306a36Sopenharmony_ci			    "%d.%d.%d.\n", ha->fw_revision[0],
328362306a36Sopenharmony_ci			    ha->fw_revision[1], ha->fw_revision[2]);
328462306a36Sopenharmony_ci		}
328562306a36Sopenharmony_ci	}
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci	qla2x00_flash_disable(ha);
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci	return ret;
329062306a36Sopenharmony_ci}
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ciint
329362306a36Sopenharmony_ciqla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
329462306a36Sopenharmony_ci{
329562306a36Sopenharmony_ci	int ret = QLA_SUCCESS;
329662306a36Sopenharmony_ci	uint32_t pcihdr, pcids;
329762306a36Sopenharmony_ci	uint32_t *dcode = mbuf;
329862306a36Sopenharmony_ci	uint8_t *bcode = mbuf;
329962306a36Sopenharmony_ci	uint8_t code_type, last_image;
330062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	if (!mbuf)
330362306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
330662306a36Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
330762306a36Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
330862306a36Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
331162306a36Sopenharmony_ci	pcihdr = ha->flt_region_boot << 2;
331262306a36Sopenharmony_ci	last_image = 1;
331362306a36Sopenharmony_ci	do {
331462306a36Sopenharmony_ci		/* Verify PCI expansion ROM header. */
331562306a36Sopenharmony_ci		ha->isp_ops->read_optrom(vha, dcode, pcihdr, 0x20 * 4);
331662306a36Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
331762306a36Sopenharmony_ci		if (memcmp(bcode, "\x55\xaa", 2)) {
331862306a36Sopenharmony_ci			/* No signature */
331962306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0154,
332062306a36Sopenharmony_ci			    "No matching ROM signature.\n");
332162306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
332262306a36Sopenharmony_ci			break;
332362306a36Sopenharmony_ci		}
332462306a36Sopenharmony_ci
332562306a36Sopenharmony_ci		/* Locate PCI data structure. */
332662306a36Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci		ha->isp_ops->read_optrom(vha, dcode, pcids, 0x20 * 4);
332962306a36Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci		/* Validate signature of PCI data structure. */
333262306a36Sopenharmony_ci		if (memcmp(bcode, "PCIR", 4)) {
333362306a36Sopenharmony_ci			/* Incorrect header. */
333462306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0155,
333562306a36Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
333662306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
333762306a36Sopenharmony_ci			break;
333862306a36Sopenharmony_ci		}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci		/* Read version */
334162306a36Sopenharmony_ci		code_type = bcode[0x14];
334262306a36Sopenharmony_ci		switch (code_type) {
334362306a36Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
334462306a36Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
334562306a36Sopenharmony_ci			ha->bios_revision[0] = bcode[0x12];
334662306a36Sopenharmony_ci			ha->bios_revision[1] = bcode[0x13];
334762306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0156,
334862306a36Sopenharmony_ci			    "Read BIOS %d.%d.\n",
334962306a36Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
335062306a36Sopenharmony_ci			break;
335162306a36Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
335262306a36Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
335362306a36Sopenharmony_ci			ha->fcode_revision[0] = bcode[0x12];
335462306a36Sopenharmony_ci			ha->fcode_revision[1] = bcode[0x13];
335562306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0157,
335662306a36Sopenharmony_ci			    "Read FCODE %d.%d.\n",
335762306a36Sopenharmony_ci			    ha->fcode_revision[1], ha->fcode_revision[0]);
335862306a36Sopenharmony_ci			break;
335962306a36Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
336062306a36Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
336162306a36Sopenharmony_ci			ha->efi_revision[0] = bcode[0x12];
336262306a36Sopenharmony_ci			ha->efi_revision[1] = bcode[0x13];
336362306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0158,
336462306a36Sopenharmony_ci			    "Read EFI %d.%d.\n",
336562306a36Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
336662306a36Sopenharmony_ci			break;
336762306a36Sopenharmony_ci		default:
336862306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0159,
336962306a36Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
337062306a36Sopenharmony_ci			    code_type, pcids);
337162306a36Sopenharmony_ci			break;
337262306a36Sopenharmony_ci		}
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci		/* Locate next PCI expansion ROM. */
337762306a36Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
337862306a36Sopenharmony_ci	} while (!last_image);
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	/* Read firmware image information. */
338162306a36Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
338262306a36Sopenharmony_ci	dcode = mbuf;
338362306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, dcode, ha->flt_region_fw << 2, 0x20);
338462306a36Sopenharmony_ci	bcode = mbuf + (pcihdr % 4);
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	/* Validate signature of PCI data structure. */
338762306a36Sopenharmony_ci	if (bcode[0x0] == 0x3 && bcode[0x1] == 0x0 &&
338862306a36Sopenharmony_ci	    bcode[0x2] == 0x40 && bcode[0x3] == 0x40) {
338962306a36Sopenharmony_ci		ha->fw_revision[0] = bcode[0x4];
339062306a36Sopenharmony_ci		ha->fw_revision[1] = bcode[0x5];
339162306a36Sopenharmony_ci		ha->fw_revision[2] = bcode[0x6];
339262306a36Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0153,
339362306a36Sopenharmony_ci		    "Firmware revision %d.%d.%d\n",
339462306a36Sopenharmony_ci		    ha->fw_revision[0], ha->fw_revision[1],
339562306a36Sopenharmony_ci		    ha->fw_revision[2]);
339662306a36Sopenharmony_ci	}
339762306a36Sopenharmony_ci
339862306a36Sopenharmony_ci	return ret;
339962306a36Sopenharmony_ci}
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ciint
340262306a36Sopenharmony_ciqla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
340362306a36Sopenharmony_ci{
340462306a36Sopenharmony_ci	int ret = QLA_SUCCESS;
340562306a36Sopenharmony_ci	uint32_t pcihdr = 0, pcids = 0;
340662306a36Sopenharmony_ci	uint32_t *dcode = mbuf;
340762306a36Sopenharmony_ci	uint8_t *bcode = mbuf;
340862306a36Sopenharmony_ci	uint8_t code_type, last_image;
340962306a36Sopenharmony_ci	int i;
341062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
341162306a36Sopenharmony_ci	uint32_t faddr = 0;
341262306a36Sopenharmony_ci	struct active_regions active_regions = { };
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	if (IS_P3P_TYPE(ha))
341562306a36Sopenharmony_ci		return ret;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	if (!mbuf)
341862306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
342162306a36Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
342262306a36Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
342362306a36Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	pcihdr = ha->flt_region_boot << 2;
342662306a36Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
342762306a36Sopenharmony_ci		qla27xx_get_active_image(vha, &active_regions);
342862306a36Sopenharmony_ci		if (active_regions.global == QLA27XX_SECONDARY_IMAGE) {
342962306a36Sopenharmony_ci			pcihdr = ha->flt_region_boot_sec << 2;
343062306a36Sopenharmony_ci		}
343162306a36Sopenharmony_ci	}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci	do {
343462306a36Sopenharmony_ci		/* Verify PCI expansion ROM header. */
343562306a36Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
343662306a36Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
343762306a36Sopenharmony_ci		if (memcmp(bcode, "\x55\xaa", 2)) {
343862306a36Sopenharmony_ci			/* No signature */
343962306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0059,
344062306a36Sopenharmony_ci			    "No matching ROM signature.\n");
344162306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
344262306a36Sopenharmony_ci			break;
344362306a36Sopenharmony_ci		}
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci		/* Locate PCI data structure. */
344662306a36Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
344962306a36Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci		/* Validate signature of PCI data structure. */
345262306a36Sopenharmony_ci		if (memcmp(bcode, "PCIR", 4)) {
345362306a36Sopenharmony_ci			/* Incorrect header. */
345462306a36Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x005a,
345562306a36Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
345662306a36Sopenharmony_ci			ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32);
345762306a36Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
345862306a36Sopenharmony_ci			break;
345962306a36Sopenharmony_ci		}
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci		/* Read version */
346262306a36Sopenharmony_ci		code_type = bcode[0x14];
346362306a36Sopenharmony_ci		switch (code_type) {
346462306a36Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
346562306a36Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
346662306a36Sopenharmony_ci			ha->bios_revision[0] = bcode[0x12];
346762306a36Sopenharmony_ci			ha->bios_revision[1] = bcode[0x13];
346862306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005b,
346962306a36Sopenharmony_ci			    "Read BIOS %d.%d.\n",
347062306a36Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
347162306a36Sopenharmony_ci			break;
347262306a36Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
347362306a36Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
347462306a36Sopenharmony_ci			ha->fcode_revision[0] = bcode[0x12];
347562306a36Sopenharmony_ci			ha->fcode_revision[1] = bcode[0x13];
347662306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005c,
347762306a36Sopenharmony_ci			    "Read FCODE %d.%d.\n",
347862306a36Sopenharmony_ci			    ha->fcode_revision[1], ha->fcode_revision[0]);
347962306a36Sopenharmony_ci			break;
348062306a36Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
348162306a36Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
348262306a36Sopenharmony_ci			ha->efi_revision[0] = bcode[0x12];
348362306a36Sopenharmony_ci			ha->efi_revision[1] = bcode[0x13];
348462306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005d,
348562306a36Sopenharmony_ci			    "Read EFI %d.%d.\n",
348662306a36Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
348762306a36Sopenharmony_ci			break;
348862306a36Sopenharmony_ci		default:
348962306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x005e,
349062306a36Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
349162306a36Sopenharmony_ci			    code_type, pcids);
349262306a36Sopenharmony_ci			break;
349362306a36Sopenharmony_ci		}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci		/* Locate next PCI expansion ROM. */
349862306a36Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
349962306a36Sopenharmony_ci	} while (!last_image);
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	/* Read firmware image information. */
350262306a36Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
350362306a36Sopenharmony_ci	faddr = ha->flt_region_fw;
350462306a36Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
350562306a36Sopenharmony_ci		qla27xx_get_active_image(vha, &active_regions);
350662306a36Sopenharmony_ci		if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
350762306a36Sopenharmony_ci			faddr = ha->flt_region_fw_sec;
350862306a36Sopenharmony_ci	}
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, faddr, 8);
351162306a36Sopenharmony_ci	if (qla24xx_risc_firmware_invalid(dcode)) {
351262306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x005f,
351362306a36Sopenharmony_ci		    "Unrecognized fw revision at %x.\n",
351462306a36Sopenharmony_ci		    ha->flt_region_fw * 4);
351562306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_init, vha, 0x005f, dcode, 32);
351662306a36Sopenharmony_ci	} else {
351762306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
351862306a36Sopenharmony_ci			ha->fw_revision[i] =
351962306a36Sopenharmony_ci				be32_to_cpu((__force __be32)dcode[4+i]);
352062306a36Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0060,
352162306a36Sopenharmony_ci		    "Firmware revision (flash) %u.%u.%u (%x).\n",
352262306a36Sopenharmony_ci		    ha->fw_revision[0], ha->fw_revision[1],
352362306a36Sopenharmony_ci		    ha->fw_revision[2], ha->fw_revision[3]);
352462306a36Sopenharmony_ci	}
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	/* Check for golden firmware and get version if available */
352762306a36Sopenharmony_ci	if (!IS_QLA81XX(ha)) {
352862306a36Sopenharmony_ci		/* Golden firmware is not present in non 81XX adapters */
352962306a36Sopenharmony_ci		return ret;
353062306a36Sopenharmony_ci	}
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_ci	memset(ha->gold_fw_version, 0, sizeof(ha->gold_fw_version));
353362306a36Sopenharmony_ci	faddr = ha->flt_region_gold_fw;
353462306a36Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, ha->flt_region_gold_fw, 8);
353562306a36Sopenharmony_ci	if (qla24xx_risc_firmware_invalid(dcode)) {
353662306a36Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x0056,
353762306a36Sopenharmony_ci		    "Unrecognized golden fw at %#x.\n", faddr);
353862306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_init, vha, 0x0056, dcode, 32);
353962306a36Sopenharmony_ci		return ret;
354062306a36Sopenharmony_ci	}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci	for (i = 0; i < 4; i++)
354362306a36Sopenharmony_ci		ha->gold_fw_version[i] =
354462306a36Sopenharmony_ci			be32_to_cpu((__force __be32)dcode[4+i]);
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	return ret;
354762306a36Sopenharmony_ci}
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_cistatic int
355062306a36Sopenharmony_ciqla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
355162306a36Sopenharmony_ci{
355262306a36Sopenharmony_ci	if (pos >= end || *pos != 0x82)
355362306a36Sopenharmony_ci		return 0;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	pos += 3 + pos[1];
355662306a36Sopenharmony_ci	if (pos >= end || *pos != 0x90)
355762306a36Sopenharmony_ci		return 0;
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_ci	pos += 3 + pos[1];
356062306a36Sopenharmony_ci	if (pos >= end || *pos != 0x78)
356162306a36Sopenharmony_ci		return 0;
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	return 1;
356462306a36Sopenharmony_ci}
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ciint
356762306a36Sopenharmony_ciqla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size)
356862306a36Sopenharmony_ci{
356962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
357062306a36Sopenharmony_ci	uint8_t *pos = ha->vpd;
357162306a36Sopenharmony_ci	uint8_t *end = pos + ha->vpd_size;
357262306a36Sopenharmony_ci	int len = 0;
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end))
357562306a36Sopenharmony_ci		return 0;
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	while (pos < end && *pos != 0x78) {
357862306a36Sopenharmony_ci		len = (*pos == 0x82) ? pos[1] : pos[2];
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci		if (!strncmp(pos, key, strlen(key)))
358162306a36Sopenharmony_ci			break;
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci		if (*pos != 0x90 && *pos != 0x91)
358462306a36Sopenharmony_ci			pos += len;
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci		pos += 3;
358762306a36Sopenharmony_ci	}
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	if (pos < end - len && *pos != 0x78)
359062306a36Sopenharmony_ci		return scnprintf(str, size, "%.*s", len, pos + 3);
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_ci	return 0;
359362306a36Sopenharmony_ci}
359462306a36Sopenharmony_ci
359562306a36Sopenharmony_ciint
359662306a36Sopenharmony_ciqla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha)
359762306a36Sopenharmony_ci{
359862306a36Sopenharmony_ci	int len, max_len;
359962306a36Sopenharmony_ci	uint32_t fcp_prio_addr;
360062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci	if (!ha->fcp_prio_cfg) {
360362306a36Sopenharmony_ci		ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
360462306a36Sopenharmony_ci		if (!ha->fcp_prio_cfg) {
360562306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00d5,
360662306a36Sopenharmony_ci			    "Unable to allocate memory for fcp priority data (%x).\n",
360762306a36Sopenharmony_ci			    FCP_PRIO_CFG_SIZE);
360862306a36Sopenharmony_ci			return QLA_FUNCTION_FAILED;
360962306a36Sopenharmony_ci		}
361062306a36Sopenharmony_ci	}
361162306a36Sopenharmony_ci	memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE);
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci	fcp_prio_addr = ha->flt_region_fcp_prio;
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	/* first read the fcp priority data header from flash */
361662306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, ha->fcp_prio_cfg,
361762306a36Sopenharmony_ci			fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE);
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0))
362062306a36Sopenharmony_ci		goto fail;
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	/* read remaining FCP CMD config data from flash */
362362306a36Sopenharmony_ci	fcp_prio_addr += (FCP_PRIO_CFG_HDR_SIZE >> 2);
362462306a36Sopenharmony_ci	len = ha->fcp_prio_cfg->num_entries * sizeof(struct qla_fcp_prio_entry);
362562306a36Sopenharmony_ci	max_len = FCP_PRIO_CFG_SIZE - FCP_PRIO_CFG_HDR_SIZE;
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci	ha->isp_ops->read_optrom(vha, &ha->fcp_prio_cfg->entry[0],
362862306a36Sopenharmony_ci			fcp_prio_addr << 2, (len < max_len ? len : max_len));
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	/* revalidate the entire FCP priority config data, including entries */
363162306a36Sopenharmony_ci	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1))
363262306a36Sopenharmony_ci		goto fail;
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci	ha->flags.fcp_prio_enabled = 1;
363562306a36Sopenharmony_ci	return QLA_SUCCESS;
363662306a36Sopenharmony_cifail:
363762306a36Sopenharmony_ci	vfree(ha->fcp_prio_cfg);
363862306a36Sopenharmony_ci	ha->fcp_prio_cfg = NULL;
363962306a36Sopenharmony_ci	return QLA_FUNCTION_FAILED;
364062306a36Sopenharmony_ci}
3641