18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * QLogic Fibre Channel HBA Driver
48c2ecf20Sopenharmony_ci * Copyright (c)  2003-2014 QLogic Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include "qla_def.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
118c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * NVRAM support routines
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/**
188c2ecf20Sopenharmony_ci * qla2x00_lock_nvram_access() -
198c2ecf20Sopenharmony_ci * @ha: HA context
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic void
228c2ecf20Sopenharmony_ciqla2x00_lock_nvram_access(struct qla_hw_data *ha)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	uint16_t data;
258c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
288c2ecf20Sopenharmony_ci		data = rd_reg_word(&reg->nvram);
298c2ecf20Sopenharmony_ci		while (data & NVR_BUSY) {
308c2ecf20Sopenharmony_ci			udelay(100);
318c2ecf20Sopenharmony_ci			data = rd_reg_word(&reg->nvram);
328c2ecf20Sopenharmony_ci		}
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci		/* Lock resource */
358c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->u.isp2300.host_semaphore, 0x1);
368c2ecf20Sopenharmony_ci		rd_reg_word(&reg->u.isp2300.host_semaphore);
378c2ecf20Sopenharmony_ci		udelay(5);
388c2ecf20Sopenharmony_ci		data = rd_reg_word(&reg->u.isp2300.host_semaphore);
398c2ecf20Sopenharmony_ci		while ((data & BIT_0) == 0) {
408c2ecf20Sopenharmony_ci			/* Lock failed */
418c2ecf20Sopenharmony_ci			udelay(100);
428c2ecf20Sopenharmony_ci			wrt_reg_word(&reg->u.isp2300.host_semaphore, 0x1);
438c2ecf20Sopenharmony_ci			rd_reg_word(&reg->u.isp2300.host_semaphore);
448c2ecf20Sopenharmony_ci			udelay(5);
458c2ecf20Sopenharmony_ci			data = rd_reg_word(&reg->u.isp2300.host_semaphore);
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/**
518c2ecf20Sopenharmony_ci * qla2x00_unlock_nvram_access() -
528c2ecf20Sopenharmony_ci * @ha: HA context
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic void
558c2ecf20Sopenharmony_ciqla2x00_unlock_nvram_access(struct qla_hw_data *ha)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
608c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->u.isp2300.host_semaphore, 0);
618c2ecf20Sopenharmony_ci		rd_reg_word(&reg->u.isp2300.host_semaphore);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
678c2ecf20Sopenharmony_ci * @ha: HA context
688c2ecf20Sopenharmony_ci * @data: Serial interface selector
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic void
718c2ecf20Sopenharmony_ciqla2x00_nv_write(struct qla_hw_data *ha, uint16_t data)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
768c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
778c2ecf20Sopenharmony_ci	NVRAM_DELAY();
788c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_CLOCK |
798c2ecf20Sopenharmony_ci	    NVR_WRT_ENABLE);
808c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
818c2ecf20Sopenharmony_ci	NVRAM_DELAY();
828c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
838c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
848c2ecf20Sopenharmony_ci	NVRAM_DELAY();
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/**
888c2ecf20Sopenharmony_ci * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
898c2ecf20Sopenharmony_ci *	NVRAM.
908c2ecf20Sopenharmony_ci * @ha: HA context
918c2ecf20Sopenharmony_ci * @nv_cmd: NVRAM command
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * Bit definitions for NVRAM command:
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci *	Bit 26     = start bit
968c2ecf20Sopenharmony_ci *	Bit 25, 24 = opcode
978c2ecf20Sopenharmony_ci *	Bit 23-16  = address
988c2ecf20Sopenharmony_ci *	Bit 15-0   = write data
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * Returns the word read from nvram @addr.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic uint16_t
1038c2ecf20Sopenharmony_ciqla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	uint8_t		cnt;
1068c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
1078c2ecf20Sopenharmony_ci	uint16_t	data = 0;
1088c2ecf20Sopenharmony_ci	uint16_t	reg_data;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Send command to NVRAM. */
1118c2ecf20Sopenharmony_ci	nv_cmd <<= 5;
1128c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < 11; cnt++) {
1138c2ecf20Sopenharmony_ci		if (nv_cmd & BIT_31)
1148c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
1158c2ecf20Sopenharmony_ci		else
1168c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, 0);
1178c2ecf20Sopenharmony_ci		nv_cmd <<= 1;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Read data from NVRAM. */
1218c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < 16; cnt++) {
1228c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT | NVR_CLOCK);
1238c2ecf20Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
1248c2ecf20Sopenharmony_ci		NVRAM_DELAY();
1258c2ecf20Sopenharmony_ci		data <<= 1;
1268c2ecf20Sopenharmony_ci		reg_data = rd_reg_word(&reg->nvram);
1278c2ecf20Sopenharmony_ci		if (reg_data & NVR_DATA_IN)
1288c2ecf20Sopenharmony_ci			data |= BIT_0;
1298c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT);
1308c2ecf20Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
1318c2ecf20Sopenharmony_ci		NVRAM_DELAY();
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Deselect chip. */
1358c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_DESELECT);
1368c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
1378c2ecf20Sopenharmony_ci	NVRAM_DELAY();
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return data;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/**
1448c2ecf20Sopenharmony_ci * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
1458c2ecf20Sopenharmony_ci *	request routine to get the word from NVRAM.
1468c2ecf20Sopenharmony_ci * @ha: HA context
1478c2ecf20Sopenharmony_ci * @addr: Address in NVRAM to read
1488c2ecf20Sopenharmony_ci *
1498c2ecf20Sopenharmony_ci * Returns the word read from nvram @addr.
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_cistatic uint16_t
1528c2ecf20Sopenharmony_ciqla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	uint16_t	data;
1558c2ecf20Sopenharmony_ci	uint32_t	nv_cmd;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	nv_cmd = addr << 16;
1588c2ecf20Sopenharmony_ci	nv_cmd |= NV_READ_OP;
1598c2ecf20Sopenharmony_ci	data = qla2x00_nvram_request(ha, nv_cmd);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return (data);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * qla2x00_nv_deselect() - Deselect NVRAM operations.
1668c2ecf20Sopenharmony_ci * @ha: HA context
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cistatic void
1698c2ecf20Sopenharmony_ciqla2x00_nv_deselect(struct qla_hw_data *ha)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_DESELECT);
1748c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
1758c2ecf20Sopenharmony_ci	NVRAM_DELAY();
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * qla2x00_write_nvram_word() - Write NVRAM data.
1808c2ecf20Sopenharmony_ci * @ha: HA context
1818c2ecf20Sopenharmony_ci * @addr: Address in NVRAM to write
1828c2ecf20Sopenharmony_ci * @data: word to program
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistatic void
1858c2ecf20Sopenharmony_ciqla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, __le16 data)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int count;
1888c2ecf20Sopenharmony_ci	uint16_t word;
1898c2ecf20Sopenharmony_ci	uint32_t nv_cmd, wait_cnt;
1908c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
1918c2ecf20Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
1948c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
1958c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	for (word = 0; word < 8; word++)
1988c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Write data */
2038c2ecf20Sopenharmony_ci	nv_cmd = (addr << 16) | NV_WRITE_OP;
2048c2ecf20Sopenharmony_ci	nv_cmd |= (__force u16)data;
2058c2ecf20Sopenharmony_ci	nv_cmd <<= 5;
2068c2ecf20Sopenharmony_ci	for (count = 0; count < 27; count++) {
2078c2ecf20Sopenharmony_ci		if (nv_cmd & BIT_31)
2088c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
2098c2ecf20Sopenharmony_ci		else
2108c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, 0);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		nv_cmd <<= 1;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Wait for NVRAM to become ready */
2188c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
2198c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
2208c2ecf20Sopenharmony_ci	wait_cnt = NVR_WAIT_CNT;
2218c2ecf20Sopenharmony_ci	do {
2228c2ecf20Sopenharmony_ci		if (!--wait_cnt) {
2238c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x708d,
2248c2ecf20Sopenharmony_ci			    "NVRAM didn't go ready...\n");
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci		NVRAM_DELAY();
2288c2ecf20Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
2298c2ecf20Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Disable writes */
2348c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
2358c2ecf20Sopenharmony_ci	for (count = 0; count < 10; count++)
2368c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, 0);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int
2428c2ecf20Sopenharmony_ciqla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr,
2438c2ecf20Sopenharmony_ci			     __le16 data, uint32_t tmo)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int ret, count;
2468c2ecf20Sopenharmony_ci	uint16_t word;
2478c2ecf20Sopenharmony_ci	uint32_t nv_cmd;
2488c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ret = QLA_SUCCESS;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
2538c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
2548c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (word = 0; word < 8; word++)
2578c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* Write data */
2628c2ecf20Sopenharmony_ci	nv_cmd = (addr << 16) | NV_WRITE_OP;
2638c2ecf20Sopenharmony_ci	nv_cmd |= (__force u16)data;
2648c2ecf20Sopenharmony_ci	nv_cmd <<= 5;
2658c2ecf20Sopenharmony_ci	for (count = 0; count < 27; count++) {
2668c2ecf20Sopenharmony_ci		if (nv_cmd & BIT_31)
2678c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
2688c2ecf20Sopenharmony_ci		else
2698c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, 0);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		nv_cmd <<= 1;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Wait for NVRAM to become ready */
2778c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
2788c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
2798c2ecf20Sopenharmony_ci	do {
2808c2ecf20Sopenharmony_ci		NVRAM_DELAY();
2818c2ecf20Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
2828c2ecf20Sopenharmony_ci		if (!--tmo) {
2838c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
2848c2ecf20Sopenharmony_ci			break;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Disable writes */
2918c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
2928c2ecf20Sopenharmony_ci	for (count = 0; count < 10; count++)
2938c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, 0);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return ret;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/**
3018c2ecf20Sopenharmony_ci * qla2x00_clear_nvram_protection() -
3028c2ecf20Sopenharmony_ci * @ha: HA context
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_cistatic int
3058c2ecf20Sopenharmony_ciqla2x00_clear_nvram_protection(struct qla_hw_data *ha)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int ret, stat;
3088c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
3098c2ecf20Sopenharmony_ci	uint32_t word, wait_cnt;
3108c2ecf20Sopenharmony_ci	__le16 wprot, wprot_old;
3118c2ecf20Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* Clear NVRAM write protection. */
3148c2ecf20Sopenharmony_ci	ret = QLA_FUNCTION_FAILED;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	wprot_old = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
3178c2ecf20Sopenharmony_ci	stat = qla2x00_write_nvram_word_tmo(ha, ha->nvram_base,
3188c2ecf20Sopenharmony_ci					    cpu_to_le16(0x1234), 100000);
3198c2ecf20Sopenharmony_ci	wprot = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
3208c2ecf20Sopenharmony_ci	if (stat != QLA_SUCCESS || wprot != cpu_to_le16(0x1234)) {
3218c2ecf20Sopenharmony_ci		/* Write enable. */
3228c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
3238c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, 0);
3248c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, 0);
3258c2ecf20Sopenharmony_ci		for (word = 0; word < 8; word++)
3268c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		qla2x00_nv_deselect(ha);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		/* Enable protection register. */
3318c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
3328c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
3338c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
3348c2ecf20Sopenharmony_ci		for (word = 0; word < 8; word++)
3358c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		qla2x00_nv_deselect(ha);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		/* Clear protection register (ffff is cleared). */
3408c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
3418c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
3428c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
3438c2ecf20Sopenharmony_ci		for (word = 0; word < 8; word++)
3448c2ecf20Sopenharmony_ci			qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		qla2x00_nv_deselect(ha);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		/* Wait for NVRAM to become ready. */
3498c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->nvram, NVR_SELECT);
3508c2ecf20Sopenharmony_ci		rd_reg_word(&reg->nvram);	/* PCI Posting. */
3518c2ecf20Sopenharmony_ci		wait_cnt = NVR_WAIT_CNT;
3528c2ecf20Sopenharmony_ci		do {
3538c2ecf20Sopenharmony_ci			if (!--wait_cnt) {
3548c2ecf20Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x708e,
3558c2ecf20Sopenharmony_ci				    "NVRAM didn't go ready...\n");
3568c2ecf20Sopenharmony_ci				break;
3578c2ecf20Sopenharmony_ci			}
3588c2ecf20Sopenharmony_ci			NVRAM_DELAY();
3598c2ecf20Sopenharmony_ci			word = rd_reg_word(&reg->nvram);
3608c2ecf20Sopenharmony_ci		} while ((word & NVR_DATA_IN) == 0);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		if (wait_cnt)
3638c2ecf20Sopenharmony_ci			ret = QLA_SUCCESS;
3648c2ecf20Sopenharmony_ci	} else
3658c2ecf20Sopenharmony_ci		qla2x00_write_nvram_word(ha, ha->nvram_base, wprot_old);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return ret;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic void
3718c2ecf20Sopenharmony_ciqla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
3748c2ecf20Sopenharmony_ci	uint32_t word, wait_cnt;
3758c2ecf20Sopenharmony_ci	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (stat != QLA_SUCCESS)
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Set NVRAM write protection. */
3818c2ecf20Sopenharmony_ci	/* Write enable. */
3828c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_DATA_OUT);
3838c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
3848c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, 0);
3858c2ecf20Sopenharmony_ci	for (word = 0; word < 8; word++)
3868c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Enable protection register. */
3918c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
3928c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
3938c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
3948c2ecf20Sopenharmony_ci	for (word = 0; word < 8; word++)
3958c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Enable protection register. */
4008c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
4018c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE);
4028c2ecf20Sopenharmony_ci	qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
4038c2ecf20Sopenharmony_ci	for (word = 0; word < 8; word++)
4048c2ecf20Sopenharmony_ci		qla2x00_nv_write(ha, NVR_PR_ENABLE);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	qla2x00_nv_deselect(ha);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/* Wait for NVRAM to become ready. */
4098c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, NVR_SELECT);
4108c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
4118c2ecf20Sopenharmony_ci	wait_cnt = NVR_WAIT_CNT;
4128c2ecf20Sopenharmony_ci	do {
4138c2ecf20Sopenharmony_ci		if (!--wait_cnt) {
4148c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x708f,
4158c2ecf20Sopenharmony_ci			    "NVRAM didn't go ready...\n");
4168c2ecf20Sopenharmony_ci			break;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci		NVRAM_DELAY();
4198c2ecf20Sopenharmony_ci		word = rd_reg_word(&reg->nvram);
4208c2ecf20Sopenharmony_ci	} while ((word & NVR_DATA_IN) == 0);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/*****************************************************************************/
4258c2ecf20Sopenharmony_ci/* Flash Manipulation Routines                                               */
4268c2ecf20Sopenharmony_ci/*****************************************************************************/
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic inline uint32_t
4298c2ecf20Sopenharmony_ciflash_conf_addr(struct qla_hw_data *ha, uint32_t faddr)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	return ha->flash_conf_off + faddr;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic inline uint32_t
4358c2ecf20Sopenharmony_ciflash_data_addr(struct qla_hw_data *ha, uint32_t faddr)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	return ha->flash_data_off + faddr;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic inline uint32_t
4418c2ecf20Sopenharmony_cinvram_conf_addr(struct qla_hw_data *ha, uint32_t naddr)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	return ha->nvram_conf_off + naddr;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic inline uint32_t
4478c2ecf20Sopenharmony_cinvram_data_addr(struct qla_hw_data *ha, uint32_t naddr)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	return ha->nvram_data_off + naddr;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int
4538c2ecf20Sopenharmony_ciqla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t *data)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
4568c2ecf20Sopenharmony_ci	ulong cnt = 30000;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->flash_addr, addr & ~FARX_DATA_FLAG);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	while (cnt--) {
4618c2ecf20Sopenharmony_ci		if (rd_reg_dword(&reg->flash_addr) & FARX_DATA_FLAG) {
4628c2ecf20Sopenharmony_ci			*data = rd_reg_dword(&reg->flash_data);
4638c2ecf20Sopenharmony_ci			return QLA_SUCCESS;
4648c2ecf20Sopenharmony_ci		}
4658c2ecf20Sopenharmony_ci		udelay(10);
4668c2ecf20Sopenharmony_ci		cond_resched();
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	ql_log(ql_log_warn, pci_get_drvdata(ha->pdev), 0x7090,
4708c2ecf20Sopenharmony_ci	    "Flash read dword at %x timeout.\n", addr);
4718c2ecf20Sopenharmony_ci	*data = 0xDEADDEAD;
4728c2ecf20Sopenharmony_ci	return QLA_FUNCTION_TIMEOUT;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ciint
4768c2ecf20Sopenharmony_ciqla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
4778c2ecf20Sopenharmony_ci    uint32_t dwords)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	ulong i;
4808c2ecf20Sopenharmony_ci	int ret = QLA_SUCCESS;
4818c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Dword reads to flash. */
4848c2ecf20Sopenharmony_ci	faddr =  flash_data_addr(ha, faddr);
4858c2ecf20Sopenharmony_ci	for (i = 0; i < dwords; i++, faddr++, dwptr++) {
4868c2ecf20Sopenharmony_ci		ret = qla24xx_read_flash_dword(ha, faddr, dwptr);
4878c2ecf20Sopenharmony_ci		if (ret != QLA_SUCCESS)
4888c2ecf20Sopenharmony_ci			break;
4898c2ecf20Sopenharmony_ci		cpu_to_le32s(dwptr);
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return ret;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic int
4968c2ecf20Sopenharmony_ciqla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
4998c2ecf20Sopenharmony_ci	ulong cnt = 500000;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->flash_data, data);
5028c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->flash_addr, addr | FARX_DATA_FLAG);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	while (cnt--) {
5058c2ecf20Sopenharmony_ci		if (!(rd_reg_dword(&reg->flash_addr) & FARX_DATA_FLAG))
5068c2ecf20Sopenharmony_ci			return QLA_SUCCESS;
5078c2ecf20Sopenharmony_ci		udelay(10);
5088c2ecf20Sopenharmony_ci		cond_resched();
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	ql_log(ql_log_warn, pci_get_drvdata(ha->pdev), 0x7090,
5128c2ecf20Sopenharmony_ci	    "Flash write dword at %x timeout.\n", addr);
5138c2ecf20Sopenharmony_ci	return QLA_FUNCTION_TIMEOUT;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void
5178c2ecf20Sopenharmony_ciqla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
5188c2ecf20Sopenharmony_ci    uint8_t *flash_id)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	uint32_t faddr, ids = 0;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	*man_id = *flash_id = 0;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x03ab);
5258c2ecf20Sopenharmony_ci	if (!qla24xx_read_flash_dword(ha, faddr, &ids)) {
5268c2ecf20Sopenharmony_ci		*man_id = LSB(ids);
5278c2ecf20Sopenharmony_ci		*flash_id = MSB(ids);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/* Check if man_id and flash_id are valid. */
5318c2ecf20Sopenharmony_ci	if (ids != 0xDEADDEAD && (*man_id == 0 || *flash_id == 0)) {
5328c2ecf20Sopenharmony_ci		/* Read information using 0x9f opcode
5338c2ecf20Sopenharmony_ci		 * Device ID, Mfg ID would be read in the format:
5348c2ecf20Sopenharmony_ci		 *   <Ext Dev Info><Device ID Part2><Device ID Part 1><Mfg ID>
5358c2ecf20Sopenharmony_ci		 * Example: ATMEL 0x00 01 45 1F
5368c2ecf20Sopenharmony_ci		 * Extract MFG and Dev ID from last two bytes.
5378c2ecf20Sopenharmony_ci		 */
5388c2ecf20Sopenharmony_ci		faddr = flash_conf_addr(ha, 0x009f);
5398c2ecf20Sopenharmony_ci		if (!qla24xx_read_flash_dword(ha, faddr, &ids)) {
5408c2ecf20Sopenharmony_ci			*man_id = LSB(ids);
5418c2ecf20Sopenharmony_ci			*flash_id = MSB(ids);
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int
5478c2ecf20Sopenharmony_ciqla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
5488c2ecf20Sopenharmony_ci{
5498c2ecf20Sopenharmony_ci	const char *loc, *locations[] = { "DEF", "PCI" };
5508c2ecf20Sopenharmony_ci	uint32_t pcihdr, pcids;
5518c2ecf20Sopenharmony_ci	uint16_t cnt, chksum;
5528c2ecf20Sopenharmony_ci	__le16 *wptr;
5538c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
5548c2ecf20Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
5558c2ecf20Sopenharmony_ci	struct qla_flt_location *fltl = (void *)req->ring;
5568c2ecf20Sopenharmony_ci	uint32_t *dcode = (uint32_t *)req->ring;
5578c2ecf20Sopenharmony_ci	uint8_t *buf = (void *)req->ring, *bcode,  last_image;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/*
5608c2ecf20Sopenharmony_ci	 * FLT-location structure resides after the last PCI region.
5618c2ecf20Sopenharmony_ci	 */
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* Begin with sane defaults. */
5648c2ecf20Sopenharmony_ci	loc = locations[0];
5658c2ecf20Sopenharmony_ci	*start = 0;
5668c2ecf20Sopenharmony_ci	if (IS_QLA24XX_TYPE(ha))
5678c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_24;
5688c2ecf20Sopenharmony_ci	else if (IS_QLA25XX(ha))
5698c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR;
5708c2ecf20Sopenharmony_ci	else if (IS_QLA81XX(ha))
5718c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_81;
5728c2ecf20Sopenharmony_ci	else if (IS_P3P_TYPE(ha)) {
5738c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_82;
5748c2ecf20Sopenharmony_ci		goto end;
5758c2ecf20Sopenharmony_ci	} else if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) {
5768c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_83;
5778c2ecf20Sopenharmony_ci		goto end;
5788c2ecf20Sopenharmony_ci	} else if (IS_QLA28XX(ha)) {
5798c2ecf20Sopenharmony_ci		*start = FA_FLASH_LAYOUT_ADDR_28;
5808c2ecf20Sopenharmony_ci		goto end;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
5848c2ecf20Sopenharmony_ci	pcihdr = 0;
5858c2ecf20Sopenharmony_ci	do {
5868c2ecf20Sopenharmony_ci		/* Verify PCI expansion ROM header. */
5878c2ecf20Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
5888c2ecf20Sopenharmony_ci		bcode = buf + (pcihdr % 4);
5898c2ecf20Sopenharmony_ci		if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa)
5908c2ecf20Sopenharmony_ci			goto end;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		/* Locate PCI data structure. */
5938c2ecf20Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
5948c2ecf20Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
5958c2ecf20Sopenharmony_ci		bcode = buf + (pcihdr % 4);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		/* Validate signature of PCI data structure. */
5988c2ecf20Sopenharmony_ci		if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
5998c2ecf20Sopenharmony_ci		    bcode[0x2] != 'I' || bcode[0x3] != 'R')
6008c2ecf20Sopenharmony_ci			goto end;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		/* Locate next PCI expansion ROM. */
6058c2ecf20Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
6068c2ecf20Sopenharmony_ci	} while (!last_image);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* Now verify FLT-location structure. */
6098c2ecf20Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, sizeof(*fltl) >> 2);
6108c2ecf20Sopenharmony_ci	if (memcmp(fltl->sig, "QFLT", 4))
6118c2ecf20Sopenharmony_ci		goto end;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	wptr = (__force __le16 *)req->ring;
6148c2ecf20Sopenharmony_ci	cnt = sizeof(*fltl) / sizeof(*wptr);
6158c2ecf20Sopenharmony_ci	for (chksum = 0; cnt--; wptr++)
6168c2ecf20Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
6178c2ecf20Sopenharmony_ci	if (chksum) {
6188c2ecf20Sopenharmony_ci		ql_log(ql_log_fatal, vha, 0x0045,
6198c2ecf20Sopenharmony_ci		    "Inconsistent FLTL detected: checksum=0x%x.\n", chksum);
6208c2ecf20Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e,
6218c2ecf20Sopenharmony_ci		    fltl, sizeof(*fltl));
6228c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* Good data.  Use specified location. */
6268c2ecf20Sopenharmony_ci	loc = locations[1];
6278c2ecf20Sopenharmony_ci	*start = (le16_to_cpu(fltl->start_hi) << 16 |
6288c2ecf20Sopenharmony_ci	    le16_to_cpu(fltl->start_lo)) >> 2;
6298c2ecf20Sopenharmony_ciend:
6308c2ecf20Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x0046,
6318c2ecf20Sopenharmony_ci	    "FLTL[%s] = 0x%x.\n",
6328c2ecf20Sopenharmony_ci	    loc, *start);
6338c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void
6378c2ecf20Sopenharmony_ciqla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	const char *locations[] = { "DEF", "FLT" }, *loc = locations[1];
6408c2ecf20Sopenharmony_ci	const uint32_t def_fw[] =
6418c2ecf20Sopenharmony_ci		{ FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 };
6428c2ecf20Sopenharmony_ci	const uint32_t def_boot[] =
6438c2ecf20Sopenharmony_ci		{ FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR_81 };
6448c2ecf20Sopenharmony_ci	const uint32_t def_vpd_nvram[] =
6458c2ecf20Sopenharmony_ci		{ FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR_81 };
6468c2ecf20Sopenharmony_ci	const uint32_t def_vpd0[] =
6478c2ecf20Sopenharmony_ci		{ 0, 0, FA_VPD0_ADDR_81 };
6488c2ecf20Sopenharmony_ci	const uint32_t def_vpd1[] =
6498c2ecf20Sopenharmony_ci		{ 0, 0, FA_VPD1_ADDR_81 };
6508c2ecf20Sopenharmony_ci	const uint32_t def_nvram0[] =
6518c2ecf20Sopenharmony_ci		{ 0, 0, FA_NVRAM0_ADDR_81 };
6528c2ecf20Sopenharmony_ci	const uint32_t def_nvram1[] =
6538c2ecf20Sopenharmony_ci		{ 0, 0, FA_NVRAM1_ADDR_81 };
6548c2ecf20Sopenharmony_ci	const uint32_t def_fdt[] =
6558c2ecf20Sopenharmony_ci		{ FA_FLASH_DESCR_ADDR_24, FA_FLASH_DESCR_ADDR,
6568c2ecf20Sopenharmony_ci			FA_FLASH_DESCR_ADDR_81 };
6578c2ecf20Sopenharmony_ci	const uint32_t def_npiv_conf0[] =
6588c2ecf20Sopenharmony_ci		{ FA_NPIV_CONF0_ADDR_24, FA_NPIV_CONF0_ADDR,
6598c2ecf20Sopenharmony_ci			FA_NPIV_CONF0_ADDR_81 };
6608c2ecf20Sopenharmony_ci	const uint32_t def_npiv_conf1[] =
6618c2ecf20Sopenharmony_ci		{ FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR,
6628c2ecf20Sopenharmony_ci			FA_NPIV_CONF1_ADDR_81 };
6638c2ecf20Sopenharmony_ci	const uint32_t fcp_prio_cfg0[] =
6648c2ecf20Sopenharmony_ci		{ FA_FCP_PRIO0_ADDR, FA_FCP_PRIO0_ADDR_25,
6658c2ecf20Sopenharmony_ci			0 };
6668c2ecf20Sopenharmony_ci	const uint32_t fcp_prio_cfg1[] =
6678c2ecf20Sopenharmony_ci		{ FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25,
6688c2ecf20Sopenharmony_ci			0 };
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
6718c2ecf20Sopenharmony_ci	uint32_t def = IS_QLA81XX(ha) ? 2 : IS_QLA25XX(ha) ? 1 : 0;
6728c2ecf20Sopenharmony_ci	struct qla_flt_header *flt = ha->flt;
6738c2ecf20Sopenharmony_ci	struct qla_flt_region *region = &flt->region[0];
6748c2ecf20Sopenharmony_ci	__le16 *wptr;
6758c2ecf20Sopenharmony_ci	uint16_t cnt, chksum;
6768c2ecf20Sopenharmony_ci	uint32_t start;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* Assign FCP prio region since older adapters may not have FLT, or
6798c2ecf20Sopenharmony_ci	   FCP prio region in it's FLT.
6808c2ecf20Sopenharmony_ci	 */
6818c2ecf20Sopenharmony_ci	ha->flt_region_fcp_prio = (ha->port_no == 0) ?
6828c2ecf20Sopenharmony_ci	    fcp_prio_cfg0[def] : fcp_prio_cfg1[def];
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	ha->flt_region_flt = flt_addr;
6858c2ecf20Sopenharmony_ci	wptr = (__force __le16 *)ha->flt;
6868c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, flt, flt_addr << 2,
6878c2ecf20Sopenharmony_ci	    (sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE));
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (le16_to_cpu(*wptr) == 0xffff)
6908c2ecf20Sopenharmony_ci		goto no_flash_data;
6918c2ecf20Sopenharmony_ci	if (flt->version != cpu_to_le16(1)) {
6928c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x0047,
6938c2ecf20Sopenharmony_ci		    "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
6948c2ecf20Sopenharmony_ci		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
6958c2ecf20Sopenharmony_ci		    le16_to_cpu(flt->checksum));
6968c2ecf20Sopenharmony_ci		goto no_flash_data;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	cnt = (sizeof(*flt) + le16_to_cpu(flt->length)) / sizeof(*wptr);
7008c2ecf20Sopenharmony_ci	for (chksum = 0; cnt--; wptr++)
7018c2ecf20Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
7028c2ecf20Sopenharmony_ci	if (chksum) {
7038c2ecf20Sopenharmony_ci		ql_log(ql_log_fatal, vha, 0x0048,
7048c2ecf20Sopenharmony_ci		    "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
7058c2ecf20Sopenharmony_ci		    le16_to_cpu(flt->version), le16_to_cpu(flt->length),
7068c2ecf20Sopenharmony_ci		    le16_to_cpu(flt->checksum));
7078c2ecf20Sopenharmony_ci		goto no_flash_data;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	cnt = le16_to_cpu(flt->length) / sizeof(*region);
7118c2ecf20Sopenharmony_ci	for ( ; cnt; cnt--, region++) {
7128c2ecf20Sopenharmony_ci		/* Store addresses as DWORD offsets. */
7138c2ecf20Sopenharmony_ci		start = le32_to_cpu(region->start) >> 2;
7148c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0049,
7158c2ecf20Sopenharmony_ci		    "FLT[%#x]: start=%#x end=%#x size=%#x.\n",
7168c2ecf20Sopenharmony_ci		    le16_to_cpu(region->code), start,
7178c2ecf20Sopenharmony_ci		    le32_to_cpu(region->end) >> 2,
7188c2ecf20Sopenharmony_ci		    le32_to_cpu(region->size) >> 2);
7198c2ecf20Sopenharmony_ci		if (region->attribute)
7208c2ecf20Sopenharmony_ci			ql_log(ql_dbg_init, vha, 0xffff,
7218c2ecf20Sopenharmony_ci			    "Region %x is secure\n", region->code);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		switch (le16_to_cpu(region->code)) {
7248c2ecf20Sopenharmony_ci		case FLT_REG_FCOE_FW:
7258c2ecf20Sopenharmony_ci			if (!IS_QLA8031(ha))
7268c2ecf20Sopenharmony_ci				break;
7278c2ecf20Sopenharmony_ci			ha->flt_region_fw = start;
7288c2ecf20Sopenharmony_ci			break;
7298c2ecf20Sopenharmony_ci		case FLT_REG_FW:
7308c2ecf20Sopenharmony_ci			if (IS_QLA8031(ha))
7318c2ecf20Sopenharmony_ci				break;
7328c2ecf20Sopenharmony_ci			ha->flt_region_fw = start;
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		case FLT_REG_BOOT_CODE:
7358c2ecf20Sopenharmony_ci			ha->flt_region_boot = start;
7368c2ecf20Sopenharmony_ci			break;
7378c2ecf20Sopenharmony_ci		case FLT_REG_VPD_0:
7388c2ecf20Sopenharmony_ci			if (IS_QLA8031(ha))
7398c2ecf20Sopenharmony_ci				break;
7408c2ecf20Sopenharmony_ci			ha->flt_region_vpd_nvram = start;
7418c2ecf20Sopenharmony_ci			if (IS_P3P_TYPE(ha))
7428c2ecf20Sopenharmony_ci				break;
7438c2ecf20Sopenharmony_ci			if (ha->port_no == 0)
7448c2ecf20Sopenharmony_ci				ha->flt_region_vpd = start;
7458c2ecf20Sopenharmony_ci			break;
7468c2ecf20Sopenharmony_ci		case FLT_REG_VPD_1:
7478c2ecf20Sopenharmony_ci			if (IS_P3P_TYPE(ha) || IS_QLA8031(ha))
7488c2ecf20Sopenharmony_ci				break;
7498c2ecf20Sopenharmony_ci			if (ha->port_no == 1)
7508c2ecf20Sopenharmony_ci				ha->flt_region_vpd = start;
7518c2ecf20Sopenharmony_ci			break;
7528c2ecf20Sopenharmony_ci		case FLT_REG_VPD_2:
7538c2ecf20Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
7548c2ecf20Sopenharmony_ci				break;
7558c2ecf20Sopenharmony_ci			if (ha->port_no == 2)
7568c2ecf20Sopenharmony_ci				ha->flt_region_vpd = start;
7578c2ecf20Sopenharmony_ci			break;
7588c2ecf20Sopenharmony_ci		case FLT_REG_VPD_3:
7598c2ecf20Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
7608c2ecf20Sopenharmony_ci				break;
7618c2ecf20Sopenharmony_ci			if (ha->port_no == 3)
7628c2ecf20Sopenharmony_ci				ha->flt_region_vpd = start;
7638c2ecf20Sopenharmony_ci			break;
7648c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_0:
7658c2ecf20Sopenharmony_ci			if (IS_QLA8031(ha))
7668c2ecf20Sopenharmony_ci				break;
7678c2ecf20Sopenharmony_ci			if (ha->port_no == 0)
7688c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
7698c2ecf20Sopenharmony_ci			break;
7708c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_1:
7718c2ecf20Sopenharmony_ci			if (IS_QLA8031(ha))
7728c2ecf20Sopenharmony_ci				break;
7738c2ecf20Sopenharmony_ci			if (ha->port_no == 1)
7748c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
7758c2ecf20Sopenharmony_ci			break;
7768c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_2:
7778c2ecf20Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
7788c2ecf20Sopenharmony_ci				break;
7798c2ecf20Sopenharmony_ci			if (ha->port_no == 2)
7808c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
7818c2ecf20Sopenharmony_ci			break;
7828c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_3:
7838c2ecf20Sopenharmony_ci			if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
7848c2ecf20Sopenharmony_ci				break;
7858c2ecf20Sopenharmony_ci			if (ha->port_no == 3)
7868c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
7878c2ecf20Sopenharmony_ci			break;
7888c2ecf20Sopenharmony_ci		case FLT_REG_FDT:
7898c2ecf20Sopenharmony_ci			ha->flt_region_fdt = start;
7908c2ecf20Sopenharmony_ci			break;
7918c2ecf20Sopenharmony_ci		case FLT_REG_NPIV_CONF_0:
7928c2ecf20Sopenharmony_ci			if (ha->port_no == 0)
7938c2ecf20Sopenharmony_ci				ha->flt_region_npiv_conf = start;
7948c2ecf20Sopenharmony_ci			break;
7958c2ecf20Sopenharmony_ci		case FLT_REG_NPIV_CONF_1:
7968c2ecf20Sopenharmony_ci			if (ha->port_no == 1)
7978c2ecf20Sopenharmony_ci				ha->flt_region_npiv_conf = start;
7988c2ecf20Sopenharmony_ci			break;
7998c2ecf20Sopenharmony_ci		case FLT_REG_GOLD_FW:
8008c2ecf20Sopenharmony_ci			ha->flt_region_gold_fw = start;
8018c2ecf20Sopenharmony_ci			break;
8028c2ecf20Sopenharmony_ci		case FLT_REG_FCP_PRIO_0:
8038c2ecf20Sopenharmony_ci			if (ha->port_no == 0)
8048c2ecf20Sopenharmony_ci				ha->flt_region_fcp_prio = start;
8058c2ecf20Sopenharmony_ci			break;
8068c2ecf20Sopenharmony_ci		case FLT_REG_FCP_PRIO_1:
8078c2ecf20Sopenharmony_ci			if (ha->port_no == 1)
8088c2ecf20Sopenharmony_ci				ha->flt_region_fcp_prio = start;
8098c2ecf20Sopenharmony_ci			break;
8108c2ecf20Sopenharmony_ci		case FLT_REG_BOOT_CODE_82XX:
8118c2ecf20Sopenharmony_ci			ha->flt_region_boot = start;
8128c2ecf20Sopenharmony_ci			break;
8138c2ecf20Sopenharmony_ci		case FLT_REG_BOOT_CODE_8044:
8148c2ecf20Sopenharmony_ci			if (IS_QLA8044(ha))
8158c2ecf20Sopenharmony_ci				ha->flt_region_boot = start;
8168c2ecf20Sopenharmony_ci			break;
8178c2ecf20Sopenharmony_ci		case FLT_REG_FW_82XX:
8188c2ecf20Sopenharmony_ci			ha->flt_region_fw = start;
8198c2ecf20Sopenharmony_ci			break;
8208c2ecf20Sopenharmony_ci		case FLT_REG_CNA_FW:
8218c2ecf20Sopenharmony_ci			if (IS_CNA_CAPABLE(ha))
8228c2ecf20Sopenharmony_ci				ha->flt_region_fw = start;
8238c2ecf20Sopenharmony_ci			break;
8248c2ecf20Sopenharmony_ci		case FLT_REG_GOLD_FW_82XX:
8258c2ecf20Sopenharmony_ci			ha->flt_region_gold_fw = start;
8268c2ecf20Sopenharmony_ci			break;
8278c2ecf20Sopenharmony_ci		case FLT_REG_BOOTLOAD_82XX:
8288c2ecf20Sopenharmony_ci			ha->flt_region_bootload = start;
8298c2ecf20Sopenharmony_ci			break;
8308c2ecf20Sopenharmony_ci		case FLT_REG_VPD_8XXX:
8318c2ecf20Sopenharmony_ci			if (IS_CNA_CAPABLE(ha))
8328c2ecf20Sopenharmony_ci				ha->flt_region_vpd = start;
8338c2ecf20Sopenharmony_ci			break;
8348c2ecf20Sopenharmony_ci		case FLT_REG_FCOE_NVRAM_0:
8358c2ecf20Sopenharmony_ci			if (!(IS_QLA8031(ha) || IS_QLA8044(ha)))
8368c2ecf20Sopenharmony_ci				break;
8378c2ecf20Sopenharmony_ci			if (ha->port_no == 0)
8388c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
8398c2ecf20Sopenharmony_ci			break;
8408c2ecf20Sopenharmony_ci		case FLT_REG_FCOE_NVRAM_1:
8418c2ecf20Sopenharmony_ci			if (!(IS_QLA8031(ha) || IS_QLA8044(ha)))
8428c2ecf20Sopenharmony_ci				break;
8438c2ecf20Sopenharmony_ci			if (ha->port_no == 1)
8448c2ecf20Sopenharmony_ci				ha->flt_region_nvram = start;
8458c2ecf20Sopenharmony_ci			break;
8468c2ecf20Sopenharmony_ci		case FLT_REG_IMG_PRI_27XX:
8478c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8488c2ecf20Sopenharmony_ci				ha->flt_region_img_status_pri = start;
8498c2ecf20Sopenharmony_ci			break;
8508c2ecf20Sopenharmony_ci		case FLT_REG_IMG_SEC_27XX:
8518c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8528c2ecf20Sopenharmony_ci				ha->flt_region_img_status_sec = start;
8538c2ecf20Sopenharmony_ci			break;
8548c2ecf20Sopenharmony_ci		case FLT_REG_FW_SEC_27XX:
8558c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8568c2ecf20Sopenharmony_ci				ha->flt_region_fw_sec = start;
8578c2ecf20Sopenharmony_ci			break;
8588c2ecf20Sopenharmony_ci		case FLT_REG_BOOTLOAD_SEC_27XX:
8598c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8608c2ecf20Sopenharmony_ci				ha->flt_region_boot_sec = start;
8618c2ecf20Sopenharmony_ci			break;
8628c2ecf20Sopenharmony_ci		case FLT_REG_AUX_IMG_PRI_28XX:
8638c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8648c2ecf20Sopenharmony_ci				ha->flt_region_aux_img_status_pri = start;
8658c2ecf20Sopenharmony_ci			break;
8668c2ecf20Sopenharmony_ci		case FLT_REG_AUX_IMG_SEC_28XX:
8678c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8688c2ecf20Sopenharmony_ci				ha->flt_region_aux_img_status_sec = start;
8698c2ecf20Sopenharmony_ci			break;
8708c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_0:
8718c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8728c2ecf20Sopenharmony_ci				if (ha->port_no == 0)
8738c2ecf20Sopenharmony_ci					ha->flt_region_nvram_sec = start;
8748c2ecf20Sopenharmony_ci			break;
8758c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_1:
8768c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8778c2ecf20Sopenharmony_ci				if (ha->port_no == 1)
8788c2ecf20Sopenharmony_ci					ha->flt_region_nvram_sec = start;
8798c2ecf20Sopenharmony_ci			break;
8808c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_2:
8818c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8828c2ecf20Sopenharmony_ci				if (ha->port_no == 2)
8838c2ecf20Sopenharmony_ci					ha->flt_region_nvram_sec = start;
8848c2ecf20Sopenharmony_ci			break;
8858c2ecf20Sopenharmony_ci		case FLT_REG_NVRAM_SEC_28XX_3:
8868c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
8878c2ecf20Sopenharmony_ci				if (ha->port_no == 3)
8888c2ecf20Sopenharmony_ci					ha->flt_region_nvram_sec = start;
8898c2ecf20Sopenharmony_ci			break;
8908c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_0:
8918c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_0:
8928c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
8938c2ecf20Sopenharmony_ci				ha->flt_region_vpd_nvram_sec = start;
8948c2ecf20Sopenharmony_ci				if (ha->port_no == 0)
8958c2ecf20Sopenharmony_ci					ha->flt_region_vpd_sec = start;
8968c2ecf20Sopenharmony_ci			}
8978c2ecf20Sopenharmony_ci			break;
8988c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_1:
8998c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_1:
9008c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
9018c2ecf20Sopenharmony_ci				if (ha->port_no == 1)
9028c2ecf20Sopenharmony_ci					ha->flt_region_vpd_sec = start;
9038c2ecf20Sopenharmony_ci			break;
9048c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_2:
9058c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_2:
9068c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
9078c2ecf20Sopenharmony_ci				if (ha->port_no == 2)
9088c2ecf20Sopenharmony_ci					ha->flt_region_vpd_sec = start;
9098c2ecf20Sopenharmony_ci			break;
9108c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_27XX_3:
9118c2ecf20Sopenharmony_ci		case FLT_REG_VPD_SEC_28XX_3:
9128c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
9138c2ecf20Sopenharmony_ci				if (ha->port_no == 3)
9148c2ecf20Sopenharmony_ci					ha->flt_region_vpd_sec = start;
9158c2ecf20Sopenharmony_ci			break;
9168c2ecf20Sopenharmony_ci		}
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci	goto done;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cino_flash_data:
9218c2ecf20Sopenharmony_ci	/* Use hardcoded defaults. */
9228c2ecf20Sopenharmony_ci	loc = locations[0];
9238c2ecf20Sopenharmony_ci	ha->flt_region_fw = def_fw[def];
9248c2ecf20Sopenharmony_ci	ha->flt_region_boot = def_boot[def];
9258c2ecf20Sopenharmony_ci	ha->flt_region_vpd_nvram = def_vpd_nvram[def];
9268c2ecf20Sopenharmony_ci	ha->flt_region_vpd = (ha->port_no == 0) ?
9278c2ecf20Sopenharmony_ci	    def_vpd0[def] : def_vpd1[def];
9288c2ecf20Sopenharmony_ci	ha->flt_region_nvram = (ha->port_no == 0) ?
9298c2ecf20Sopenharmony_ci	    def_nvram0[def] : def_nvram1[def];
9308c2ecf20Sopenharmony_ci	ha->flt_region_fdt = def_fdt[def];
9318c2ecf20Sopenharmony_ci	ha->flt_region_npiv_conf = (ha->port_no == 0) ?
9328c2ecf20Sopenharmony_ci	    def_npiv_conf0[def] : def_npiv_conf1[def];
9338c2ecf20Sopenharmony_cidone:
9348c2ecf20Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004a,
9358c2ecf20Sopenharmony_ci	    "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x "
9368c2ecf20Sopenharmony_ci	    "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
9378c2ecf20Sopenharmony_ci	    loc, ha->flt_region_boot, ha->flt_region_fw,
9388c2ecf20Sopenharmony_ci	    ha->flt_region_vpd_nvram, ha->flt_region_vpd, ha->flt_region_nvram,
9398c2ecf20Sopenharmony_ci	    ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf,
9408c2ecf20Sopenharmony_ci	    ha->flt_region_fcp_prio);
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic void
9448c2ecf20Sopenharmony_ciqla2xxx_get_fdt_info(scsi_qla_host_t *vha)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci#define FLASH_BLK_SIZE_4K	0x1000
9478c2ecf20Sopenharmony_ci#define FLASH_BLK_SIZE_32K	0x8000
9488c2ecf20Sopenharmony_ci#define FLASH_BLK_SIZE_64K	0x10000
9498c2ecf20Sopenharmony_ci	const char *loc, *locations[] = { "MID", "FDT" };
9508c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
9518c2ecf20Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
9528c2ecf20Sopenharmony_ci	uint16_t cnt, chksum;
9538c2ecf20Sopenharmony_ci	__le16 *wptr = (__force __le16 *)req->ring;
9548c2ecf20Sopenharmony_ci	struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring;
9558c2ecf20Sopenharmony_ci	uint8_t	man_id, flash_id;
9568c2ecf20Sopenharmony_ci	uint16_t mid = 0, fid = 0;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, fdt, ha->flt_region_fdt << 2,
9598c2ecf20Sopenharmony_ci	    OPTROM_BURST_DWORDS);
9608c2ecf20Sopenharmony_ci	if (le16_to_cpu(*wptr) == 0xffff)
9618c2ecf20Sopenharmony_ci		goto no_flash_data;
9628c2ecf20Sopenharmony_ci	if (memcmp(fdt->sig, "QLID", 4))
9638c2ecf20Sopenharmony_ci		goto no_flash_data;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++)
9668c2ecf20Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
9678c2ecf20Sopenharmony_ci	if (chksum) {
9688c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x004c,
9698c2ecf20Sopenharmony_ci		    "Inconsistent FDT detected:"
9708c2ecf20Sopenharmony_ci		    " checksum=0x%x id=%c version0x%x.\n", chksum,
9718c2ecf20Sopenharmony_ci		    fdt->sig[0], le16_to_cpu(fdt->version));
9728c2ecf20Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
9738c2ecf20Sopenharmony_ci		    fdt, sizeof(*fdt));
9748c2ecf20Sopenharmony_ci		goto no_flash_data;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	loc = locations[1];
9788c2ecf20Sopenharmony_ci	mid = le16_to_cpu(fdt->man_id);
9798c2ecf20Sopenharmony_ci	fid = le16_to_cpu(fdt->id);
9808c2ecf20Sopenharmony_ci	ha->fdt_wrt_disable = fdt->wrt_disable_bits;
9818c2ecf20Sopenharmony_ci	ha->fdt_wrt_enable = fdt->wrt_enable_bits;
9828c2ecf20Sopenharmony_ci	ha->fdt_wrt_sts_reg_cmd = fdt->wrt_sts_reg_cmd;
9838c2ecf20Sopenharmony_ci	if (IS_QLA8044(ha))
9848c2ecf20Sopenharmony_ci		ha->fdt_erase_cmd = fdt->erase_cmd;
9858c2ecf20Sopenharmony_ci	else
9868c2ecf20Sopenharmony_ci		ha->fdt_erase_cmd =
9878c2ecf20Sopenharmony_ci		    flash_conf_addr(ha, 0x0300 | fdt->erase_cmd);
9888c2ecf20Sopenharmony_ci	ha->fdt_block_size = le32_to_cpu(fdt->block_size);
9898c2ecf20Sopenharmony_ci	if (fdt->unprotect_sec_cmd) {
9908c2ecf20Sopenharmony_ci		ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0300 |
9918c2ecf20Sopenharmony_ci		    fdt->unprotect_sec_cmd);
9928c2ecf20Sopenharmony_ci		ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ?
9938c2ecf20Sopenharmony_ci		    flash_conf_addr(ha, 0x0300 | fdt->protect_sec_cmd) :
9948c2ecf20Sopenharmony_ci		    flash_conf_addr(ha, 0x0336);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	goto done;
9978c2ecf20Sopenharmony_cino_flash_data:
9988c2ecf20Sopenharmony_ci	loc = locations[0];
9998c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha)) {
10008c2ecf20Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
10018c2ecf20Sopenharmony_ci		goto done;
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci	qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id);
10048c2ecf20Sopenharmony_ci	mid = man_id;
10058c2ecf20Sopenharmony_ci	fid = flash_id;
10068c2ecf20Sopenharmony_ci	ha->fdt_wrt_disable = 0x9c;
10078c2ecf20Sopenharmony_ci	ha->fdt_erase_cmd = flash_conf_addr(ha, 0x03d8);
10088c2ecf20Sopenharmony_ci	switch (man_id) {
10098c2ecf20Sopenharmony_ci	case 0xbf: /* STT flash. */
10108c2ecf20Sopenharmony_ci		if (flash_id == 0x8e)
10118c2ecf20Sopenharmony_ci			ha->fdt_block_size = FLASH_BLK_SIZE_64K;
10128c2ecf20Sopenharmony_ci		else
10138c2ecf20Sopenharmony_ci			ha->fdt_block_size = FLASH_BLK_SIZE_32K;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci		if (flash_id == 0x80)
10168c2ecf20Sopenharmony_ci			ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0352);
10178c2ecf20Sopenharmony_ci		break;
10188c2ecf20Sopenharmony_ci	case 0x13: /* ST M25P80. */
10198c2ecf20Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
10208c2ecf20Sopenharmony_ci		break;
10218c2ecf20Sopenharmony_ci	case 0x1f: /* Atmel 26DF081A. */
10228c2ecf20Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_4K;
10238c2ecf20Sopenharmony_ci		ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0320);
10248c2ecf20Sopenharmony_ci		ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0339);
10258c2ecf20Sopenharmony_ci		ha->fdt_protect_sec_cmd = flash_conf_addr(ha, 0x0336);
10268c2ecf20Sopenharmony_ci		break;
10278c2ecf20Sopenharmony_ci	default:
10288c2ecf20Sopenharmony_ci		/* Default to 64 kb sector size. */
10298c2ecf20Sopenharmony_ci		ha->fdt_block_size = FLASH_BLK_SIZE_64K;
10308c2ecf20Sopenharmony_ci		break;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_cidone:
10338c2ecf20Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004d,
10348c2ecf20Sopenharmony_ci	    "FDT[%s]: (0x%x/0x%x) erase=0x%x "
10358c2ecf20Sopenharmony_ci	    "pr=%x wrtd=0x%x blk=0x%x.\n",
10368c2ecf20Sopenharmony_ci	    loc, mid, fid,
10378c2ecf20Sopenharmony_ci	    ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd,
10388c2ecf20Sopenharmony_ci	    ha->fdt_wrt_disable, ha->fdt_block_size);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic void
10438c2ecf20Sopenharmony_ciqla2xxx_get_idc_param(scsi_qla_host_t *vha)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci#define QLA82XX_IDC_PARAM_ADDR       0x003e885c
10468c2ecf20Sopenharmony_ci	__le32 *wptr;
10478c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
10488c2ecf20Sopenharmony_ci	struct req_que *req = ha->req_q_map[0];
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (!(IS_P3P_TYPE(ha)))
10518c2ecf20Sopenharmony_ci		return;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	wptr = (__force __le32 *)req->ring;
10548c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, req->ring, QLA82XX_IDC_PARAM_ADDR, 8);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (*wptr == cpu_to_le32(0xffffffff)) {
10578c2ecf20Sopenharmony_ci		ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT;
10588c2ecf20Sopenharmony_ci		ha->fcoe_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT;
10598c2ecf20Sopenharmony_ci	} else {
10608c2ecf20Sopenharmony_ci		ha->fcoe_dev_init_timeout = le32_to_cpu(*wptr);
10618c2ecf20Sopenharmony_ci		wptr++;
10628c2ecf20Sopenharmony_ci		ha->fcoe_reset_timeout = le32_to_cpu(*wptr);
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci	ql_dbg(ql_dbg_init, vha, 0x004e,
10658c2ecf20Sopenharmony_ci	    "fcoe_dev_init_timeout=%d "
10668c2ecf20Sopenharmony_ci	    "fcoe_reset_timeout=%d.\n", ha->fcoe_dev_init_timeout,
10678c2ecf20Sopenharmony_ci	    ha->fcoe_reset_timeout);
10688c2ecf20Sopenharmony_ci	return;
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ciint
10728c2ecf20Sopenharmony_ciqla2xxx_get_flash_info(scsi_qla_host_t *vha)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	int ret;
10758c2ecf20Sopenharmony_ci	uint32_t flt_addr;
10768c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
10798c2ecf20Sopenharmony_ci	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) &&
10808c2ecf20Sopenharmony_ci	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
10818c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	ret = qla2xxx_find_flt_start(vha, &flt_addr);
10848c2ecf20Sopenharmony_ci	if (ret != QLA_SUCCESS)
10858c2ecf20Sopenharmony_ci		return ret;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	qla2xxx_get_flt_info(vha, flt_addr);
10888c2ecf20Sopenharmony_ci	qla2xxx_get_fdt_info(vha);
10898c2ecf20Sopenharmony_ci	qla2xxx_get_idc_param(vha);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_civoid
10958c2ecf20Sopenharmony_ciqla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci#define NPIV_CONFIG_SIZE	(16*1024)
10988c2ecf20Sopenharmony_ci	void *data;
10998c2ecf20Sopenharmony_ci	__le16 *wptr;
11008c2ecf20Sopenharmony_ci	uint16_t cnt, chksum;
11018c2ecf20Sopenharmony_ci	int i;
11028c2ecf20Sopenharmony_ci	struct qla_npiv_header hdr;
11038c2ecf20Sopenharmony_ci	struct qla_npiv_entry *entry;
11048c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
11078c2ecf20Sopenharmony_ci	    !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
11088c2ecf20Sopenharmony_ci		return;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	if (ha->flags.nic_core_reset_hdlr_active)
11118c2ecf20Sopenharmony_ci		return;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (IS_QLA8044(ha))
11148c2ecf20Sopenharmony_ci		return;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, &hdr, ha->flt_region_npiv_conf << 2,
11178c2ecf20Sopenharmony_ci	    sizeof(struct qla_npiv_header));
11188c2ecf20Sopenharmony_ci	if (hdr.version == cpu_to_le16(0xffff))
11198c2ecf20Sopenharmony_ci		return;
11208c2ecf20Sopenharmony_ci	if (hdr.version != cpu_to_le16(1)) {
11218c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7090,
11228c2ecf20Sopenharmony_ci		    "Unsupported NPIV-Config "
11238c2ecf20Sopenharmony_ci		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
11248c2ecf20Sopenharmony_ci		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
11258c2ecf20Sopenharmony_ci		    le16_to_cpu(hdr.checksum));
11268c2ecf20Sopenharmony_ci		return;
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL);
11308c2ecf20Sopenharmony_ci	if (!data) {
11318c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7091,
11328c2ecf20Sopenharmony_ci		    "Unable to allocate memory for data.\n");
11338c2ecf20Sopenharmony_ci		return;
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, data, ha->flt_region_npiv_conf << 2,
11378c2ecf20Sopenharmony_ci	    NPIV_CONFIG_SIZE);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	cnt = (sizeof(hdr) + le16_to_cpu(hdr.entries) * sizeof(*entry)) >> 1;
11408c2ecf20Sopenharmony_ci	for (wptr = data, chksum = 0; cnt--; wptr++)
11418c2ecf20Sopenharmony_ci		chksum += le16_to_cpu(*wptr);
11428c2ecf20Sopenharmony_ci	if (chksum) {
11438c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7092,
11448c2ecf20Sopenharmony_ci		    "Inconsistent NPIV-Config "
11458c2ecf20Sopenharmony_ci		    "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
11468c2ecf20Sopenharmony_ci		    le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
11478c2ecf20Sopenharmony_ci		    le16_to_cpu(hdr.checksum));
11488c2ecf20Sopenharmony_ci		goto done;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	entry = data + sizeof(struct qla_npiv_header);
11528c2ecf20Sopenharmony_ci	cnt = le16_to_cpu(hdr.entries);
11538c2ecf20Sopenharmony_ci	for (i = 0; cnt; cnt--, entry++, i++) {
11548c2ecf20Sopenharmony_ci		uint16_t flags;
11558c2ecf20Sopenharmony_ci		struct fc_vport_identifiers vid;
11568c2ecf20Sopenharmony_ci		struct fc_vport *vport;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci		memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci		flags = le16_to_cpu(entry->flags);
11618c2ecf20Sopenharmony_ci		if (flags == 0xffff)
11628c2ecf20Sopenharmony_ci			continue;
11638c2ecf20Sopenharmony_ci		if ((flags & BIT_0) == 0)
11648c2ecf20Sopenharmony_ci			continue;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci		memset(&vid, 0, sizeof(vid));
11678c2ecf20Sopenharmony_ci		vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
11688c2ecf20Sopenharmony_ci		vid.vport_type = FC_PORTTYPE_NPIV;
11698c2ecf20Sopenharmony_ci		vid.disable = false;
11708c2ecf20Sopenharmony_ci		vid.port_name = wwn_to_u64(entry->port_name);
11718c2ecf20Sopenharmony_ci		vid.node_name = wwn_to_u64(entry->node_name);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_user, vha, 0x7093,
11748c2ecf20Sopenharmony_ci		    "NPIV[%02x]: wwpn=%llx wwnn=%llx vf_id=%#x Q_qos=%#x F_qos=%#x.\n",
11758c2ecf20Sopenharmony_ci		    cnt, vid.port_name, vid.node_name,
11768c2ecf20Sopenharmony_ci		    le16_to_cpu(entry->vf_id),
11778c2ecf20Sopenharmony_ci		    entry->q_qos, entry->f_qos);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci		if (i < QLA_PRECONFIG_VPORTS) {
11808c2ecf20Sopenharmony_ci			vport = fc_vport_create(vha->host, 0, &vid);
11818c2ecf20Sopenharmony_ci			if (!vport)
11828c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0x7094,
11838c2ecf20Sopenharmony_ci				    "NPIV-Config Failed to create vport [%02x]: wwpn=%llx wwnn=%llx.\n",
11848c2ecf20Sopenharmony_ci				    cnt, vid.port_name, vid.node_name);
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_cidone:
11888c2ecf20Sopenharmony_ci	kfree(data);
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cistatic int
11928c2ecf20Sopenharmony_ciqla24xx_unprotect_flash(scsi_qla_host_t *vha)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
11958c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	if (ha->flags.fac_supported)
11988c2ecf20Sopenharmony_ci		return qla81xx_fac_do_write_enable(vha, 1);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	/* Enable flash write. */
12018c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
12028c2ecf20Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
12038c2ecf20Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	if (!ha->fdt_wrt_disable)
12068c2ecf20Sopenharmony_ci		goto done;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* Disable flash write-protection, first clear SR protection bit */
12098c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
12108c2ecf20Sopenharmony_ci	/* Then write zero again to clear remaining SR bits.*/
12118c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
12128c2ecf20Sopenharmony_cidone:
12138c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int
12178c2ecf20Sopenharmony_ciqla24xx_protect_flash(scsi_qla_host_t *vha)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
12208c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
12218c2ecf20Sopenharmony_ci	ulong cnt = 300;
12228c2ecf20Sopenharmony_ci	uint32_t faddr, dword;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	if (ha->flags.fac_supported)
12258c2ecf20Sopenharmony_ci		return qla81xx_fac_do_write_enable(vha, 0);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	if (!ha->fdt_wrt_disable)
12288c2ecf20Sopenharmony_ci		goto skip_wrt_protect;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	/* Enable flash write-protection and wait for completion. */
12318c2ecf20Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x101);
12328c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, faddr, ha->fdt_wrt_disable);
12338c2ecf20Sopenharmony_ci	faddr = flash_conf_addr(ha, 0x5);
12348c2ecf20Sopenharmony_ci	while (cnt--) {
12358c2ecf20Sopenharmony_ci		if (!qla24xx_read_flash_dword(ha, faddr, &dword)) {
12368c2ecf20Sopenharmony_ci			if (!(dword & BIT_0))
12378c2ecf20Sopenharmony_ci				break;
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci		udelay(10);
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ciskip_wrt_protect:
12438c2ecf20Sopenharmony_ci	/* Disable flash write. */
12448c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
12458c2ecf20Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic int
12518c2ecf20Sopenharmony_ciqla24xx_erase_sector(scsi_qla_host_t *vha, uint32_t fdata)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
12548c2ecf20Sopenharmony_ci	uint32_t start, finish;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	if (ha->flags.fac_supported) {
12578c2ecf20Sopenharmony_ci		start = fdata >> 2;
12588c2ecf20Sopenharmony_ci		finish = start + (ha->fdt_block_size >> 2) - 1;
12598c2ecf20Sopenharmony_ci		return qla81xx_fac_erase_sector(vha, flash_data_addr(ha,
12608c2ecf20Sopenharmony_ci		    start), flash_data_addr(ha, finish));
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	return qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd,
12648c2ecf20Sopenharmony_ci	    (fdata & 0xff00) | ((fdata << 16) & 0xff0000) |
12658c2ecf20Sopenharmony_ci	    ((fdata >> 16) & 0xff));
12668c2ecf20Sopenharmony_ci}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_cistatic int
12698c2ecf20Sopenharmony_ciqla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr,
12708c2ecf20Sopenharmony_ci    uint32_t dwords)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	int ret;
12738c2ecf20Sopenharmony_ci	ulong liter;
12748c2ecf20Sopenharmony_ci	ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
12758c2ecf20Sopenharmony_ci	uint32_t sec_mask, rest_addr, fdata;
12768c2ecf20Sopenharmony_ci	dma_addr_t optrom_dma;
12778c2ecf20Sopenharmony_ci	void *optrom = NULL;
12788c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) &&
12818c2ecf20Sopenharmony_ci	    !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
12828c2ecf20Sopenharmony_ci		goto next;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	/* Allocate dma buffer for burst write */
12858c2ecf20Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
12868c2ecf20Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
12878c2ecf20Sopenharmony_ci	if (!optrom) {
12888c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7095,
12898c2ecf20Sopenharmony_ci		    "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cinext:
12938c2ecf20Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
12948c2ecf20Sopenharmony_ci	    "Unprotect flash...\n");
12958c2ecf20Sopenharmony_ci	ret = qla24xx_unprotect_flash(vha);
12968c2ecf20Sopenharmony_ci	if (ret) {
12978c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7096,
12988c2ecf20Sopenharmony_ci		    "Failed to unprotect flash.\n");
12998c2ecf20Sopenharmony_ci		goto done;
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	rest_addr = (ha->fdt_block_size >> 2) - 1;
13038c2ecf20Sopenharmony_ci	sec_mask = ~rest_addr;
13048c2ecf20Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
13058c2ecf20Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci		/* Are we at the beginning of a sector? */
13088c2ecf20Sopenharmony_ci		if (!(faddr & rest_addr)) {
13098c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
13108c2ecf20Sopenharmony_ci			    "Erase sector %#x...\n", faddr);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci			ret = qla24xx_erase_sector(vha, fdata);
13138c2ecf20Sopenharmony_ci			if (ret) {
13148c2ecf20Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x7007,
13158c2ecf20Sopenharmony_ci				    "Failed to erase sector %x.\n", faddr);
13168c2ecf20Sopenharmony_ci				break;
13178c2ecf20Sopenharmony_ci			}
13188c2ecf20Sopenharmony_ci		}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		if (optrom) {
13218c2ecf20Sopenharmony_ci			/* If smaller than a burst remaining */
13228c2ecf20Sopenharmony_ci			if (dwords - liter < dburst)
13238c2ecf20Sopenharmony_ci				dburst = dwords - liter;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci			/* Copy to dma buffer */
13268c2ecf20Sopenharmony_ci			memcpy(optrom, dwptr, dburst << 2);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci			/* Burst write */
13298c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
13308c2ecf20Sopenharmony_ci			    "Write burst (%#lx dwords)...\n", dburst);
13318c2ecf20Sopenharmony_ci			ret = qla2x00_load_ram(vha, optrom_dma,
13328c2ecf20Sopenharmony_ci			    flash_data_addr(ha, faddr), dburst);
13338c2ecf20Sopenharmony_ci			if (!ret) {
13348c2ecf20Sopenharmony_ci				liter += dburst - 1;
13358c2ecf20Sopenharmony_ci				faddr += dburst - 1;
13368c2ecf20Sopenharmony_ci				dwptr += dburst - 1;
13378c2ecf20Sopenharmony_ci				continue;
13388c2ecf20Sopenharmony_ci			}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7097,
13418c2ecf20Sopenharmony_ci			    "Failed burst-write at %x (%p/%#llx)....\n",
13428c2ecf20Sopenharmony_ci			    flash_data_addr(ha, faddr), optrom,
13438c2ecf20Sopenharmony_ci			    (u64)optrom_dma);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci			dma_free_coherent(&ha->pdev->dev,
13468c2ecf20Sopenharmony_ci			    OPTROM_BURST_SIZE, optrom, optrom_dma);
13478c2ecf20Sopenharmony_ci			optrom = NULL;
13488c2ecf20Sopenharmony_ci			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
13498c2ecf20Sopenharmony_ci				break;
13508c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7098,
13518c2ecf20Sopenharmony_ci			    "Reverting to slow write...\n");
13528c2ecf20Sopenharmony_ci		}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci		/* Slow write */
13558c2ecf20Sopenharmony_ci		ret = qla24xx_write_flash_dword(ha,
13568c2ecf20Sopenharmony_ci		    flash_data_addr(ha, faddr), le32_to_cpu(*dwptr));
13578c2ecf20Sopenharmony_ci		if (ret) {
13588c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x7006,
13598c2ecf20Sopenharmony_ci			    "Failed slow write %x (%x)\n", faddr, *dwptr);
13608c2ecf20Sopenharmony_ci			break;
13618c2ecf20Sopenharmony_ci		}
13628c2ecf20Sopenharmony_ci	}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
13658c2ecf20Sopenharmony_ci	    "Protect flash...\n");
13668c2ecf20Sopenharmony_ci	ret = qla24xx_protect_flash(vha);
13678c2ecf20Sopenharmony_ci	if (ret)
13688c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7099,
13698c2ecf20Sopenharmony_ci		    "Failed to protect flash\n");
13708c2ecf20Sopenharmony_cidone:
13718c2ecf20Sopenharmony_ci	if (optrom)
13728c2ecf20Sopenharmony_ci		dma_free_coherent(&ha->pdev->dev,
13738c2ecf20Sopenharmony_ci		    OPTROM_BURST_SIZE, optrom, optrom_dma);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	return ret;
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ciuint8_t *
13798c2ecf20Sopenharmony_ciqla2x00_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
13808c2ecf20Sopenharmony_ci    uint32_t bytes)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci	uint32_t i;
13838c2ecf20Sopenharmony_ci	__le16 *wptr;
13848c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	/* Word reads to NVRAM via registers. */
13878c2ecf20Sopenharmony_ci	wptr = buf;
13888c2ecf20Sopenharmony_ci	qla2x00_lock_nvram_access(ha);
13898c2ecf20Sopenharmony_ci	for (i = 0; i < bytes >> 1; i++, naddr++)
13908c2ecf20Sopenharmony_ci		wptr[i] = cpu_to_le16(qla2x00_get_nvram_word(ha,
13918c2ecf20Sopenharmony_ci		    naddr));
13928c2ecf20Sopenharmony_ci	qla2x00_unlock_nvram_access(ha);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	return buf;
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ciuint8_t *
13988c2ecf20Sopenharmony_ciqla24xx_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
13998c2ecf20Sopenharmony_ci    uint32_t bytes)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
14028c2ecf20Sopenharmony_ci	uint32_t *dwptr = buf;
14038c2ecf20Sopenharmony_ci	uint32_t i;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
14068c2ecf20Sopenharmony_ci		return  buf;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	/* Dword reads to flash. */
14098c2ecf20Sopenharmony_ci	naddr = nvram_data_addr(ha, naddr);
14108c2ecf20Sopenharmony_ci	bytes >>= 2;
14118c2ecf20Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
14128c2ecf20Sopenharmony_ci		if (qla24xx_read_flash_dword(ha, naddr, dwptr))
14138c2ecf20Sopenharmony_ci			break;
14148c2ecf20Sopenharmony_ci		cpu_to_le32s(dwptr);
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	return buf;
14188c2ecf20Sopenharmony_ci}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ciint
14218c2ecf20Sopenharmony_ciqla2x00_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
14228c2ecf20Sopenharmony_ci    uint32_t bytes)
14238c2ecf20Sopenharmony_ci{
14248c2ecf20Sopenharmony_ci	int ret, stat;
14258c2ecf20Sopenharmony_ci	uint32_t i;
14268c2ecf20Sopenharmony_ci	uint16_t *wptr;
14278c2ecf20Sopenharmony_ci	unsigned long flags;
14288c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	ret = QLA_SUCCESS;
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
14338c2ecf20Sopenharmony_ci	qla2x00_lock_nvram_access(ha);
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	/* Disable NVRAM write-protection. */
14368c2ecf20Sopenharmony_ci	stat = qla2x00_clear_nvram_protection(ha);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	wptr = (uint16_t *)buf;
14398c2ecf20Sopenharmony_ci	for (i = 0; i < bytes >> 1; i++, naddr++) {
14408c2ecf20Sopenharmony_ci		qla2x00_write_nvram_word(ha, naddr,
14418c2ecf20Sopenharmony_ci		    cpu_to_le16(*wptr));
14428c2ecf20Sopenharmony_ci		wptr++;
14438c2ecf20Sopenharmony_ci	}
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	/* Enable NVRAM write-protection. */
14468c2ecf20Sopenharmony_ci	qla2x00_set_nvram_protection(ha, stat);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	qla2x00_unlock_nvram_access(ha);
14498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	return ret;
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ciint
14558c2ecf20Sopenharmony_ciqla24xx_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
14568c2ecf20Sopenharmony_ci    uint32_t bytes)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
14598c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
14608c2ecf20Sopenharmony_ci	__le32 *dwptr = buf;
14618c2ecf20Sopenharmony_ci	uint32_t i;
14628c2ecf20Sopenharmony_ci	int ret;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	ret = QLA_SUCCESS;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
14678c2ecf20Sopenharmony_ci		return ret;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	/* Enable flash write. */
14708c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
14718c2ecf20Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
14728c2ecf20Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	/* Disable NVRAM write-protection. */
14758c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
14768c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	/* Dword writes to flash. */
14798c2ecf20Sopenharmony_ci	naddr = nvram_data_addr(ha, naddr);
14808c2ecf20Sopenharmony_ci	bytes >>= 2;
14818c2ecf20Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
14828c2ecf20Sopenharmony_ci		if (qla24xx_write_flash_dword(ha, naddr, le32_to_cpu(*dwptr))) {
14838c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_user, vha, 0x709a,
14848c2ecf20Sopenharmony_ci			    "Unable to program nvram address=%x data=%x.\n",
14858c2ecf20Sopenharmony_ci			    naddr, *dwptr);
14868c2ecf20Sopenharmony_ci			break;
14878c2ecf20Sopenharmony_ci		}
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	/* Enable NVRAM write-protection. */
14918c2ecf20Sopenharmony_ci	qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0x8c);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	/* Disable flash write. */
14948c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->ctrl_status,
14958c2ecf20Sopenharmony_ci	    rd_reg_dword(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
14968c2ecf20Sopenharmony_ci	rd_reg_dword(&reg->ctrl_status);	/* PCI Posting. */
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	return ret;
14998c2ecf20Sopenharmony_ci}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ciuint8_t *
15028c2ecf20Sopenharmony_ciqla25xx_read_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
15038c2ecf20Sopenharmony_ci    uint32_t bytes)
15048c2ecf20Sopenharmony_ci{
15058c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
15068c2ecf20Sopenharmony_ci	uint32_t *dwptr = buf;
15078c2ecf20Sopenharmony_ci	uint32_t i;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	/* Dword reads to flash. */
15108c2ecf20Sopenharmony_ci	naddr = flash_data_addr(ha, ha->flt_region_vpd_nvram | naddr);
15118c2ecf20Sopenharmony_ci	bytes >>= 2;
15128c2ecf20Sopenharmony_ci	for (i = 0; i < bytes; i++, naddr++, dwptr++) {
15138c2ecf20Sopenharmony_ci		if (qla24xx_read_flash_dword(ha, naddr, dwptr))
15148c2ecf20Sopenharmony_ci			break;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci		cpu_to_le32s(dwptr);
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	return buf;
15208c2ecf20Sopenharmony_ci}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci#define RMW_BUFFER_SIZE	(64 * 1024)
15238c2ecf20Sopenharmony_ciint
15248c2ecf20Sopenharmony_ciqla25xx_write_nvram_data(scsi_qla_host_t *vha, void *buf, uint32_t naddr,
15258c2ecf20Sopenharmony_ci    uint32_t bytes)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
15288c2ecf20Sopenharmony_ci	uint8_t *dbuf = vmalloc(RMW_BUFFER_SIZE);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	if (!dbuf)
15318c2ecf20Sopenharmony_ci		return QLA_MEMORY_ALLOC_FAILED;
15328c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
15338c2ecf20Sopenharmony_ci	    RMW_BUFFER_SIZE);
15348c2ecf20Sopenharmony_ci	memcpy(dbuf + (naddr << 2), buf, bytes);
15358c2ecf20Sopenharmony_ci	ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
15368c2ecf20Sopenharmony_ci	    RMW_BUFFER_SIZE);
15378c2ecf20Sopenharmony_ci	vfree(dbuf);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic inline void
15438c2ecf20Sopenharmony_ciqla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	if (IS_QLA2322(ha)) {
15468c2ecf20Sopenharmony_ci		/* Flip all colors. */
15478c2ecf20Sopenharmony_ci		if (ha->beacon_color_state == QLA_LED_ALL_ON) {
15488c2ecf20Sopenharmony_ci			/* Turn off. */
15498c2ecf20Sopenharmony_ci			ha->beacon_color_state = 0;
15508c2ecf20Sopenharmony_ci			*pflags = GPIO_LED_ALL_OFF;
15518c2ecf20Sopenharmony_ci		} else {
15528c2ecf20Sopenharmony_ci			/* Turn on. */
15538c2ecf20Sopenharmony_ci			ha->beacon_color_state = QLA_LED_ALL_ON;
15548c2ecf20Sopenharmony_ci			*pflags = GPIO_LED_RGA_ON;
15558c2ecf20Sopenharmony_ci		}
15568c2ecf20Sopenharmony_ci	} else {
15578c2ecf20Sopenharmony_ci		/* Flip green led only. */
15588c2ecf20Sopenharmony_ci		if (ha->beacon_color_state == QLA_LED_GRN_ON) {
15598c2ecf20Sopenharmony_ci			/* Turn off. */
15608c2ecf20Sopenharmony_ci			ha->beacon_color_state = 0;
15618c2ecf20Sopenharmony_ci			*pflags = GPIO_LED_GREEN_OFF_AMBER_OFF;
15628c2ecf20Sopenharmony_ci		} else {
15638c2ecf20Sopenharmony_ci			/* Turn on. */
15648c2ecf20Sopenharmony_ci			ha->beacon_color_state = QLA_LED_GRN_ON;
15658c2ecf20Sopenharmony_ci			*pflags = GPIO_LED_GREEN_ON_AMBER_OFF;
15668c2ecf20Sopenharmony_ci		}
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci#define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r))
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_civoid
15738c2ecf20Sopenharmony_ciqla2x00_beacon_blink(struct scsi_qla_host *vha)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	uint16_t gpio_enable;
15768c2ecf20Sopenharmony_ci	uint16_t gpio_data;
15778c2ecf20Sopenharmony_ci	uint16_t led_color = 0;
15788c2ecf20Sopenharmony_ci	unsigned long flags;
15798c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
15808c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
15838c2ecf20Sopenharmony_ci		return;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/* Save the Original GPIOE. */
15888c2ecf20Sopenharmony_ci	if (ha->pio_address) {
15898c2ecf20Sopenharmony_ci		gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
15908c2ecf20Sopenharmony_ci		gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
15918c2ecf20Sopenharmony_ci	} else {
15928c2ecf20Sopenharmony_ci		gpio_enable = rd_reg_word(&reg->gpioe);
15938c2ecf20Sopenharmony_ci		gpio_data = rd_reg_word(&reg->gpiod);
15948c2ecf20Sopenharmony_ci	}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	/* Set the modified gpio_enable values */
15978c2ecf20Sopenharmony_ci	gpio_enable |= GPIO_LED_MASK;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	if (ha->pio_address) {
16008c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
16018c2ecf20Sopenharmony_ci	} else {
16028c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->gpioe, gpio_enable);
16038c2ecf20Sopenharmony_ci		rd_reg_word(&reg->gpioe);
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	qla2x00_flip_colors(ha, &led_color);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	/* Clear out any previously set LED color. */
16098c2ecf20Sopenharmony_ci	gpio_data &= ~GPIO_LED_MASK;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	/* Set the new input LED color to GPIOD. */
16128c2ecf20Sopenharmony_ci	gpio_data |= led_color;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/* Set the modified gpio_data values */
16158c2ecf20Sopenharmony_ci	if (ha->pio_address) {
16168c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
16178c2ecf20Sopenharmony_ci	} else {
16188c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->gpiod, gpio_data);
16198c2ecf20Sopenharmony_ci		rd_reg_word(&reg->gpiod);
16208c2ecf20Sopenharmony_ci	}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ciint
16268c2ecf20Sopenharmony_ciqla2x00_beacon_on(struct scsi_qla_host *vha)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	uint16_t gpio_enable;
16298c2ecf20Sopenharmony_ci	uint16_t gpio_data;
16308c2ecf20Sopenharmony_ci	unsigned long flags;
16318c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
16328c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
16358c2ecf20Sopenharmony_ci	ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
16388c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x709b,
16398c2ecf20Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
16408c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	/* Turn off LEDs. */
16448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
16458c2ecf20Sopenharmony_ci	if (ha->pio_address) {
16468c2ecf20Sopenharmony_ci		gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
16478c2ecf20Sopenharmony_ci		gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
16488c2ecf20Sopenharmony_ci	} else {
16498c2ecf20Sopenharmony_ci		gpio_enable = rd_reg_word(&reg->gpioe);
16508c2ecf20Sopenharmony_ci		gpio_data = rd_reg_word(&reg->gpiod);
16518c2ecf20Sopenharmony_ci	}
16528c2ecf20Sopenharmony_ci	gpio_enable |= GPIO_LED_MASK;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	/* Set the modified gpio_enable values. */
16558c2ecf20Sopenharmony_ci	if (ha->pio_address) {
16568c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
16578c2ecf20Sopenharmony_ci	} else {
16588c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->gpioe, gpio_enable);
16598c2ecf20Sopenharmony_ci		rd_reg_word(&reg->gpioe);
16608c2ecf20Sopenharmony_ci	}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	/* Clear out previously set LED colour. */
16638c2ecf20Sopenharmony_ci	gpio_data &= ~GPIO_LED_MASK;
16648c2ecf20Sopenharmony_ci	if (ha->pio_address) {
16658c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
16668c2ecf20Sopenharmony_ci	} else {
16678c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->gpiod, gpio_data);
16688c2ecf20Sopenharmony_ci		rd_reg_word(&reg->gpiod);
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	/*
16738c2ecf20Sopenharmony_ci	 * Let the per HBA timer kick off the blinking process based on
16748c2ecf20Sopenharmony_ci	 * the following flags. No need to do anything else now.
16758c2ecf20Sopenharmony_ci	 */
16768c2ecf20Sopenharmony_ci	ha->beacon_blink_led = 1;
16778c2ecf20Sopenharmony_ci	ha->beacon_color_state = 0;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
16808c2ecf20Sopenharmony_ci}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ciint
16838c2ecf20Sopenharmony_ciqla2x00_beacon_off(struct scsi_qla_host *vha)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	int rval = QLA_SUCCESS;
16868c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	ha->beacon_blink_led = 0;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	/* Set the on flag so when it gets flipped it will be off. */
16918c2ecf20Sopenharmony_ci	if (IS_QLA2322(ha))
16928c2ecf20Sopenharmony_ci		ha->beacon_color_state = QLA_LED_ALL_ON;
16938c2ecf20Sopenharmony_ci	else
16948c2ecf20Sopenharmony_ci		ha->beacon_color_state = QLA_LED_GRN_ON;
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	ha->isp_ops->beacon_blink(vha);	/* This turns green LED off */
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
16998c2ecf20Sopenharmony_ci	ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	rval = qla2x00_set_fw_options(vha, ha->fw_options);
17028c2ecf20Sopenharmony_ci	if (rval != QLA_SUCCESS)
17038c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x709c,
17048c2ecf20Sopenharmony_ci		    "Unable to update fw options (beacon off).\n");
17058c2ecf20Sopenharmony_ci	return rval;
17068c2ecf20Sopenharmony_ci}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_cistatic inline void
17108c2ecf20Sopenharmony_ciqla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
17118c2ecf20Sopenharmony_ci{
17128c2ecf20Sopenharmony_ci	/* Flip all colors. */
17138c2ecf20Sopenharmony_ci	if (ha->beacon_color_state == QLA_LED_ALL_ON) {
17148c2ecf20Sopenharmony_ci		/* Turn off. */
17158c2ecf20Sopenharmony_ci		ha->beacon_color_state = 0;
17168c2ecf20Sopenharmony_ci		*pflags = 0;
17178c2ecf20Sopenharmony_ci	} else {
17188c2ecf20Sopenharmony_ci		/* Turn on. */
17198c2ecf20Sopenharmony_ci		ha->beacon_color_state = QLA_LED_ALL_ON;
17208c2ecf20Sopenharmony_ci		*pflags = GPDX_LED_YELLOW_ON | GPDX_LED_AMBER_ON;
17218c2ecf20Sopenharmony_ci	}
17228c2ecf20Sopenharmony_ci}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_civoid
17258c2ecf20Sopenharmony_ciqla24xx_beacon_blink(struct scsi_qla_host *vha)
17268c2ecf20Sopenharmony_ci{
17278c2ecf20Sopenharmony_ci	uint16_t led_color = 0;
17288c2ecf20Sopenharmony_ci	uint32_t gpio_data;
17298c2ecf20Sopenharmony_ci	unsigned long flags;
17308c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
17318c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	/* Save the Original GPIOD. */
17348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
17358c2ecf20Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	/* Enable the gpio_data reg for update. */
17388c2ecf20Sopenharmony_ci	gpio_data |= GPDX_LED_UPDATE_MASK;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
17418c2ecf20Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	/* Set the color bits. */
17448c2ecf20Sopenharmony_ci	qla24xx_flip_colors(ha, &led_color);
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	/* Clear out any previously set LED color. */
17478c2ecf20Sopenharmony_ci	gpio_data &= ~GPDX_LED_COLOR_MASK;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	/* Set the new input LED color to GPIOD. */
17508c2ecf20Sopenharmony_ci	gpio_data |= led_color;
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	/* Set the modified gpio_data values. */
17538c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
17548c2ecf20Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
17558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
17568c2ecf20Sopenharmony_ci}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_cistatic uint32_t
17598c2ecf20Sopenharmony_ciqla83xx_select_led_port(struct qla_hw_data *ha)
17608c2ecf20Sopenharmony_ci{
17618c2ecf20Sopenharmony_ci	uint32_t led_select_value = 0;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))
17648c2ecf20Sopenharmony_ci		goto out;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	if (ha->port_no == 0)
17678c2ecf20Sopenharmony_ci		led_select_value = QLA83XX_LED_PORT0;
17688c2ecf20Sopenharmony_ci	else
17698c2ecf20Sopenharmony_ci		led_select_value = QLA83XX_LED_PORT1;
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ciout:
17728c2ecf20Sopenharmony_ci	return led_select_value;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_civoid
17768c2ecf20Sopenharmony_ciqla83xx_beacon_blink(struct scsi_qla_host *vha)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	uint32_t led_select_value;
17798c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
17808c2ecf20Sopenharmony_ci	uint16_t led_cfg[6];
17818c2ecf20Sopenharmony_ci	uint16_t orig_led_cfg[6];
17828c2ecf20Sopenharmony_ci	uint32_t led_10_value, led_43_value;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha) && !IS_QLA27XX(ha) &&
17858c2ecf20Sopenharmony_ci	    !IS_QLA28XX(ha))
17868c2ecf20Sopenharmony_ci		return;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	if (!ha->beacon_blink_led)
17898c2ecf20Sopenharmony_ci		return;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
17928c2ecf20Sopenharmony_ci		qla2x00_write_ram_word(vha, 0x1003, 0x40000230);
17938c2ecf20Sopenharmony_ci		qla2x00_write_ram_word(vha, 0x1004, 0x40000230);
17948c2ecf20Sopenharmony_ci	} else if (IS_QLA2031(ha)) {
17958c2ecf20Sopenharmony_ci		led_select_value = qla83xx_select_led_port(ha);
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x40000230);
17988c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value + 4, 0x40000230);
17998c2ecf20Sopenharmony_ci	} else if (IS_QLA8031(ha)) {
18008c2ecf20Sopenharmony_ci		led_select_value = qla83xx_select_led_port(ha);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci		qla83xx_rd_reg(vha, led_select_value, &led_10_value);
18038c2ecf20Sopenharmony_ci		qla83xx_rd_reg(vha, led_select_value + 0x10, &led_43_value);
18048c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x01f44000);
18058c2ecf20Sopenharmony_ci		msleep(500);
18068c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, 0x400001f4);
18078c2ecf20Sopenharmony_ci		msleep(1000);
18088c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value, led_10_value);
18098c2ecf20Sopenharmony_ci		qla83xx_wr_reg(vha, led_select_value + 0x10, led_43_value);
18108c2ecf20Sopenharmony_ci	} else if (IS_QLA81XX(ha)) {
18118c2ecf20Sopenharmony_ci		int rval;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci		/* Save Current */
18148c2ecf20Sopenharmony_ci		rval = qla81xx_get_led_config(vha, orig_led_cfg);
18158c2ecf20Sopenharmony_ci		/* Do the blink */
18168c2ecf20Sopenharmony_ci		if (rval == QLA_SUCCESS) {
18178c2ecf20Sopenharmony_ci			if (IS_QLA81XX(ha)) {
18188c2ecf20Sopenharmony_ci				led_cfg[0] = 0x4000;
18198c2ecf20Sopenharmony_ci				led_cfg[1] = 0x2000;
18208c2ecf20Sopenharmony_ci				led_cfg[2] = 0;
18218c2ecf20Sopenharmony_ci				led_cfg[3] = 0;
18228c2ecf20Sopenharmony_ci				led_cfg[4] = 0;
18238c2ecf20Sopenharmony_ci				led_cfg[5] = 0;
18248c2ecf20Sopenharmony_ci			} else {
18258c2ecf20Sopenharmony_ci				led_cfg[0] = 0x4000;
18268c2ecf20Sopenharmony_ci				led_cfg[1] = 0x4000;
18278c2ecf20Sopenharmony_ci				led_cfg[2] = 0x4000;
18288c2ecf20Sopenharmony_ci				led_cfg[3] = 0x2000;
18298c2ecf20Sopenharmony_ci				led_cfg[4] = 0;
18308c2ecf20Sopenharmony_ci				led_cfg[5] = 0x2000;
18318c2ecf20Sopenharmony_ci			}
18328c2ecf20Sopenharmony_ci			rval = qla81xx_set_led_config(vha, led_cfg);
18338c2ecf20Sopenharmony_ci			msleep(1000);
18348c2ecf20Sopenharmony_ci			if (IS_QLA81XX(ha)) {
18358c2ecf20Sopenharmony_ci				led_cfg[0] = 0x4000;
18368c2ecf20Sopenharmony_ci				led_cfg[1] = 0x2000;
18378c2ecf20Sopenharmony_ci				led_cfg[2] = 0;
18388c2ecf20Sopenharmony_ci			} else {
18398c2ecf20Sopenharmony_ci				led_cfg[0] = 0x4000;
18408c2ecf20Sopenharmony_ci				led_cfg[1] = 0x2000;
18418c2ecf20Sopenharmony_ci				led_cfg[2] = 0x4000;
18428c2ecf20Sopenharmony_ci				led_cfg[3] = 0x4000;
18438c2ecf20Sopenharmony_ci				led_cfg[4] = 0;
18448c2ecf20Sopenharmony_ci				led_cfg[5] = 0x2000;
18458c2ecf20Sopenharmony_ci			}
18468c2ecf20Sopenharmony_ci			rval = qla81xx_set_led_config(vha, led_cfg);
18478c2ecf20Sopenharmony_ci		}
18488c2ecf20Sopenharmony_ci		/* On exit, restore original (presumes no status change) */
18498c2ecf20Sopenharmony_ci		qla81xx_set_led_config(vha, orig_led_cfg);
18508c2ecf20Sopenharmony_ci	}
18518c2ecf20Sopenharmony_ci}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ciint
18548c2ecf20Sopenharmony_ciqla24xx_beacon_on(struct scsi_qla_host *vha)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	uint32_t gpio_data;
18578c2ecf20Sopenharmony_ci	unsigned long flags;
18588c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
18598c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
18628c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (IS_QLA8031(ha) || IS_QLA81XX(ha))
18658c2ecf20Sopenharmony_ci		goto skip_gpio; /* let blink handle it */
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (ha->beacon_blink_led == 0) {
18688c2ecf20Sopenharmony_ci		/* Enable firmware for update */
18698c2ecf20Sopenharmony_ci		ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci		if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS)
18728c2ecf20Sopenharmony_ci			return QLA_FUNCTION_FAILED;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci		if (qla2x00_get_fw_options(vha, ha->fw_options) !=
18758c2ecf20Sopenharmony_ci		    QLA_SUCCESS) {
18768c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7009,
18778c2ecf20Sopenharmony_ci			    "Unable to update fw options (beacon on).\n");
18788c2ecf20Sopenharmony_ci			return QLA_FUNCTION_FAILED;
18798c2ecf20Sopenharmony_ci		}
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci		if (IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
18828c2ecf20Sopenharmony_ci			goto skip_gpio;
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
18858c2ecf20Sopenharmony_ci		gpio_data = rd_reg_dword(&reg->gpiod);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci		/* Enable the gpio_data reg for update. */
18888c2ecf20Sopenharmony_ci		gpio_data |= GPDX_LED_UPDATE_MASK;
18898c2ecf20Sopenharmony_ci		wrt_reg_dword(&reg->gpiod, gpio_data);
18908c2ecf20Sopenharmony_ci		rd_reg_dword(&reg->gpiod);
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
18938c2ecf20Sopenharmony_ci	}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	/* So all colors blink together. */
18968c2ecf20Sopenharmony_ci	ha->beacon_color_state = 0;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ciskip_gpio:
18998c2ecf20Sopenharmony_ci	/* Let the per HBA timer kick off the blinking process. */
19008c2ecf20Sopenharmony_ci	ha->beacon_blink_led = 1;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
19038c2ecf20Sopenharmony_ci}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ciint
19068c2ecf20Sopenharmony_ciqla24xx_beacon_off(struct scsi_qla_host *vha)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	uint32_t gpio_data;
19098c2ecf20Sopenharmony_ci	unsigned long flags;
19108c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
19118c2ecf20Sopenharmony_ci	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
19148c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	if (!ha->flags.fw_started)
19178c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	ha->beacon_blink_led = 0;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	if (IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
19228c2ecf20Sopenharmony_ci		goto set_fw_options;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	if (IS_QLA8031(ha) || IS_QLA81XX(ha))
19258c2ecf20Sopenharmony_ci		return QLA_SUCCESS;
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	ha->beacon_color_state = QLA_LED_ALL_ON;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	ha->isp_ops->beacon_blink(vha);	/* Will flip to all off. */
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	/* Give control back to firmware. */
19328c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
19338c2ecf20Sopenharmony_ci	gpio_data = rd_reg_dword(&reg->gpiod);
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	/* Disable the gpio_data reg for update. */
19368c2ecf20Sopenharmony_ci	gpio_data &= ~GPDX_LED_UPDATE_MASK;
19378c2ecf20Sopenharmony_ci	wrt_reg_dword(&reg->gpiod, gpio_data);
19388c2ecf20Sopenharmony_ci	rd_reg_dword(&reg->gpiod);
19398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ciset_fw_options:
19428c2ecf20Sopenharmony_ci	ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
19458c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x704d,
19468c2ecf20Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
19478c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
19488c2ecf20Sopenharmony_ci	}
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
19518c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x704e,
19528c2ecf20Sopenharmony_ci		    "Unable to update fw options (beacon on).\n");
19538c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
19548c2ecf20Sopenharmony_ci	}
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
19578c2ecf20Sopenharmony_ci}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci/*
19618c2ecf20Sopenharmony_ci * Flash support routines
19628c2ecf20Sopenharmony_ci */
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci/**
19658c2ecf20Sopenharmony_ci * qla2x00_flash_enable() - Setup flash for reading and writing.
19668c2ecf20Sopenharmony_ci * @ha: HA context
19678c2ecf20Sopenharmony_ci */
19688c2ecf20Sopenharmony_cistatic void
19698c2ecf20Sopenharmony_ciqla2x00_flash_enable(struct qla_hw_data *ha)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	uint16_t data;
19728c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	data = rd_reg_word(&reg->ctrl_status);
19758c2ecf20Sopenharmony_ci	data |= CSR_FLASH_ENABLE;
19768c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, data);
19778c2ecf20Sopenharmony_ci	rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
19788c2ecf20Sopenharmony_ci}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci/**
19818c2ecf20Sopenharmony_ci * qla2x00_flash_disable() - Disable flash and allow RISC to run.
19828c2ecf20Sopenharmony_ci * @ha: HA context
19838c2ecf20Sopenharmony_ci */
19848c2ecf20Sopenharmony_cistatic void
19858c2ecf20Sopenharmony_ciqla2x00_flash_disable(struct qla_hw_data *ha)
19868c2ecf20Sopenharmony_ci{
19878c2ecf20Sopenharmony_ci	uint16_t data;
19888c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	data = rd_reg_word(&reg->ctrl_status);
19918c2ecf20Sopenharmony_ci	data &= ~(CSR_FLASH_ENABLE);
19928c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, data);
19938c2ecf20Sopenharmony_ci	rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
19948c2ecf20Sopenharmony_ci}
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci/**
19978c2ecf20Sopenharmony_ci * qla2x00_read_flash_byte() - Reads a byte from flash
19988c2ecf20Sopenharmony_ci * @ha: HA context
19998c2ecf20Sopenharmony_ci * @addr: Address in flash to read
20008c2ecf20Sopenharmony_ci *
20018c2ecf20Sopenharmony_ci * A word is read from the chip, but, only the lower byte is valid.
20028c2ecf20Sopenharmony_ci *
20038c2ecf20Sopenharmony_ci * Returns the byte read from flash @addr.
20048c2ecf20Sopenharmony_ci */
20058c2ecf20Sopenharmony_cistatic uint8_t
20068c2ecf20Sopenharmony_ciqla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	uint16_t data;
20098c2ecf20Sopenharmony_ci	uint16_t bank_select;
20108c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	bank_select = rd_reg_word(&reg->ctrl_status);
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
20158c2ecf20Sopenharmony_ci		/* Specify 64K address range: */
20168c2ecf20Sopenharmony_ci		/*  clear out Module Select and Flash Address bits [19:16]. */
20178c2ecf20Sopenharmony_ci		bank_select &= ~0xf8;
20188c2ecf20Sopenharmony_ci		bank_select |= addr >> 12 & 0xf0;
20198c2ecf20Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
20208c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20218c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
20248c2ecf20Sopenharmony_ci		data = rd_reg_word(&reg->flash_data);
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci		return (uint8_t)data;
20278c2ecf20Sopenharmony_ci	}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	/* Setup bit 16 of flash address. */
20308c2ecf20Sopenharmony_ci	if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
20318c2ecf20Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
20328c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20338c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
20348c2ecf20Sopenharmony_ci	} else if (((addr & BIT_16) == 0) &&
20358c2ecf20Sopenharmony_ci	    (bank_select & CSR_FLASH_64K_BANK)) {
20368c2ecf20Sopenharmony_ci		bank_select &= ~(CSR_FLASH_64K_BANK);
20378c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20388c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
20398c2ecf20Sopenharmony_ci	}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci	/* Always perform IO mapped accesses to the FLASH registers. */
20428c2ecf20Sopenharmony_ci	if (ha->pio_address) {
20438c2ecf20Sopenharmony_ci		uint16_t data2;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
20468c2ecf20Sopenharmony_ci		do {
20478c2ecf20Sopenharmony_ci			data = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
20488c2ecf20Sopenharmony_ci			barrier();
20498c2ecf20Sopenharmony_ci			cpu_relax();
20508c2ecf20Sopenharmony_ci			data2 = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
20518c2ecf20Sopenharmony_ci		} while (data != data2);
20528c2ecf20Sopenharmony_ci	} else {
20538c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
20548c2ecf20Sopenharmony_ci		data = qla2x00_debounce_register(&reg->flash_data);
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	return (uint8_t)data;
20588c2ecf20Sopenharmony_ci}
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci/**
20618c2ecf20Sopenharmony_ci * qla2x00_write_flash_byte() - Write a byte to flash
20628c2ecf20Sopenharmony_ci * @ha: HA context
20638c2ecf20Sopenharmony_ci * @addr: Address in flash to write
20648c2ecf20Sopenharmony_ci * @data: Data to write
20658c2ecf20Sopenharmony_ci */
20668c2ecf20Sopenharmony_cistatic void
20678c2ecf20Sopenharmony_ciqla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data)
20688c2ecf20Sopenharmony_ci{
20698c2ecf20Sopenharmony_ci	uint16_t bank_select;
20708c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	bank_select = rd_reg_word(&reg->ctrl_status);
20738c2ecf20Sopenharmony_ci	if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
20748c2ecf20Sopenharmony_ci		/* Specify 64K address range: */
20758c2ecf20Sopenharmony_ci		/*  clear out Module Select and Flash Address bits [19:16]. */
20768c2ecf20Sopenharmony_ci		bank_select &= ~0xf8;
20778c2ecf20Sopenharmony_ci		bank_select |= addr >> 12 & 0xf0;
20788c2ecf20Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
20798c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20808c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
20838c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
20848c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_data, (uint16_t)data);
20858c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci		return;
20888c2ecf20Sopenharmony_ci	}
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	/* Setup bit 16 of flash address. */
20918c2ecf20Sopenharmony_ci	if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
20928c2ecf20Sopenharmony_ci		bank_select |= CSR_FLASH_64K_BANK;
20938c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20948c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
20958c2ecf20Sopenharmony_ci	} else if (((addr & BIT_16) == 0) &&
20968c2ecf20Sopenharmony_ci	    (bank_select & CSR_FLASH_64K_BANK)) {
20978c2ecf20Sopenharmony_ci		bank_select &= ~(CSR_FLASH_64K_BANK);
20988c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->ctrl_status, bank_select);
20998c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);	/* PCI Posting. */
21008c2ecf20Sopenharmony_ci	}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	/* Always perform IO mapped accesses to the FLASH registers. */
21038c2ecf20Sopenharmony_ci	if (ha->pio_address) {
21048c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
21058c2ecf20Sopenharmony_ci		WRT_REG_WORD_PIO(PIO_REG(ha, flash_data), (uint16_t)data);
21068c2ecf20Sopenharmony_ci	} else {
21078c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_address, (uint16_t)addr);
21088c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
21098c2ecf20Sopenharmony_ci		wrt_reg_word(&reg->flash_data, (uint16_t)data);
21108c2ecf20Sopenharmony_ci		rd_reg_word(&reg->ctrl_status);		/* PCI Posting. */
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci/**
21158c2ecf20Sopenharmony_ci * qla2x00_poll_flash() - Polls flash for completion.
21168c2ecf20Sopenharmony_ci * @ha: HA context
21178c2ecf20Sopenharmony_ci * @addr: Address in flash to poll
21188c2ecf20Sopenharmony_ci * @poll_data: Data to be polled
21198c2ecf20Sopenharmony_ci * @man_id: Flash manufacturer ID
21208c2ecf20Sopenharmony_ci * @flash_id: Flash ID
21218c2ecf20Sopenharmony_ci *
21228c2ecf20Sopenharmony_ci * This function polls the device until bit 7 of what is read matches data
21238c2ecf20Sopenharmony_ci * bit 7 or until data bit 5 becomes a 1.  If that hapens, the flash ROM timed
21248c2ecf20Sopenharmony_ci * out (a fatal error).  The flash book recommeds reading bit 7 again after
21258c2ecf20Sopenharmony_ci * reading bit 5 as a 1.
21268c2ecf20Sopenharmony_ci *
21278c2ecf20Sopenharmony_ci * Returns 0 on success, else non-zero.
21288c2ecf20Sopenharmony_ci */
21298c2ecf20Sopenharmony_cistatic int
21308c2ecf20Sopenharmony_ciqla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data,
21318c2ecf20Sopenharmony_ci    uint8_t man_id, uint8_t flash_id)
21328c2ecf20Sopenharmony_ci{
21338c2ecf20Sopenharmony_ci	int status;
21348c2ecf20Sopenharmony_ci	uint8_t flash_data;
21358c2ecf20Sopenharmony_ci	uint32_t cnt;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	status = 1;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	/* Wait for 30 seconds for command to finish. */
21408c2ecf20Sopenharmony_ci	poll_data &= BIT_7;
21418c2ecf20Sopenharmony_ci	for (cnt = 3000000; cnt; cnt--) {
21428c2ecf20Sopenharmony_ci		flash_data = qla2x00_read_flash_byte(ha, addr);
21438c2ecf20Sopenharmony_ci		if ((flash_data & BIT_7) == poll_data) {
21448c2ecf20Sopenharmony_ci			status = 0;
21458c2ecf20Sopenharmony_ci			break;
21468c2ecf20Sopenharmony_ci		}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci		if (man_id != 0x40 && man_id != 0xda) {
21498c2ecf20Sopenharmony_ci			if ((flash_data & BIT_5) && cnt > 2)
21508c2ecf20Sopenharmony_ci				cnt = 2;
21518c2ecf20Sopenharmony_ci		}
21528c2ecf20Sopenharmony_ci		udelay(10);
21538c2ecf20Sopenharmony_ci		barrier();
21548c2ecf20Sopenharmony_ci		cond_resched();
21558c2ecf20Sopenharmony_ci	}
21568c2ecf20Sopenharmony_ci	return status;
21578c2ecf20Sopenharmony_ci}
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci/**
21608c2ecf20Sopenharmony_ci * qla2x00_program_flash_address() - Programs a flash address
21618c2ecf20Sopenharmony_ci * @ha: HA context
21628c2ecf20Sopenharmony_ci * @addr: Address in flash to program
21638c2ecf20Sopenharmony_ci * @data: Data to be written in flash
21648c2ecf20Sopenharmony_ci * @man_id: Flash manufacturer ID
21658c2ecf20Sopenharmony_ci * @flash_id: Flash ID
21668c2ecf20Sopenharmony_ci *
21678c2ecf20Sopenharmony_ci * Returns 0 on success, else non-zero.
21688c2ecf20Sopenharmony_ci */
21698c2ecf20Sopenharmony_cistatic int
21708c2ecf20Sopenharmony_ciqla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr,
21718c2ecf20Sopenharmony_ci    uint8_t data, uint8_t man_id, uint8_t flash_id)
21728c2ecf20Sopenharmony_ci{
21738c2ecf20Sopenharmony_ci	/* Write Program Command Sequence. */
21748c2ecf20Sopenharmony_ci	if (IS_OEM_001(ha)) {
21758c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
21768c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
21778c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xa0);
21788c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr, data);
21798c2ecf20Sopenharmony_ci	} else {
21808c2ecf20Sopenharmony_ci		if (man_id == 0xda && flash_id == 0xc1) {
21818c2ecf20Sopenharmony_ci			qla2x00_write_flash_byte(ha, addr, data);
21828c2ecf20Sopenharmony_ci			if (addr & 0x7e)
21838c2ecf20Sopenharmony_ci				return 0;
21848c2ecf20Sopenharmony_ci		} else {
21858c2ecf20Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
21868c2ecf20Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
21878c2ecf20Sopenharmony_ci			qla2x00_write_flash_byte(ha, 0x5555, 0xa0);
21888c2ecf20Sopenharmony_ci			qla2x00_write_flash_byte(ha, addr, data);
21898c2ecf20Sopenharmony_ci		}
21908c2ecf20Sopenharmony_ci	}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	udelay(150);
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	/* Wait for write to complete. */
21958c2ecf20Sopenharmony_ci	return qla2x00_poll_flash(ha, addr, data, man_id, flash_id);
21968c2ecf20Sopenharmony_ci}
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci/**
21998c2ecf20Sopenharmony_ci * qla2x00_erase_flash() - Erase the flash.
22008c2ecf20Sopenharmony_ci * @ha: HA context
22018c2ecf20Sopenharmony_ci * @man_id: Flash manufacturer ID
22028c2ecf20Sopenharmony_ci * @flash_id: Flash ID
22038c2ecf20Sopenharmony_ci *
22048c2ecf20Sopenharmony_ci * Returns 0 on success, else non-zero.
22058c2ecf20Sopenharmony_ci */
22068c2ecf20Sopenharmony_cistatic int
22078c2ecf20Sopenharmony_ciqla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id)
22088c2ecf20Sopenharmony_ci{
22098c2ecf20Sopenharmony_ci	/* Individual Sector Erase Command Sequence */
22108c2ecf20Sopenharmony_ci	if (IS_OEM_001(ha)) {
22118c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
22128c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
22138c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0x80);
22148c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
22158c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x555, 0x55);
22168c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0xaaa, 0x10);
22178c2ecf20Sopenharmony_ci	} else {
22188c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22198c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22208c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0x80);
22218c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22228c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22238c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, 0x5555, 0x10);
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	udelay(150);
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	/* Wait for erase to complete. */
22298c2ecf20Sopenharmony_ci	return qla2x00_poll_flash(ha, 0x00, 0x80, man_id, flash_id);
22308c2ecf20Sopenharmony_ci}
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci/**
22338c2ecf20Sopenharmony_ci * qla2x00_erase_flash_sector() - Erase a flash sector.
22348c2ecf20Sopenharmony_ci * @ha: HA context
22358c2ecf20Sopenharmony_ci * @addr: Flash sector to erase
22368c2ecf20Sopenharmony_ci * @sec_mask: Sector address mask
22378c2ecf20Sopenharmony_ci * @man_id: Flash manufacturer ID
22388c2ecf20Sopenharmony_ci * @flash_id: Flash ID
22398c2ecf20Sopenharmony_ci *
22408c2ecf20Sopenharmony_ci * Returns 0 on success, else non-zero.
22418c2ecf20Sopenharmony_ci */
22428c2ecf20Sopenharmony_cistatic int
22438c2ecf20Sopenharmony_ciqla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
22448c2ecf20Sopenharmony_ci    uint32_t sec_mask, uint8_t man_id, uint8_t flash_id)
22458c2ecf20Sopenharmony_ci{
22468c2ecf20Sopenharmony_ci	/* Individual Sector Erase Command Sequence */
22478c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22488c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22498c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0x80);
22508c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22518c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22528c2ecf20Sopenharmony_ci	if (man_id == 0x1f && flash_id == 0x13)
22538c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x10);
22548c2ecf20Sopenharmony_ci	else
22558c2ecf20Sopenharmony_ci		qla2x00_write_flash_byte(ha, addr & sec_mask, 0x30);
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	udelay(150);
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	/* Wait for erase to complete. */
22608c2ecf20Sopenharmony_ci	return qla2x00_poll_flash(ha, addr, 0x80, man_id, flash_id);
22618c2ecf20Sopenharmony_ci}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci/**
22648c2ecf20Sopenharmony_ci * qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip.
22658c2ecf20Sopenharmony_ci * @ha: host adapter
22668c2ecf20Sopenharmony_ci * @man_id: Flash manufacturer ID
22678c2ecf20Sopenharmony_ci * @flash_id: Flash ID
22688c2ecf20Sopenharmony_ci */
22698c2ecf20Sopenharmony_cistatic void
22708c2ecf20Sopenharmony_ciqla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
22718c2ecf20Sopenharmony_ci    uint8_t *flash_id)
22728c2ecf20Sopenharmony_ci{
22738c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22748c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22758c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0x90);
22768c2ecf20Sopenharmony_ci	*man_id = qla2x00_read_flash_byte(ha, 0x0000);
22778c2ecf20Sopenharmony_ci	*flash_id = qla2x00_read_flash_byte(ha, 0x0001);
22788c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
22798c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
22808c2ecf20Sopenharmony_ci	qla2x00_write_flash_byte(ha, 0x5555, 0xf0);
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_cistatic void
22848c2ecf20Sopenharmony_ciqla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf,
22858c2ecf20Sopenharmony_ci	uint32_t saddr, uint32_t length)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
22888c2ecf20Sopenharmony_ci	uint32_t midpoint, ilength;
22898c2ecf20Sopenharmony_ci	uint8_t data;
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	midpoint = length / 2;
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, 0);
22948c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);
22958c2ecf20Sopenharmony_ci	for (ilength = 0; ilength < length; saddr++, ilength++, tmp_buf++) {
22968c2ecf20Sopenharmony_ci		if (ilength == midpoint) {
22978c2ecf20Sopenharmony_ci			wrt_reg_word(&reg->nvram, NVR_SELECT);
22988c2ecf20Sopenharmony_ci			rd_reg_word(&reg->nvram);
22998c2ecf20Sopenharmony_ci		}
23008c2ecf20Sopenharmony_ci		data = qla2x00_read_flash_byte(ha, saddr);
23018c2ecf20Sopenharmony_ci		if (saddr % 100)
23028c2ecf20Sopenharmony_ci			udelay(10);
23038c2ecf20Sopenharmony_ci		*tmp_buf = data;
23048c2ecf20Sopenharmony_ci		cond_resched();
23058c2ecf20Sopenharmony_ci	}
23068c2ecf20Sopenharmony_ci}
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_cistatic inline void
23098c2ecf20Sopenharmony_ciqla2x00_suspend_hba(struct scsi_qla_host *vha)
23108c2ecf20Sopenharmony_ci{
23118c2ecf20Sopenharmony_ci	int cnt;
23128c2ecf20Sopenharmony_ci	unsigned long flags;
23138c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
23148c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	/* Suspend HBA. */
23178c2ecf20Sopenharmony_ci	scsi_block_requests(vha->host);
23188c2ecf20Sopenharmony_ci	ha->isp_ops->disable_intrs(ha);
23198c2ecf20Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	/* Pause RISC. */
23228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
23238c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->hccr, HCCR_PAUSE_RISC);
23248c2ecf20Sopenharmony_ci	rd_reg_word(&reg->hccr);
23258c2ecf20Sopenharmony_ci	if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
23268c2ecf20Sopenharmony_ci		for (cnt = 0; cnt < 30000; cnt++) {
23278c2ecf20Sopenharmony_ci			if ((rd_reg_word(&reg->hccr) & HCCR_RISC_PAUSE) != 0)
23288c2ecf20Sopenharmony_ci				break;
23298c2ecf20Sopenharmony_ci			udelay(100);
23308c2ecf20Sopenharmony_ci		}
23318c2ecf20Sopenharmony_ci	} else {
23328c2ecf20Sopenharmony_ci		udelay(10);
23338c2ecf20Sopenharmony_ci	}
23348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
23358c2ecf20Sopenharmony_ci}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_cistatic inline void
23388c2ecf20Sopenharmony_ciqla2x00_resume_hba(struct scsi_qla_host *vha)
23398c2ecf20Sopenharmony_ci{
23408c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	/* Resume HBA. */
23438c2ecf20Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
23448c2ecf20Sopenharmony_ci	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
23458c2ecf20Sopenharmony_ci	qla2xxx_wake_dpc(vha);
23468c2ecf20Sopenharmony_ci	qla2x00_wait_for_chip_reset(vha);
23478c2ecf20Sopenharmony_ci	scsi_unblock_requests(vha->host);
23488c2ecf20Sopenharmony_ci}
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_civoid *
23518c2ecf20Sopenharmony_ciqla2x00_read_optrom_data(struct scsi_qla_host *vha, void *buf,
23528c2ecf20Sopenharmony_ci    uint32_t offset, uint32_t length)
23538c2ecf20Sopenharmony_ci{
23548c2ecf20Sopenharmony_ci	uint32_t addr, midpoint;
23558c2ecf20Sopenharmony_ci	uint8_t *data;
23568c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
23578c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	/* Suspend HBA. */
23608c2ecf20Sopenharmony_ci	qla2x00_suspend_hba(vha);
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	/* Go with read. */
23638c2ecf20Sopenharmony_ci	midpoint = ha->optrom_size / 2;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	qla2x00_flash_enable(ha);
23668c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->nvram, 0);
23678c2ecf20Sopenharmony_ci	rd_reg_word(&reg->nvram);		/* PCI Posting. */
23688c2ecf20Sopenharmony_ci	for (addr = offset, data = buf; addr < length; addr++, data++) {
23698c2ecf20Sopenharmony_ci		if (addr == midpoint) {
23708c2ecf20Sopenharmony_ci			wrt_reg_word(&reg->nvram, NVR_SELECT);
23718c2ecf20Sopenharmony_ci			rd_reg_word(&reg->nvram);	/* PCI Posting. */
23728c2ecf20Sopenharmony_ci		}
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci		*data = qla2x00_read_flash_byte(ha, addr);
23758c2ecf20Sopenharmony_ci	}
23768c2ecf20Sopenharmony_ci	qla2x00_flash_disable(ha);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	/* Resume HBA. */
23798c2ecf20Sopenharmony_ci	qla2x00_resume_hba(vha);
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci	return buf;
23828c2ecf20Sopenharmony_ci}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ciint
23858c2ecf20Sopenharmony_ciqla2x00_write_optrom_data(struct scsi_qla_host *vha, void *buf,
23868c2ecf20Sopenharmony_ci    uint32_t offset, uint32_t length)
23878c2ecf20Sopenharmony_ci{
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	int rval;
23908c2ecf20Sopenharmony_ci	uint8_t man_id, flash_id, sec_number, *data;
23918c2ecf20Sopenharmony_ci	uint16_t wd;
23928c2ecf20Sopenharmony_ci	uint32_t addr, liter, sec_mask, rest_addr;
23938c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
23948c2ecf20Sopenharmony_ci	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	/* Suspend HBA. */
23978c2ecf20Sopenharmony_ci	qla2x00_suspend_hba(vha);
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	rval = QLA_SUCCESS;
24008c2ecf20Sopenharmony_ci	sec_number = 0;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	/* Reset ISP chip. */
24038c2ecf20Sopenharmony_ci	wrt_reg_word(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
24048c2ecf20Sopenharmony_ci	pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_ci	/* Go with write. */
24078c2ecf20Sopenharmony_ci	qla2x00_flash_enable(ha);
24088c2ecf20Sopenharmony_ci	do {	/* Loop once to provide quick error exit */
24098c2ecf20Sopenharmony_ci		/* Structure of flash memory based on manufacturer */
24108c2ecf20Sopenharmony_ci		if (IS_OEM_001(ha)) {
24118c2ecf20Sopenharmony_ci			/* OEM variant with special flash part. */
24128c2ecf20Sopenharmony_ci			man_id = flash_id = 0;
24138c2ecf20Sopenharmony_ci			rest_addr = 0xffff;
24148c2ecf20Sopenharmony_ci			sec_mask   = 0x10000;
24158c2ecf20Sopenharmony_ci			goto update_flash;
24168c2ecf20Sopenharmony_ci		}
24178c2ecf20Sopenharmony_ci		qla2x00_get_flash_manufacturer(ha, &man_id, &flash_id);
24188c2ecf20Sopenharmony_ci		switch (man_id) {
24198c2ecf20Sopenharmony_ci		case 0x20: /* ST flash. */
24208c2ecf20Sopenharmony_ci			if (flash_id == 0xd2 || flash_id == 0xe3) {
24218c2ecf20Sopenharmony_ci				/*
24228c2ecf20Sopenharmony_ci				 * ST m29w008at part - 64kb sector size with
24238c2ecf20Sopenharmony_ci				 * 32kb,8kb,8kb,16kb sectors at memory address
24248c2ecf20Sopenharmony_ci				 * 0xf0000.
24258c2ecf20Sopenharmony_ci				 */
24268c2ecf20Sopenharmony_ci				rest_addr = 0xffff;
24278c2ecf20Sopenharmony_ci				sec_mask = 0x10000;
24288c2ecf20Sopenharmony_ci				break;
24298c2ecf20Sopenharmony_ci			}
24308c2ecf20Sopenharmony_ci			/*
24318c2ecf20Sopenharmony_ci			 * ST m29w010b part - 16kb sector size
24328c2ecf20Sopenharmony_ci			 * Default to 16kb sectors
24338c2ecf20Sopenharmony_ci			 */
24348c2ecf20Sopenharmony_ci			rest_addr = 0x3fff;
24358c2ecf20Sopenharmony_ci			sec_mask = 0x1c000;
24368c2ecf20Sopenharmony_ci			break;
24378c2ecf20Sopenharmony_ci		case 0x40: /* Mostel flash. */
24388c2ecf20Sopenharmony_ci			/* Mostel v29c51001 part - 512 byte sector size. */
24398c2ecf20Sopenharmony_ci			rest_addr = 0x1ff;
24408c2ecf20Sopenharmony_ci			sec_mask = 0x1fe00;
24418c2ecf20Sopenharmony_ci			break;
24428c2ecf20Sopenharmony_ci		case 0xbf: /* SST flash. */
24438c2ecf20Sopenharmony_ci			/* SST39sf10 part - 4kb sector size. */
24448c2ecf20Sopenharmony_ci			rest_addr = 0xfff;
24458c2ecf20Sopenharmony_ci			sec_mask = 0x1f000;
24468c2ecf20Sopenharmony_ci			break;
24478c2ecf20Sopenharmony_ci		case 0xda: /* Winbond flash. */
24488c2ecf20Sopenharmony_ci			/* Winbond W29EE011 part - 256 byte sector size. */
24498c2ecf20Sopenharmony_ci			rest_addr = 0x7f;
24508c2ecf20Sopenharmony_ci			sec_mask = 0x1ff80;
24518c2ecf20Sopenharmony_ci			break;
24528c2ecf20Sopenharmony_ci		case 0xc2: /* Macronix flash. */
24538c2ecf20Sopenharmony_ci			/* 64k sector size. */
24548c2ecf20Sopenharmony_ci			if (flash_id == 0x38 || flash_id == 0x4f) {
24558c2ecf20Sopenharmony_ci				rest_addr = 0xffff;
24568c2ecf20Sopenharmony_ci				sec_mask = 0x10000;
24578c2ecf20Sopenharmony_ci				break;
24588c2ecf20Sopenharmony_ci			}
24598c2ecf20Sopenharmony_ci			fallthrough;
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci		case 0x1f: /* Atmel flash. */
24628c2ecf20Sopenharmony_ci			/* 512k sector size. */
24638c2ecf20Sopenharmony_ci			if (flash_id == 0x13) {
24648c2ecf20Sopenharmony_ci				rest_addr = 0x7fffffff;
24658c2ecf20Sopenharmony_ci				sec_mask =   0x80000000;
24668c2ecf20Sopenharmony_ci				break;
24678c2ecf20Sopenharmony_ci			}
24688c2ecf20Sopenharmony_ci			fallthrough;
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci		case 0x01: /* AMD flash. */
24718c2ecf20Sopenharmony_ci			if (flash_id == 0x38 || flash_id == 0x40 ||
24728c2ecf20Sopenharmony_ci			    flash_id == 0x4f) {
24738c2ecf20Sopenharmony_ci				/* Am29LV081 part - 64kb sector size. */
24748c2ecf20Sopenharmony_ci				/* Am29LV002BT part - 64kb sector size. */
24758c2ecf20Sopenharmony_ci				rest_addr = 0xffff;
24768c2ecf20Sopenharmony_ci				sec_mask = 0x10000;
24778c2ecf20Sopenharmony_ci				break;
24788c2ecf20Sopenharmony_ci			} else if (flash_id == 0x3e) {
24798c2ecf20Sopenharmony_ci				/*
24808c2ecf20Sopenharmony_ci				 * Am29LV008b part - 64kb sector size with
24818c2ecf20Sopenharmony_ci				 * 32kb,8kb,8kb,16kb sector at memory address
24828c2ecf20Sopenharmony_ci				 * h0xf0000.
24838c2ecf20Sopenharmony_ci				 */
24848c2ecf20Sopenharmony_ci				rest_addr = 0xffff;
24858c2ecf20Sopenharmony_ci				sec_mask = 0x10000;
24868c2ecf20Sopenharmony_ci				break;
24878c2ecf20Sopenharmony_ci			} else if (flash_id == 0x20 || flash_id == 0x6e) {
24888c2ecf20Sopenharmony_ci				/*
24898c2ecf20Sopenharmony_ci				 * Am29LV010 part or AM29f010 - 16kb sector
24908c2ecf20Sopenharmony_ci				 * size.
24918c2ecf20Sopenharmony_ci				 */
24928c2ecf20Sopenharmony_ci				rest_addr = 0x3fff;
24938c2ecf20Sopenharmony_ci				sec_mask = 0x1c000;
24948c2ecf20Sopenharmony_ci				break;
24958c2ecf20Sopenharmony_ci			} else if (flash_id == 0x6d) {
24968c2ecf20Sopenharmony_ci				/* Am29LV001 part - 8kb sector size. */
24978c2ecf20Sopenharmony_ci				rest_addr = 0x1fff;
24988c2ecf20Sopenharmony_ci				sec_mask = 0x1e000;
24998c2ecf20Sopenharmony_ci				break;
25008c2ecf20Sopenharmony_ci			}
25018c2ecf20Sopenharmony_ci			fallthrough;
25028c2ecf20Sopenharmony_ci		default:
25038c2ecf20Sopenharmony_ci			/* Default to 16 kb sector size. */
25048c2ecf20Sopenharmony_ci			rest_addr = 0x3fff;
25058c2ecf20Sopenharmony_ci			sec_mask = 0x1c000;
25068c2ecf20Sopenharmony_ci			break;
25078c2ecf20Sopenharmony_ci		}
25088c2ecf20Sopenharmony_ci
25098c2ecf20Sopenharmony_ciupdate_flash:
25108c2ecf20Sopenharmony_ci		if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
25118c2ecf20Sopenharmony_ci			if (qla2x00_erase_flash(ha, man_id, flash_id)) {
25128c2ecf20Sopenharmony_ci				rval = QLA_FUNCTION_FAILED;
25138c2ecf20Sopenharmony_ci				break;
25148c2ecf20Sopenharmony_ci			}
25158c2ecf20Sopenharmony_ci		}
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci		for (addr = offset, liter = 0; liter < length; liter++,
25188c2ecf20Sopenharmony_ci		    addr++) {
25198c2ecf20Sopenharmony_ci			data = buf + liter;
25208c2ecf20Sopenharmony_ci			/* Are we at the beginning of a sector? */
25218c2ecf20Sopenharmony_ci			if ((addr & rest_addr) == 0) {
25228c2ecf20Sopenharmony_ci				if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
25238c2ecf20Sopenharmony_ci					if (addr >= 0x10000UL) {
25248c2ecf20Sopenharmony_ci						if (((addr >> 12) & 0xf0) &&
25258c2ecf20Sopenharmony_ci						    ((man_id == 0x01 &&
25268c2ecf20Sopenharmony_ci							flash_id == 0x3e) ||
25278c2ecf20Sopenharmony_ci						     (man_id == 0x20 &&
25288c2ecf20Sopenharmony_ci							 flash_id == 0xd2))) {
25298c2ecf20Sopenharmony_ci							sec_number++;
25308c2ecf20Sopenharmony_ci							if (sec_number == 1) {
25318c2ecf20Sopenharmony_ci								rest_addr =
25328c2ecf20Sopenharmony_ci								    0x7fff;
25338c2ecf20Sopenharmony_ci								sec_mask =
25348c2ecf20Sopenharmony_ci								    0x18000;
25358c2ecf20Sopenharmony_ci							} else if (
25368c2ecf20Sopenharmony_ci							    sec_number == 2 ||
25378c2ecf20Sopenharmony_ci							    sec_number == 3) {
25388c2ecf20Sopenharmony_ci								rest_addr =
25398c2ecf20Sopenharmony_ci								    0x1fff;
25408c2ecf20Sopenharmony_ci								sec_mask =
25418c2ecf20Sopenharmony_ci								    0x1e000;
25428c2ecf20Sopenharmony_ci							} else if (
25438c2ecf20Sopenharmony_ci							    sec_number == 4) {
25448c2ecf20Sopenharmony_ci								rest_addr =
25458c2ecf20Sopenharmony_ci								    0x3fff;
25468c2ecf20Sopenharmony_ci								sec_mask =
25478c2ecf20Sopenharmony_ci								    0x1c000;
25488c2ecf20Sopenharmony_ci							}
25498c2ecf20Sopenharmony_ci						}
25508c2ecf20Sopenharmony_ci					}
25518c2ecf20Sopenharmony_ci				} else if (addr == ha->optrom_size / 2) {
25528c2ecf20Sopenharmony_ci					wrt_reg_word(&reg->nvram, NVR_SELECT);
25538c2ecf20Sopenharmony_ci					rd_reg_word(&reg->nvram);
25548c2ecf20Sopenharmony_ci				}
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci				if (flash_id == 0xda && man_id == 0xc1) {
25578c2ecf20Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x5555,
25588c2ecf20Sopenharmony_ci					    0xaa);
25598c2ecf20Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x2aaa,
25608c2ecf20Sopenharmony_ci					    0x55);
25618c2ecf20Sopenharmony_ci					qla2x00_write_flash_byte(ha, 0x5555,
25628c2ecf20Sopenharmony_ci					    0xa0);
25638c2ecf20Sopenharmony_ci				} else if (!IS_QLA2322(ha) && !IS_QLA6322(ha)) {
25648c2ecf20Sopenharmony_ci					/* Then erase it */
25658c2ecf20Sopenharmony_ci					if (qla2x00_erase_flash_sector(ha,
25668c2ecf20Sopenharmony_ci					    addr, sec_mask, man_id,
25678c2ecf20Sopenharmony_ci					    flash_id)) {
25688c2ecf20Sopenharmony_ci						rval = QLA_FUNCTION_FAILED;
25698c2ecf20Sopenharmony_ci						break;
25708c2ecf20Sopenharmony_ci					}
25718c2ecf20Sopenharmony_ci					if (man_id == 0x01 && flash_id == 0x6d)
25728c2ecf20Sopenharmony_ci						sec_number++;
25738c2ecf20Sopenharmony_ci				}
25748c2ecf20Sopenharmony_ci			}
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci			if (man_id == 0x01 && flash_id == 0x6d) {
25778c2ecf20Sopenharmony_ci				if (sec_number == 1 &&
25788c2ecf20Sopenharmony_ci				    addr == (rest_addr - 1)) {
25798c2ecf20Sopenharmony_ci					rest_addr = 0x0fff;
25808c2ecf20Sopenharmony_ci					sec_mask   = 0x1f000;
25818c2ecf20Sopenharmony_ci				} else if (sec_number == 3 && (addr & 0x7ffe)) {
25828c2ecf20Sopenharmony_ci					rest_addr = 0x3fff;
25838c2ecf20Sopenharmony_ci					sec_mask   = 0x1c000;
25848c2ecf20Sopenharmony_ci				}
25858c2ecf20Sopenharmony_ci			}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci			if (qla2x00_program_flash_address(ha, addr, *data,
25888c2ecf20Sopenharmony_ci			    man_id, flash_id)) {
25898c2ecf20Sopenharmony_ci				rval = QLA_FUNCTION_FAILED;
25908c2ecf20Sopenharmony_ci				break;
25918c2ecf20Sopenharmony_ci			}
25928c2ecf20Sopenharmony_ci			cond_resched();
25938c2ecf20Sopenharmony_ci		}
25948c2ecf20Sopenharmony_ci	} while (0);
25958c2ecf20Sopenharmony_ci	qla2x00_flash_disable(ha);
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	/* Resume HBA. */
25988c2ecf20Sopenharmony_ci	qla2x00_resume_hba(vha);
25998c2ecf20Sopenharmony_ci
26008c2ecf20Sopenharmony_ci	return rval;
26018c2ecf20Sopenharmony_ci}
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_civoid *
26048c2ecf20Sopenharmony_ciqla24xx_read_optrom_data(struct scsi_qla_host *vha, void *buf,
26058c2ecf20Sopenharmony_ci    uint32_t offset, uint32_t length)
26068c2ecf20Sopenharmony_ci{
26078c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci	/* Suspend HBA. */
26108c2ecf20Sopenharmony_ci	scsi_block_requests(vha->host);
26118c2ecf20Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci	/* Go with read. */
26148c2ecf20Sopenharmony_ci	qla24xx_read_flash_data(vha, buf, offset >> 2, length >> 2);
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci	/* Resume HBA. */
26178c2ecf20Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
26188c2ecf20Sopenharmony_ci	scsi_unblock_requests(vha->host);
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	return buf;
26218c2ecf20Sopenharmony_ci}
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_cistatic int
26248c2ecf20Sopenharmony_ciqla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, uint32_t *buf,
26258c2ecf20Sopenharmony_ci    uint32_t len, uint32_t buf_size_without_sfub, uint8_t *sfub_buf)
26268c2ecf20Sopenharmony_ci{
26278c2ecf20Sopenharmony_ci	uint32_t *p, check_sum = 0;
26288c2ecf20Sopenharmony_ci	int i;
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	p = buf + buf_size_without_sfub;
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci	/* Extract SFUB from end of file */
26338c2ecf20Sopenharmony_ci	memcpy(sfub_buf, (uint8_t *)p,
26348c2ecf20Sopenharmony_ci	    sizeof(struct secure_flash_update_block));
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_ci	for (i = 0; i < (sizeof(struct secure_flash_update_block) >> 2); i++)
26378c2ecf20Sopenharmony_ci		check_sum += p[i];
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_ci	check_sum = (~check_sum) + 1;
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_ci	if (check_sum != p[i]) {
26428c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7097,
26438c2ecf20Sopenharmony_ci		    "SFUB checksum failed, 0x%x, 0x%x\n",
26448c2ecf20Sopenharmony_ci		    check_sum, p[i]);
26458c2ecf20Sopenharmony_ci		return QLA_COMMAND_ERROR;
26468c2ecf20Sopenharmony_ci	}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic int
26528c2ecf20Sopenharmony_ciqla28xx_get_flash_region(struct scsi_qla_host *vha, uint32_t start,
26538c2ecf20Sopenharmony_ci    struct qla_flt_region *region)
26548c2ecf20Sopenharmony_ci{
26558c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
26568c2ecf20Sopenharmony_ci	struct qla_flt_header *flt = ha->flt;
26578c2ecf20Sopenharmony_ci	struct qla_flt_region *flt_reg = &flt->region[0];
26588c2ecf20Sopenharmony_ci	uint16_t cnt;
26598c2ecf20Sopenharmony_ci	int rval = QLA_FUNCTION_FAILED;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	if (!ha->flt)
26628c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ci	cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
26658c2ecf20Sopenharmony_ci	for (; cnt; cnt--, flt_reg++) {
26668c2ecf20Sopenharmony_ci		if (le32_to_cpu(flt_reg->start) == start) {
26678c2ecf20Sopenharmony_ci			memcpy((uint8_t *)region, flt_reg,
26688c2ecf20Sopenharmony_ci			    sizeof(struct qla_flt_region));
26698c2ecf20Sopenharmony_ci			rval = QLA_SUCCESS;
26708c2ecf20Sopenharmony_ci			break;
26718c2ecf20Sopenharmony_ci		}
26728c2ecf20Sopenharmony_ci	}
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	return rval;
26758c2ecf20Sopenharmony_ci}
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_cistatic int
26788c2ecf20Sopenharmony_ciqla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
26798c2ecf20Sopenharmony_ci    uint32_t dwords)
26808c2ecf20Sopenharmony_ci{
26818c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
26828c2ecf20Sopenharmony_ci	ulong liter;
26838c2ecf20Sopenharmony_ci	ulong dburst = OPTROM_BURST_DWORDS; /* burst size in dwords */
26848c2ecf20Sopenharmony_ci	uint32_t sec_mask, rest_addr, fdata;
26858c2ecf20Sopenharmony_ci	void *optrom = NULL;
26868c2ecf20Sopenharmony_ci	dma_addr_t optrom_dma;
26878c2ecf20Sopenharmony_ci	int rval, ret;
26888c2ecf20Sopenharmony_ci	struct secure_flash_update_block *sfub;
26898c2ecf20Sopenharmony_ci	dma_addr_t sfub_dma;
26908c2ecf20Sopenharmony_ci	uint32_t offset = faddr << 2;
26918c2ecf20Sopenharmony_ci	uint32_t buf_size_without_sfub = 0;
26928c2ecf20Sopenharmony_ci	struct qla_flt_region region;
26938c2ecf20Sopenharmony_ci	bool reset_to_rom = false;
26948c2ecf20Sopenharmony_ci	uint32_t risc_size, risc_attr = 0;
26958c2ecf20Sopenharmony_ci	__be32 *fw_array = NULL;
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	/* Retrieve region info - must be a start address passed in */
26988c2ecf20Sopenharmony_ci	rval = qla28xx_get_flash_region(vha, offset, &region);
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci	if (rval != QLA_SUCCESS) {
27018c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0xffff,
27028c2ecf20Sopenharmony_ci		    "Invalid address %x - not a region start address\n",
27038c2ecf20Sopenharmony_ci		    offset);
27048c2ecf20Sopenharmony_ci		goto done;
27058c2ecf20Sopenharmony_ci	}
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_ci	/* Allocate dma buffer for burst write */
27088c2ecf20Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
27098c2ecf20Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
27108c2ecf20Sopenharmony_ci	if (!optrom) {
27118c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7095,
27128c2ecf20Sopenharmony_ci		    "Failed allocate burst (%x bytes)\n", OPTROM_BURST_SIZE);
27138c2ecf20Sopenharmony_ci		rval = QLA_COMMAND_ERROR;
27148c2ecf20Sopenharmony_ci		goto done;
27158c2ecf20Sopenharmony_ci	}
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	/*
27188c2ecf20Sopenharmony_ci	 * If adapter supports secure flash and region is secure
27198c2ecf20Sopenharmony_ci	 * extract secure flash update block (SFUB) and verify
27208c2ecf20Sopenharmony_ci	 */
27218c2ecf20Sopenharmony_ci	if (ha->flags.secure_adapter && region.attribute) {
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
27248c2ecf20Sopenharmony_ci		    "Region %x is secure\n", region.code);
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci		switch (le16_to_cpu(region.code)) {
27278c2ecf20Sopenharmony_ci		case FLT_REG_FW:
27288c2ecf20Sopenharmony_ci		case FLT_REG_FW_SEC_27XX:
27298c2ecf20Sopenharmony_ci		case FLT_REG_MPI_PRI_28XX:
27308c2ecf20Sopenharmony_ci		case FLT_REG_MPI_SEC_28XX:
27318c2ecf20Sopenharmony_ci			fw_array = (__force __be32 *)dwptr;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci			/* 1st fw array */
27348c2ecf20Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
27358c2ecf20Sopenharmony_ci			risc_attr = be32_to_cpu(fw_array[9]);
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci			buf_size_without_sfub = risc_size;
27388c2ecf20Sopenharmony_ci			fw_array += risc_size;
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci			/* 2nd fw array */
27418c2ecf20Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci			buf_size_without_sfub += risc_size;
27448c2ecf20Sopenharmony_ci			fw_array += risc_size;
27458c2ecf20Sopenharmony_ci
27468c2ecf20Sopenharmony_ci			/* 1st dump template */
27478c2ecf20Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[2]);
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci			/* skip header and ignore checksum */
27508c2ecf20Sopenharmony_ci			buf_size_without_sfub += risc_size;
27518c2ecf20Sopenharmony_ci			fw_array += risc_size;
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci			if (risc_attr & BIT_9) {
27548c2ecf20Sopenharmony_ci				/* 2nd dump template */
27558c2ecf20Sopenharmony_ci				risc_size = be32_to_cpu(fw_array[2]);
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci				/* skip header and ignore checksum */
27588c2ecf20Sopenharmony_ci				buf_size_without_sfub += risc_size;
27598c2ecf20Sopenharmony_ci				fw_array += risc_size;
27608c2ecf20Sopenharmony_ci			}
27618c2ecf20Sopenharmony_ci			break;
27628c2ecf20Sopenharmony_ci
27638c2ecf20Sopenharmony_ci		case FLT_REG_PEP_PRI_28XX:
27648c2ecf20Sopenharmony_ci		case FLT_REG_PEP_SEC_28XX:
27658c2ecf20Sopenharmony_ci			fw_array = (__force __be32 *)dwptr;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci			/* 1st fw array */
27688c2ecf20Sopenharmony_ci			risc_size = be32_to_cpu(fw_array[3]);
27698c2ecf20Sopenharmony_ci			risc_attr = be32_to_cpu(fw_array[9]);
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci			buf_size_without_sfub = risc_size;
27728c2ecf20Sopenharmony_ci			fw_array += risc_size;
27738c2ecf20Sopenharmony_ci			break;
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci		default:
27768c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha,
27778c2ecf20Sopenharmony_ci			    0xffff, "Secure region %x not supported\n",
27788c2ecf20Sopenharmony_ci			    region.code);
27798c2ecf20Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
27808c2ecf20Sopenharmony_ci			goto done;
27818c2ecf20Sopenharmony_ci		}
27828c2ecf20Sopenharmony_ci
27838c2ecf20Sopenharmony_ci		sfub = dma_alloc_coherent(&ha->pdev->dev,
27848c2ecf20Sopenharmony_ci			sizeof(struct secure_flash_update_block), &sfub_dma,
27858c2ecf20Sopenharmony_ci			GFP_KERNEL);
27868c2ecf20Sopenharmony_ci		if (!sfub) {
27878c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xffff,
27888c2ecf20Sopenharmony_ci			    "Unable to allocate memory for SFUB\n");
27898c2ecf20Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
27908c2ecf20Sopenharmony_ci			goto done;
27918c2ecf20Sopenharmony_ci		}
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci		rval = qla28xx_extract_sfub_and_verify(vha, dwptr, dwords,
27948c2ecf20Sopenharmony_ci			buf_size_without_sfub, (uint8_t *)sfub);
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci		if (rval != QLA_SUCCESS)
27978c2ecf20Sopenharmony_ci			goto done;
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28008c2ecf20Sopenharmony_ci		    "SFUB extract and verify successful\n");
28018c2ecf20Sopenharmony_ci	}
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	rest_addr = (ha->fdt_block_size >> 2) - 1;
28048c2ecf20Sopenharmony_ci	sec_mask = ~rest_addr;
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	/* Lock semaphore */
28078c2ecf20Sopenharmony_ci	rval = qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_LOCK);
28088c2ecf20Sopenharmony_ci	if (rval != QLA_SUCCESS) {
28098c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0xffff,
28108c2ecf20Sopenharmony_ci		    "Unable to lock flash semaphore.");
28118c2ecf20Sopenharmony_ci		goto done;
28128c2ecf20Sopenharmony_ci	}
28138c2ecf20Sopenharmony_ci
28148c2ecf20Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
28158c2ecf20Sopenharmony_ci	    "Unprotect flash...\n");
28168c2ecf20Sopenharmony_ci	rval = qla24xx_unprotect_flash(vha);
28178c2ecf20Sopenharmony_ci	if (rval) {
28188c2ecf20Sopenharmony_ci		qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
28198c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7096, "Failed unprotect flash\n");
28208c2ecf20Sopenharmony_ci		goto done;
28218c2ecf20Sopenharmony_ci	}
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++) {
28248c2ecf20Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci		/* If start of sector */
28278c2ecf20Sopenharmony_ci		if (!(faddr & rest_addr)) {
28288c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
28298c2ecf20Sopenharmony_ci			    "Erase sector %#x...\n", faddr);
28308c2ecf20Sopenharmony_ci			rval = qla24xx_erase_sector(vha, fdata);
28318c2ecf20Sopenharmony_ci			if (rval) {
28328c2ecf20Sopenharmony_ci				ql_dbg(ql_dbg_user, vha, 0x7007,
28338c2ecf20Sopenharmony_ci				    "Failed erase sector %#x\n", faddr);
28348c2ecf20Sopenharmony_ci				goto write_protect;
28358c2ecf20Sopenharmony_ci			}
28368c2ecf20Sopenharmony_ci		}
28378c2ecf20Sopenharmony_ci	}
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci	if (ha->flags.secure_adapter) {
28408c2ecf20Sopenharmony_ci		/*
28418c2ecf20Sopenharmony_ci		 * If adapter supports secure flash but FW doesn't,
28428c2ecf20Sopenharmony_ci		 * disable write protect, release semaphore and reset
28438c2ecf20Sopenharmony_ci		 * chip to execute ROM code in order to update region securely
28448c2ecf20Sopenharmony_ci		 */
28458c2ecf20Sopenharmony_ci		if (!ha->flags.secure_fw) {
28468c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28478c2ecf20Sopenharmony_ci			    "Disable Write and Release Semaphore.");
28488c2ecf20Sopenharmony_ci			rval = qla24xx_protect_flash(vha);
28498c2ecf20Sopenharmony_ci			if (rval != QLA_SUCCESS) {
28508c2ecf20Sopenharmony_ci				qla81xx_fac_semaphore_access(vha,
28518c2ecf20Sopenharmony_ci					FAC_SEMAPHORE_UNLOCK);
28528c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
28538c2ecf20Sopenharmony_ci				    "Unable to protect flash.");
28548c2ecf20Sopenharmony_ci				goto done;
28558c2ecf20Sopenharmony_ci			}
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28588c2ecf20Sopenharmony_ci			    "Reset chip to ROM.");
28598c2ecf20Sopenharmony_ci			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
28608c2ecf20Sopenharmony_ci			set_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags);
28618c2ecf20Sopenharmony_ci			qla2xxx_wake_dpc(vha);
28628c2ecf20Sopenharmony_ci			rval = qla2x00_wait_for_chip_reset(vha);
28638c2ecf20Sopenharmony_ci			if (rval != QLA_SUCCESS) {
28648c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
28658c2ecf20Sopenharmony_ci				    "Unable to reset to ROM code.");
28668c2ecf20Sopenharmony_ci				goto done;
28678c2ecf20Sopenharmony_ci			}
28688c2ecf20Sopenharmony_ci			reset_to_rom = true;
28698c2ecf20Sopenharmony_ci			ha->flags.fac_supported = 0;
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28728c2ecf20Sopenharmony_ci			    "Lock Semaphore");
28738c2ecf20Sopenharmony_ci			rval = qla2xxx_write_remote_register(vha,
28748c2ecf20Sopenharmony_ci			    FLASH_SEMAPHORE_REGISTER_ADDR, 0x00020002);
28758c2ecf20Sopenharmony_ci			if (rval != QLA_SUCCESS) {
28768c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
28778c2ecf20Sopenharmony_ci				    "Unable to lock flash semaphore.");
28788c2ecf20Sopenharmony_ci				goto done;
28798c2ecf20Sopenharmony_ci			}
28808c2ecf20Sopenharmony_ci
28818c2ecf20Sopenharmony_ci			/* Unprotect flash */
28828c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28838c2ecf20Sopenharmony_ci			    "Enable Write.");
28848c2ecf20Sopenharmony_ci			rval = qla2x00_write_ram_word(vha, 0x7ffd0101, 0);
28858c2ecf20Sopenharmony_ci			if (rval) {
28868c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0x7096,
28878c2ecf20Sopenharmony_ci				    "Failed unprotect flash\n");
28888c2ecf20Sopenharmony_ci				goto done;
28898c2ecf20Sopenharmony_ci			}
28908c2ecf20Sopenharmony_ci		}
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci		/* If region is secure, send Secure Flash MB Cmd */
28938c2ecf20Sopenharmony_ci		if (region.attribute && buf_size_without_sfub) {
28948c2ecf20Sopenharmony_ci			ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
28958c2ecf20Sopenharmony_ci			    "Sending Secure Flash MB Cmd\n");
28968c2ecf20Sopenharmony_ci			rval = qla28xx_secure_flash_update(vha, 0,
28978c2ecf20Sopenharmony_ci				le16_to_cpu(region.code),
28988c2ecf20Sopenharmony_ci				buf_size_without_sfub, sfub_dma,
28998c2ecf20Sopenharmony_ci				sizeof(struct secure_flash_update_block) >> 2);
29008c2ecf20Sopenharmony_ci			if (rval != QLA_SUCCESS) {
29018c2ecf20Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xffff,
29028c2ecf20Sopenharmony_ci				    "Secure Flash MB Cmd failed %x.", rval);
29038c2ecf20Sopenharmony_ci				goto write_protect;
29048c2ecf20Sopenharmony_ci			}
29058c2ecf20Sopenharmony_ci		}
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci	}
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci	/* re-init flash offset */
29108c2ecf20Sopenharmony_ci	faddr = offset >> 2;
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
29138c2ecf20Sopenharmony_ci		fdata = (faddr & sec_mask) << 2;
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci		/* If smaller than a burst remaining */
29168c2ecf20Sopenharmony_ci		if (dwords - liter < dburst)
29178c2ecf20Sopenharmony_ci			dburst = dwords - liter;
29188c2ecf20Sopenharmony_ci
29198c2ecf20Sopenharmony_ci		/* Copy to dma buffer */
29208c2ecf20Sopenharmony_ci		memcpy(optrom, dwptr, dburst << 2);
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci		/* Burst write */
29238c2ecf20Sopenharmony_ci		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
29248c2ecf20Sopenharmony_ci		    "Write burst (%#lx dwords)...\n", dburst);
29258c2ecf20Sopenharmony_ci		rval = qla2x00_load_ram(vha, optrom_dma,
29268c2ecf20Sopenharmony_ci		    flash_data_addr(ha, faddr), dburst);
29278c2ecf20Sopenharmony_ci		if (rval != QLA_SUCCESS) {
29288c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x7097,
29298c2ecf20Sopenharmony_ci			    "Failed burst write at %x (%p/%#llx)...\n",
29308c2ecf20Sopenharmony_ci			    flash_data_addr(ha, faddr), optrom,
29318c2ecf20Sopenharmony_ci			    (u64)optrom_dma);
29328c2ecf20Sopenharmony_ci			break;
29338c2ecf20Sopenharmony_ci		}
29348c2ecf20Sopenharmony_ci
29358c2ecf20Sopenharmony_ci		liter += dburst - 1;
29368c2ecf20Sopenharmony_ci		faddr += dburst - 1;
29378c2ecf20Sopenharmony_ci		dwptr += dburst - 1;
29388c2ecf20Sopenharmony_ci		continue;
29398c2ecf20Sopenharmony_ci	}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ciwrite_protect:
29428c2ecf20Sopenharmony_ci	ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095,
29438c2ecf20Sopenharmony_ci	    "Protect flash...\n");
29448c2ecf20Sopenharmony_ci	ret = qla24xx_protect_flash(vha);
29458c2ecf20Sopenharmony_ci	if (ret) {
29468c2ecf20Sopenharmony_ci		qla81xx_fac_semaphore_access(vha, FAC_SEMAPHORE_UNLOCK);
29478c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x7099,
29488c2ecf20Sopenharmony_ci		    "Failed protect flash\n");
29498c2ecf20Sopenharmony_ci		rval = QLA_COMMAND_ERROR;
29508c2ecf20Sopenharmony_ci	}
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci	if (reset_to_rom == true) {
29538c2ecf20Sopenharmony_ci		/* Schedule DPC to restart the RISC */
29548c2ecf20Sopenharmony_ci		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
29558c2ecf20Sopenharmony_ci		qla2xxx_wake_dpc(vha);
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci		ret = qla2x00_wait_for_hba_online(vha);
29588c2ecf20Sopenharmony_ci		if (ret != QLA_SUCCESS) {
29598c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xffff,
29608c2ecf20Sopenharmony_ci			    "Adapter did not come out of reset\n");
29618c2ecf20Sopenharmony_ci			rval = QLA_COMMAND_ERROR;
29628c2ecf20Sopenharmony_ci		}
29638c2ecf20Sopenharmony_ci	}
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_cidone:
29668c2ecf20Sopenharmony_ci	if (optrom)
29678c2ecf20Sopenharmony_ci		dma_free_coherent(&ha->pdev->dev,
29688c2ecf20Sopenharmony_ci		    OPTROM_BURST_SIZE, optrom, optrom_dma);
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci	return rval;
29718c2ecf20Sopenharmony_ci}
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ciint
29748c2ecf20Sopenharmony_ciqla24xx_write_optrom_data(struct scsi_qla_host *vha, void *buf,
29758c2ecf20Sopenharmony_ci    uint32_t offset, uint32_t length)
29768c2ecf20Sopenharmony_ci{
29778c2ecf20Sopenharmony_ci	int rval;
29788c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
29798c2ecf20Sopenharmony_ci
29808c2ecf20Sopenharmony_ci	/* Suspend HBA. */
29818c2ecf20Sopenharmony_ci	scsi_block_requests(vha->host);
29828c2ecf20Sopenharmony_ci	set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_ci	/* Go with write. */
29858c2ecf20Sopenharmony_ci	if (IS_QLA28XX(ha))
29868c2ecf20Sopenharmony_ci		rval = qla28xx_write_flash_data(vha, buf, offset >> 2,
29878c2ecf20Sopenharmony_ci						length >> 2);
29888c2ecf20Sopenharmony_ci	else
29898c2ecf20Sopenharmony_ci		rval = qla24xx_write_flash_data(vha, buf, offset >> 2,
29908c2ecf20Sopenharmony_ci						length >> 2);
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
29938c2ecf20Sopenharmony_ci	scsi_unblock_requests(vha->host);
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	return rval;
29968c2ecf20Sopenharmony_ci}
29978c2ecf20Sopenharmony_ci
29988c2ecf20Sopenharmony_civoid *
29998c2ecf20Sopenharmony_ciqla25xx_read_optrom_data(struct scsi_qla_host *vha, void *buf,
30008c2ecf20Sopenharmony_ci    uint32_t offset, uint32_t length)
30018c2ecf20Sopenharmony_ci{
30028c2ecf20Sopenharmony_ci	int rval;
30038c2ecf20Sopenharmony_ci	dma_addr_t optrom_dma;
30048c2ecf20Sopenharmony_ci	void *optrom;
30058c2ecf20Sopenharmony_ci	uint8_t *pbuf;
30068c2ecf20Sopenharmony_ci	uint32_t faddr, left, burst;
30078c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
30108c2ecf20Sopenharmony_ci	    IS_QLA27XX(ha) || IS_QLA28XX(ha))
30118c2ecf20Sopenharmony_ci		goto try_fast;
30128c2ecf20Sopenharmony_ci	if (offset & 0xfff)
30138c2ecf20Sopenharmony_ci		goto slow_read;
30148c2ecf20Sopenharmony_ci	if (length < OPTROM_BURST_SIZE)
30158c2ecf20Sopenharmony_ci		goto slow_read;
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_citry_fast:
30188c2ecf20Sopenharmony_ci	if (offset & 0xff)
30198c2ecf20Sopenharmony_ci		goto slow_read;
30208c2ecf20Sopenharmony_ci	optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
30218c2ecf20Sopenharmony_ci	    &optrom_dma, GFP_KERNEL);
30228c2ecf20Sopenharmony_ci	if (!optrom) {
30238c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x00cc,
30248c2ecf20Sopenharmony_ci		    "Unable to allocate memory for optrom burst read (%x KB).\n",
30258c2ecf20Sopenharmony_ci		    OPTROM_BURST_SIZE / 1024);
30268c2ecf20Sopenharmony_ci		goto slow_read;
30278c2ecf20Sopenharmony_ci	}
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci	pbuf = buf;
30308c2ecf20Sopenharmony_ci	faddr = offset >> 2;
30318c2ecf20Sopenharmony_ci	left = length >> 2;
30328c2ecf20Sopenharmony_ci	burst = OPTROM_BURST_DWORDS;
30338c2ecf20Sopenharmony_ci	while (left != 0) {
30348c2ecf20Sopenharmony_ci		if (burst > left)
30358c2ecf20Sopenharmony_ci			burst = left;
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci		rval = qla2x00_dump_ram(vha, optrom_dma,
30388c2ecf20Sopenharmony_ci		    flash_data_addr(ha, faddr), burst);
30398c2ecf20Sopenharmony_ci		if (rval) {
30408c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00f5,
30418c2ecf20Sopenharmony_ci			    "Unable to burst-read optrom segment (%x/%x/%llx).\n",
30428c2ecf20Sopenharmony_ci			    rval, flash_data_addr(ha, faddr),
30438c2ecf20Sopenharmony_ci			    (unsigned long long)optrom_dma);
30448c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00f6,
30458c2ecf20Sopenharmony_ci			    "Reverting to slow-read.\n");
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci			dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
30488c2ecf20Sopenharmony_ci			    optrom, optrom_dma);
30498c2ecf20Sopenharmony_ci			goto slow_read;
30508c2ecf20Sopenharmony_ci		}
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci		memcpy(pbuf, optrom, burst * 4);
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci		left -= burst;
30558c2ecf20Sopenharmony_ci		faddr += burst;
30568c2ecf20Sopenharmony_ci		pbuf += burst * 4;
30578c2ecf20Sopenharmony_ci	}
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
30608c2ecf20Sopenharmony_ci	    optrom_dma);
30618c2ecf20Sopenharmony_ci
30628c2ecf20Sopenharmony_ci	return buf;
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_cislow_read:
30658c2ecf20Sopenharmony_ci    return qla24xx_read_optrom_data(vha, buf, offset, length);
30668c2ecf20Sopenharmony_ci}
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_ci/**
30698c2ecf20Sopenharmony_ci * qla2x00_get_fcode_version() - Determine an FCODE image's version.
30708c2ecf20Sopenharmony_ci * @ha: HA context
30718c2ecf20Sopenharmony_ci * @pcids: Pointer to the FCODE PCI data structure
30728c2ecf20Sopenharmony_ci *
30738c2ecf20Sopenharmony_ci * The process of retrieving the FCODE version information is at best
30748c2ecf20Sopenharmony_ci * described as interesting.
30758c2ecf20Sopenharmony_ci *
30768c2ecf20Sopenharmony_ci * Within the first 100h bytes of the image an ASCII string is present
30778c2ecf20Sopenharmony_ci * which contains several pieces of information including the FCODE
30788c2ecf20Sopenharmony_ci * version.  Unfortunately it seems the only reliable way to retrieve
30798c2ecf20Sopenharmony_ci * the version is by scanning for another sentinel within the string,
30808c2ecf20Sopenharmony_ci * the FCODE build date:
30818c2ecf20Sopenharmony_ci *
30828c2ecf20Sopenharmony_ci *	... 2.00.02 10/17/02 ...
30838c2ecf20Sopenharmony_ci *
30848c2ecf20Sopenharmony_ci * Returns QLA_SUCCESS on successful retrieval of version.
30858c2ecf20Sopenharmony_ci */
30868c2ecf20Sopenharmony_cistatic void
30878c2ecf20Sopenharmony_ciqla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids)
30888c2ecf20Sopenharmony_ci{
30898c2ecf20Sopenharmony_ci	int ret = QLA_FUNCTION_FAILED;
30908c2ecf20Sopenharmony_ci	uint32_t istart, iend, iter, vend;
30918c2ecf20Sopenharmony_ci	uint8_t do_next, rbyte, *vbyte;
30928c2ecf20Sopenharmony_ci
30938c2ecf20Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_ci	/* Skip the PCI data structure. */
30968c2ecf20Sopenharmony_ci	istart = pcids +
30978c2ecf20Sopenharmony_ci	    ((qla2x00_read_flash_byte(ha, pcids + 0x0B) << 8) |
30988c2ecf20Sopenharmony_ci		qla2x00_read_flash_byte(ha, pcids + 0x0A));
30998c2ecf20Sopenharmony_ci	iend = istart + 0x100;
31008c2ecf20Sopenharmony_ci	do {
31018c2ecf20Sopenharmony_ci		/* Scan for the sentinel date string...eeewww. */
31028c2ecf20Sopenharmony_ci		do_next = 0;
31038c2ecf20Sopenharmony_ci		iter = istart;
31048c2ecf20Sopenharmony_ci		while ((iter < iend) && !do_next) {
31058c2ecf20Sopenharmony_ci			iter++;
31068c2ecf20Sopenharmony_ci			if (qla2x00_read_flash_byte(ha, iter) == '/') {
31078c2ecf20Sopenharmony_ci				if (qla2x00_read_flash_byte(ha, iter + 2) ==
31088c2ecf20Sopenharmony_ci				    '/')
31098c2ecf20Sopenharmony_ci					do_next++;
31108c2ecf20Sopenharmony_ci				else if (qla2x00_read_flash_byte(ha,
31118c2ecf20Sopenharmony_ci				    iter + 3) == '/')
31128c2ecf20Sopenharmony_ci					do_next++;
31138c2ecf20Sopenharmony_ci			}
31148c2ecf20Sopenharmony_ci		}
31158c2ecf20Sopenharmony_ci		if (!do_next)
31168c2ecf20Sopenharmony_ci			break;
31178c2ecf20Sopenharmony_ci
31188c2ecf20Sopenharmony_ci		/* Backtrack to previous ' ' (space). */
31198c2ecf20Sopenharmony_ci		do_next = 0;
31208c2ecf20Sopenharmony_ci		while ((iter > istart) && !do_next) {
31218c2ecf20Sopenharmony_ci			iter--;
31228c2ecf20Sopenharmony_ci			if (qla2x00_read_flash_byte(ha, iter) == ' ')
31238c2ecf20Sopenharmony_ci				do_next++;
31248c2ecf20Sopenharmony_ci		}
31258c2ecf20Sopenharmony_ci		if (!do_next)
31268c2ecf20Sopenharmony_ci			break;
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci		/*
31298c2ecf20Sopenharmony_ci		 * Mark end of version tag, and find previous ' ' (space) or
31308c2ecf20Sopenharmony_ci		 * string length (recent FCODE images -- major hack ahead!!!).
31318c2ecf20Sopenharmony_ci		 */
31328c2ecf20Sopenharmony_ci		vend = iter - 1;
31338c2ecf20Sopenharmony_ci		do_next = 0;
31348c2ecf20Sopenharmony_ci		while ((iter > istart) && !do_next) {
31358c2ecf20Sopenharmony_ci			iter--;
31368c2ecf20Sopenharmony_ci			rbyte = qla2x00_read_flash_byte(ha, iter);
31378c2ecf20Sopenharmony_ci			if (rbyte == ' ' || rbyte == 0xd || rbyte == 0x10)
31388c2ecf20Sopenharmony_ci				do_next++;
31398c2ecf20Sopenharmony_ci		}
31408c2ecf20Sopenharmony_ci		if (!do_next)
31418c2ecf20Sopenharmony_ci			break;
31428c2ecf20Sopenharmony_ci
31438c2ecf20Sopenharmony_ci		/* Mark beginning of version tag, and copy data. */
31448c2ecf20Sopenharmony_ci		iter++;
31458c2ecf20Sopenharmony_ci		if ((vend - iter) &&
31468c2ecf20Sopenharmony_ci		    ((vend - iter) < sizeof(ha->fcode_revision))) {
31478c2ecf20Sopenharmony_ci			vbyte = ha->fcode_revision;
31488c2ecf20Sopenharmony_ci			while (iter <= vend) {
31498c2ecf20Sopenharmony_ci				*vbyte++ = qla2x00_read_flash_byte(ha, iter);
31508c2ecf20Sopenharmony_ci				iter++;
31518c2ecf20Sopenharmony_ci			}
31528c2ecf20Sopenharmony_ci			ret = QLA_SUCCESS;
31538c2ecf20Sopenharmony_ci		}
31548c2ecf20Sopenharmony_ci	} while (0);
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_ci	if (ret != QLA_SUCCESS)
31578c2ecf20Sopenharmony_ci		memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
31588c2ecf20Sopenharmony_ci}
31598c2ecf20Sopenharmony_ci
31608c2ecf20Sopenharmony_ciint
31618c2ecf20Sopenharmony_ciqla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
31628c2ecf20Sopenharmony_ci{
31638c2ecf20Sopenharmony_ci	int ret = QLA_SUCCESS;
31648c2ecf20Sopenharmony_ci	uint8_t code_type, last_image;
31658c2ecf20Sopenharmony_ci	uint32_t pcihdr, pcids;
31668c2ecf20Sopenharmony_ci	uint8_t *dbyte;
31678c2ecf20Sopenharmony_ci	uint16_t *dcode;
31688c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
31698c2ecf20Sopenharmony_ci
31708c2ecf20Sopenharmony_ci	if (!ha->pio_address || !mbuf)
31718c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
31748c2ecf20Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
31758c2ecf20Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
31768c2ecf20Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	qla2x00_flash_enable(ha);
31798c2ecf20Sopenharmony_ci
31808c2ecf20Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
31818c2ecf20Sopenharmony_ci	pcihdr = 0;
31828c2ecf20Sopenharmony_ci	last_image = 1;
31838c2ecf20Sopenharmony_ci	do {
31848c2ecf20Sopenharmony_ci		/* Verify PCI expansion ROM header. */
31858c2ecf20Sopenharmony_ci		if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
31868c2ecf20Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
31878c2ecf20Sopenharmony_ci			/* No signature */
31888c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0050,
31898c2ecf20Sopenharmony_ci			    "No matching ROM signature.\n");
31908c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
31918c2ecf20Sopenharmony_ci			break;
31928c2ecf20Sopenharmony_ci		}
31938c2ecf20Sopenharmony_ci
31948c2ecf20Sopenharmony_ci		/* Locate PCI data structure. */
31958c2ecf20Sopenharmony_ci		pcids = pcihdr +
31968c2ecf20Sopenharmony_ci		    ((qla2x00_read_flash_byte(ha, pcihdr + 0x19) << 8) |
31978c2ecf20Sopenharmony_ci			qla2x00_read_flash_byte(ha, pcihdr + 0x18));
31988c2ecf20Sopenharmony_ci
31998c2ecf20Sopenharmony_ci		/* Validate signature of PCI data structure. */
32008c2ecf20Sopenharmony_ci		if (qla2x00_read_flash_byte(ha, pcids) != 'P' ||
32018c2ecf20Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x1) != 'C' ||
32028c2ecf20Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
32038c2ecf20Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
32048c2ecf20Sopenharmony_ci			/* Incorrect header. */
32058c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0051,
32068c2ecf20Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
32078c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
32088c2ecf20Sopenharmony_ci			break;
32098c2ecf20Sopenharmony_ci		}
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_ci		/* Read version */
32128c2ecf20Sopenharmony_ci		code_type = qla2x00_read_flash_byte(ha, pcids + 0x14);
32138c2ecf20Sopenharmony_ci		switch (code_type) {
32148c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
32158c2ecf20Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
32168c2ecf20Sopenharmony_ci			ha->bios_revision[0] =
32178c2ecf20Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x12);
32188c2ecf20Sopenharmony_ci			ha->bios_revision[1] =
32198c2ecf20Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x13);
32208c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0052,
32218c2ecf20Sopenharmony_ci			    "Read BIOS %d.%d.\n",
32228c2ecf20Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
32238c2ecf20Sopenharmony_ci			break;
32248c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
32258c2ecf20Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
32268c2ecf20Sopenharmony_ci			/* Eeeewww... */
32278c2ecf20Sopenharmony_ci			qla2x00_get_fcode_version(ha, pcids);
32288c2ecf20Sopenharmony_ci			break;
32298c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
32308c2ecf20Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
32318c2ecf20Sopenharmony_ci			ha->efi_revision[0] =
32328c2ecf20Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x12);
32338c2ecf20Sopenharmony_ci			ha->efi_revision[1] =
32348c2ecf20Sopenharmony_ci			    qla2x00_read_flash_byte(ha, pcids + 0x13);
32358c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0053,
32368c2ecf20Sopenharmony_ci			    "Read EFI %d.%d.\n",
32378c2ecf20Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
32388c2ecf20Sopenharmony_ci			break;
32398c2ecf20Sopenharmony_ci		default:
32408c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0054,
32418c2ecf20Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
32428c2ecf20Sopenharmony_ci			    code_type, pcids);
32438c2ecf20Sopenharmony_ci			break;
32448c2ecf20Sopenharmony_ci		}
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci		last_image = qla2x00_read_flash_byte(ha, pcids + 0x15) & BIT_7;
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci		/* Locate next PCI expansion ROM. */
32498c2ecf20Sopenharmony_ci		pcihdr += ((qla2x00_read_flash_byte(ha, pcids + 0x11) << 8) |
32508c2ecf20Sopenharmony_ci		    qla2x00_read_flash_byte(ha, pcids + 0x10)) * 512;
32518c2ecf20Sopenharmony_ci	} while (!last_image);
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_ci	if (IS_QLA2322(ha)) {
32548c2ecf20Sopenharmony_ci		/* Read firmware image information. */
32558c2ecf20Sopenharmony_ci		memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
32568c2ecf20Sopenharmony_ci		dbyte = mbuf;
32578c2ecf20Sopenharmony_ci		memset(dbyte, 0, 8);
32588c2ecf20Sopenharmony_ci		dcode = (uint16_t *)dbyte;
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci		qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
32618c2ecf20Sopenharmony_ci		    8);
32628c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010a,
32638c2ecf20Sopenharmony_ci		    "Dumping fw "
32648c2ecf20Sopenharmony_ci		    "ver from flash:.\n");
32658c2ecf20Sopenharmony_ci		ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010b,
32668c2ecf20Sopenharmony_ci		    dbyte, 32);
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_ci		if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
32698c2ecf20Sopenharmony_ci		    dcode[2] == 0xffff && dcode[3] == 0xffff) ||
32708c2ecf20Sopenharmony_ci		    (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
32718c2ecf20Sopenharmony_ci		    dcode[3] == 0)) {
32728c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0057,
32738c2ecf20Sopenharmony_ci			    "Unrecognized fw revision at %x.\n",
32748c2ecf20Sopenharmony_ci			    ha->flt_region_fw * 4);
32758c2ecf20Sopenharmony_ci		} else {
32768c2ecf20Sopenharmony_ci			/* values are in big endian */
32778c2ecf20Sopenharmony_ci			ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
32788c2ecf20Sopenharmony_ci			ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3];
32798c2ecf20Sopenharmony_ci			ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5];
32808c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0058,
32818c2ecf20Sopenharmony_ci			    "FW Version: "
32828c2ecf20Sopenharmony_ci			    "%d.%d.%d.\n", ha->fw_revision[0],
32838c2ecf20Sopenharmony_ci			    ha->fw_revision[1], ha->fw_revision[2]);
32848c2ecf20Sopenharmony_ci		}
32858c2ecf20Sopenharmony_ci	}
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci	qla2x00_flash_disable(ha);
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	return ret;
32908c2ecf20Sopenharmony_ci}
32918c2ecf20Sopenharmony_ci
32928c2ecf20Sopenharmony_ciint
32938c2ecf20Sopenharmony_ciqla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
32948c2ecf20Sopenharmony_ci{
32958c2ecf20Sopenharmony_ci	int ret = QLA_SUCCESS;
32968c2ecf20Sopenharmony_ci	uint32_t pcihdr, pcids;
32978c2ecf20Sopenharmony_ci	uint32_t *dcode = mbuf;
32988c2ecf20Sopenharmony_ci	uint8_t *bcode = mbuf;
32998c2ecf20Sopenharmony_ci	uint8_t code_type, last_image;
33008c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci	if (!mbuf)
33038c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
33068c2ecf20Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
33078c2ecf20Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
33088c2ecf20Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	/* Begin with first PCI expansion ROM header. */
33118c2ecf20Sopenharmony_ci	pcihdr = ha->flt_region_boot << 2;
33128c2ecf20Sopenharmony_ci	last_image = 1;
33138c2ecf20Sopenharmony_ci	do {
33148c2ecf20Sopenharmony_ci		/* Verify PCI expansion ROM header. */
33158c2ecf20Sopenharmony_ci		ha->isp_ops->read_optrom(vha, dcode, pcihdr, 0x20 * 4);
33168c2ecf20Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
33178c2ecf20Sopenharmony_ci		if (memcmp(bcode, "\x55\xaa", 2)) {
33188c2ecf20Sopenharmony_ci			/* No signature */
33198c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0154,
33208c2ecf20Sopenharmony_ci			    "No matching ROM signature.\n");
33218c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
33228c2ecf20Sopenharmony_ci			break;
33238c2ecf20Sopenharmony_ci		}
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci		/* Locate PCI data structure. */
33268c2ecf20Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_ci		ha->isp_ops->read_optrom(vha, dcode, pcids, 0x20 * 4);
33298c2ecf20Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
33308c2ecf20Sopenharmony_ci
33318c2ecf20Sopenharmony_ci		/* Validate signature of PCI data structure. */
33328c2ecf20Sopenharmony_ci		if (memcmp(bcode, "PCIR", 4)) {
33338c2ecf20Sopenharmony_ci			/* Incorrect header. */
33348c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0155,
33358c2ecf20Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
33368c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
33378c2ecf20Sopenharmony_ci			break;
33388c2ecf20Sopenharmony_ci		}
33398c2ecf20Sopenharmony_ci
33408c2ecf20Sopenharmony_ci		/* Read version */
33418c2ecf20Sopenharmony_ci		code_type = bcode[0x14];
33428c2ecf20Sopenharmony_ci		switch (code_type) {
33438c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
33448c2ecf20Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
33458c2ecf20Sopenharmony_ci			ha->bios_revision[0] = bcode[0x12];
33468c2ecf20Sopenharmony_ci			ha->bios_revision[1] = bcode[0x13];
33478c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0156,
33488c2ecf20Sopenharmony_ci			    "Read BIOS %d.%d.\n",
33498c2ecf20Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
33508c2ecf20Sopenharmony_ci			break;
33518c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
33528c2ecf20Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
33538c2ecf20Sopenharmony_ci			ha->fcode_revision[0] = bcode[0x12];
33548c2ecf20Sopenharmony_ci			ha->fcode_revision[1] = bcode[0x13];
33558c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0157,
33568c2ecf20Sopenharmony_ci			    "Read FCODE %d.%d.\n",
33578c2ecf20Sopenharmony_ci			    ha->fcode_revision[1], ha->fcode_revision[0]);
33588c2ecf20Sopenharmony_ci			break;
33598c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
33608c2ecf20Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
33618c2ecf20Sopenharmony_ci			ha->efi_revision[0] = bcode[0x12];
33628c2ecf20Sopenharmony_ci			ha->efi_revision[1] = bcode[0x13];
33638c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x0158,
33648c2ecf20Sopenharmony_ci			    "Read EFI %d.%d.\n",
33658c2ecf20Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
33668c2ecf20Sopenharmony_ci			break;
33678c2ecf20Sopenharmony_ci		default:
33688c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x0159,
33698c2ecf20Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
33708c2ecf20Sopenharmony_ci			    code_type, pcids);
33718c2ecf20Sopenharmony_ci			break;
33728c2ecf20Sopenharmony_ci		}
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_ci		/* Locate next PCI expansion ROM. */
33778c2ecf20Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
33788c2ecf20Sopenharmony_ci	} while (!last_image);
33798c2ecf20Sopenharmony_ci
33808c2ecf20Sopenharmony_ci	/* Read firmware image information. */
33818c2ecf20Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
33828c2ecf20Sopenharmony_ci	dcode = mbuf;
33838c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, dcode, ha->flt_region_fw << 2, 0x20);
33848c2ecf20Sopenharmony_ci	bcode = mbuf + (pcihdr % 4);
33858c2ecf20Sopenharmony_ci
33868c2ecf20Sopenharmony_ci	/* Validate signature of PCI data structure. */
33878c2ecf20Sopenharmony_ci	if (bcode[0x0] == 0x3 && bcode[0x1] == 0x0 &&
33888c2ecf20Sopenharmony_ci	    bcode[0x2] == 0x40 && bcode[0x3] == 0x40) {
33898c2ecf20Sopenharmony_ci		ha->fw_revision[0] = bcode[0x4];
33908c2ecf20Sopenharmony_ci		ha->fw_revision[1] = bcode[0x5];
33918c2ecf20Sopenharmony_ci		ha->fw_revision[2] = bcode[0x6];
33928c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0153,
33938c2ecf20Sopenharmony_ci		    "Firmware revision %d.%d.%d\n",
33948c2ecf20Sopenharmony_ci		    ha->fw_revision[0], ha->fw_revision[1],
33958c2ecf20Sopenharmony_ci		    ha->fw_revision[2]);
33968c2ecf20Sopenharmony_ci	}
33978c2ecf20Sopenharmony_ci
33988c2ecf20Sopenharmony_ci	return ret;
33998c2ecf20Sopenharmony_ci}
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ciint
34028c2ecf20Sopenharmony_ciqla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
34038c2ecf20Sopenharmony_ci{
34048c2ecf20Sopenharmony_ci	int ret = QLA_SUCCESS;
34058c2ecf20Sopenharmony_ci	uint32_t pcihdr = 0, pcids = 0;
34068c2ecf20Sopenharmony_ci	uint32_t *dcode = mbuf;
34078c2ecf20Sopenharmony_ci	uint8_t *bcode = mbuf;
34088c2ecf20Sopenharmony_ci	uint8_t code_type, last_image;
34098c2ecf20Sopenharmony_ci	int i;
34108c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
34118c2ecf20Sopenharmony_ci	uint32_t faddr = 0;
34128c2ecf20Sopenharmony_ci	struct active_regions active_regions = { };
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	if (IS_P3P_TYPE(ha))
34158c2ecf20Sopenharmony_ci		return ret;
34168c2ecf20Sopenharmony_ci
34178c2ecf20Sopenharmony_ci	if (!mbuf)
34188c2ecf20Sopenharmony_ci		return QLA_FUNCTION_FAILED;
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_ci	memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
34218c2ecf20Sopenharmony_ci	memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
34228c2ecf20Sopenharmony_ci	memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
34238c2ecf20Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
34248c2ecf20Sopenharmony_ci
34258c2ecf20Sopenharmony_ci	pcihdr = ha->flt_region_boot << 2;
34268c2ecf20Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
34278c2ecf20Sopenharmony_ci		qla27xx_get_active_image(vha, &active_regions);
34288c2ecf20Sopenharmony_ci		if (active_regions.global == QLA27XX_SECONDARY_IMAGE) {
34298c2ecf20Sopenharmony_ci			pcihdr = ha->flt_region_boot_sec << 2;
34308c2ecf20Sopenharmony_ci		}
34318c2ecf20Sopenharmony_ci	}
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	do {
34348c2ecf20Sopenharmony_ci		/* Verify PCI expansion ROM header. */
34358c2ecf20Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
34368c2ecf20Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
34378c2ecf20Sopenharmony_ci		if (memcmp(bcode, "\x55\xaa", 2)) {
34388c2ecf20Sopenharmony_ci			/* No signature */
34398c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x0059,
34408c2ecf20Sopenharmony_ci			    "No matching ROM signature.\n");
34418c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
34428c2ecf20Sopenharmony_ci			break;
34438c2ecf20Sopenharmony_ci		}
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_ci		/* Locate PCI data structure. */
34468c2ecf20Sopenharmony_ci		pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci		qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
34498c2ecf20Sopenharmony_ci		bcode = mbuf + (pcihdr % 4);
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci		/* Validate signature of PCI data structure. */
34528c2ecf20Sopenharmony_ci		if (memcmp(bcode, "PCIR", 4)) {
34538c2ecf20Sopenharmony_ci			/* Incorrect header. */
34548c2ecf20Sopenharmony_ci			ql_log(ql_log_fatal, vha, 0x005a,
34558c2ecf20Sopenharmony_ci			    "PCI data struct not found pcir_adr=%x.\n", pcids);
34568c2ecf20Sopenharmony_ci			ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32);
34578c2ecf20Sopenharmony_ci			ret = QLA_FUNCTION_FAILED;
34588c2ecf20Sopenharmony_ci			break;
34598c2ecf20Sopenharmony_ci		}
34608c2ecf20Sopenharmony_ci
34618c2ecf20Sopenharmony_ci		/* Read version */
34628c2ecf20Sopenharmony_ci		code_type = bcode[0x14];
34638c2ecf20Sopenharmony_ci		switch (code_type) {
34648c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_BIOS:
34658c2ecf20Sopenharmony_ci			/* Intel x86, PC-AT compatible. */
34668c2ecf20Sopenharmony_ci			ha->bios_revision[0] = bcode[0x12];
34678c2ecf20Sopenharmony_ci			ha->bios_revision[1] = bcode[0x13];
34688c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005b,
34698c2ecf20Sopenharmony_ci			    "Read BIOS %d.%d.\n",
34708c2ecf20Sopenharmony_ci			    ha->bios_revision[1], ha->bios_revision[0]);
34718c2ecf20Sopenharmony_ci			break;
34728c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_FCODE:
34738c2ecf20Sopenharmony_ci			/* Open Firmware standard for PCI (FCode). */
34748c2ecf20Sopenharmony_ci			ha->fcode_revision[0] = bcode[0x12];
34758c2ecf20Sopenharmony_ci			ha->fcode_revision[1] = bcode[0x13];
34768c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005c,
34778c2ecf20Sopenharmony_ci			    "Read FCODE %d.%d.\n",
34788c2ecf20Sopenharmony_ci			    ha->fcode_revision[1], ha->fcode_revision[0]);
34798c2ecf20Sopenharmony_ci			break;
34808c2ecf20Sopenharmony_ci		case ROM_CODE_TYPE_EFI:
34818c2ecf20Sopenharmony_ci			/* Extensible Firmware Interface (EFI). */
34828c2ecf20Sopenharmony_ci			ha->efi_revision[0] = bcode[0x12];
34838c2ecf20Sopenharmony_ci			ha->efi_revision[1] = bcode[0x13];
34848c2ecf20Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0x005d,
34858c2ecf20Sopenharmony_ci			    "Read EFI %d.%d.\n",
34868c2ecf20Sopenharmony_ci			    ha->efi_revision[1], ha->efi_revision[0]);
34878c2ecf20Sopenharmony_ci			break;
34888c2ecf20Sopenharmony_ci		default:
34898c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x005e,
34908c2ecf20Sopenharmony_ci			    "Unrecognized code type %x at pcids %x.\n",
34918c2ecf20Sopenharmony_ci			    code_type, pcids);
34928c2ecf20Sopenharmony_ci			break;
34938c2ecf20Sopenharmony_ci		}
34948c2ecf20Sopenharmony_ci
34958c2ecf20Sopenharmony_ci		last_image = bcode[0x15] & BIT_7;
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci		/* Locate next PCI expansion ROM. */
34988c2ecf20Sopenharmony_ci		pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
34998c2ecf20Sopenharmony_ci	} while (!last_image);
35008c2ecf20Sopenharmony_ci
35018c2ecf20Sopenharmony_ci	/* Read firmware image information. */
35028c2ecf20Sopenharmony_ci	memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
35038c2ecf20Sopenharmony_ci	faddr = ha->flt_region_fw;
35048c2ecf20Sopenharmony_ci	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
35058c2ecf20Sopenharmony_ci		qla27xx_get_active_image(vha, &active_regions);
35068c2ecf20Sopenharmony_ci		if (active_regions.global == QLA27XX_SECONDARY_IMAGE)
35078c2ecf20Sopenharmony_ci			faddr = ha->flt_region_fw_sec;
35088c2ecf20Sopenharmony_ci	}
35098c2ecf20Sopenharmony_ci
35108c2ecf20Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, faddr, 8);
35118c2ecf20Sopenharmony_ci	if (qla24xx_risc_firmware_invalid(dcode)) {
35128c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x005f,
35138c2ecf20Sopenharmony_ci		    "Unrecognized fw revision at %x.\n",
35148c2ecf20Sopenharmony_ci		    ha->flt_region_fw * 4);
35158c2ecf20Sopenharmony_ci		ql_dump_buffer(ql_dbg_init, vha, 0x005f, dcode, 32);
35168c2ecf20Sopenharmony_ci	} else {
35178c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++)
35188c2ecf20Sopenharmony_ci			ha->fw_revision[i] =
35198c2ecf20Sopenharmony_ci				be32_to_cpu((__force __be32)dcode[4+i]);
35208c2ecf20Sopenharmony_ci		ql_dbg(ql_dbg_init, vha, 0x0060,
35218c2ecf20Sopenharmony_ci		    "Firmware revision (flash) %u.%u.%u (%x).\n",
35228c2ecf20Sopenharmony_ci		    ha->fw_revision[0], ha->fw_revision[1],
35238c2ecf20Sopenharmony_ci		    ha->fw_revision[2], ha->fw_revision[3]);
35248c2ecf20Sopenharmony_ci	}
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_ci	/* Check for golden firmware and get version if available */
35278c2ecf20Sopenharmony_ci	if (!IS_QLA81XX(ha)) {
35288c2ecf20Sopenharmony_ci		/* Golden firmware is not present in non 81XX adapters */
35298c2ecf20Sopenharmony_ci		return ret;
35308c2ecf20Sopenharmony_ci	}
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	memset(ha->gold_fw_version, 0, sizeof(ha->gold_fw_version));
35338c2ecf20Sopenharmony_ci	faddr = ha->flt_region_gold_fw;
35348c2ecf20Sopenharmony_ci	qla24xx_read_flash_data(vha, dcode, ha->flt_region_gold_fw, 8);
35358c2ecf20Sopenharmony_ci	if (qla24xx_risc_firmware_invalid(dcode)) {
35368c2ecf20Sopenharmony_ci		ql_log(ql_log_warn, vha, 0x0056,
35378c2ecf20Sopenharmony_ci		    "Unrecognized golden fw at %#x.\n", faddr);
35388c2ecf20Sopenharmony_ci		ql_dump_buffer(ql_dbg_init, vha, 0x0056, dcode, 32);
35398c2ecf20Sopenharmony_ci		return ret;
35408c2ecf20Sopenharmony_ci	}
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++)
35438c2ecf20Sopenharmony_ci		ha->gold_fw_version[i] =
35448c2ecf20Sopenharmony_ci			be32_to_cpu((__force __be32)dcode[4+i]);
35458c2ecf20Sopenharmony_ci
35468c2ecf20Sopenharmony_ci	return ret;
35478c2ecf20Sopenharmony_ci}
35488c2ecf20Sopenharmony_ci
35498c2ecf20Sopenharmony_cistatic int
35508c2ecf20Sopenharmony_ciqla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
35518c2ecf20Sopenharmony_ci{
35528c2ecf20Sopenharmony_ci	if (pos >= end || *pos != 0x82)
35538c2ecf20Sopenharmony_ci		return 0;
35548c2ecf20Sopenharmony_ci
35558c2ecf20Sopenharmony_ci	pos += 3 + pos[1];
35568c2ecf20Sopenharmony_ci	if (pos >= end || *pos != 0x90)
35578c2ecf20Sopenharmony_ci		return 0;
35588c2ecf20Sopenharmony_ci
35598c2ecf20Sopenharmony_ci	pos += 3 + pos[1];
35608c2ecf20Sopenharmony_ci	if (pos >= end || *pos != 0x78)
35618c2ecf20Sopenharmony_ci		return 0;
35628c2ecf20Sopenharmony_ci
35638c2ecf20Sopenharmony_ci	return 1;
35648c2ecf20Sopenharmony_ci}
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_ciint
35678c2ecf20Sopenharmony_ciqla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size)
35688c2ecf20Sopenharmony_ci{
35698c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
35708c2ecf20Sopenharmony_ci	uint8_t *pos = ha->vpd;
35718c2ecf20Sopenharmony_ci	uint8_t *end = pos + ha->vpd_size;
35728c2ecf20Sopenharmony_ci	int len = 0;
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci	if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end))
35758c2ecf20Sopenharmony_ci		return 0;
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	while (pos < end && *pos != 0x78) {
35788c2ecf20Sopenharmony_ci		len = (*pos == 0x82) ? pos[1] : pos[2];
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci		if (!strncmp(pos, key, strlen(key)))
35818c2ecf20Sopenharmony_ci			break;
35828c2ecf20Sopenharmony_ci
35838c2ecf20Sopenharmony_ci		if (*pos != 0x90 && *pos != 0x91)
35848c2ecf20Sopenharmony_ci			pos += len;
35858c2ecf20Sopenharmony_ci
35868c2ecf20Sopenharmony_ci		pos += 3;
35878c2ecf20Sopenharmony_ci	}
35888c2ecf20Sopenharmony_ci
35898c2ecf20Sopenharmony_ci	if (pos < end - len && *pos != 0x78)
35908c2ecf20Sopenharmony_ci		return scnprintf(str, size, "%.*s", len, pos + 3);
35918c2ecf20Sopenharmony_ci
35928c2ecf20Sopenharmony_ci	return 0;
35938c2ecf20Sopenharmony_ci}
35948c2ecf20Sopenharmony_ci
35958c2ecf20Sopenharmony_ciint
35968c2ecf20Sopenharmony_ciqla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha)
35978c2ecf20Sopenharmony_ci{
35988c2ecf20Sopenharmony_ci	int len, max_len;
35998c2ecf20Sopenharmony_ci	uint32_t fcp_prio_addr;
36008c2ecf20Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
36018c2ecf20Sopenharmony_ci
36028c2ecf20Sopenharmony_ci	if (!ha->fcp_prio_cfg) {
36038c2ecf20Sopenharmony_ci		ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
36048c2ecf20Sopenharmony_ci		if (!ha->fcp_prio_cfg) {
36058c2ecf20Sopenharmony_ci			ql_log(ql_log_warn, vha, 0x00d5,
36068c2ecf20Sopenharmony_ci			    "Unable to allocate memory for fcp priority data (%x).\n",
36078c2ecf20Sopenharmony_ci			    FCP_PRIO_CFG_SIZE);
36088c2ecf20Sopenharmony_ci			return QLA_FUNCTION_FAILED;
36098c2ecf20Sopenharmony_ci		}
36108c2ecf20Sopenharmony_ci	}
36118c2ecf20Sopenharmony_ci	memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE);
36128c2ecf20Sopenharmony_ci
36138c2ecf20Sopenharmony_ci	fcp_prio_addr = ha->flt_region_fcp_prio;
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_ci	/* first read the fcp priority data header from flash */
36168c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, ha->fcp_prio_cfg,
36178c2ecf20Sopenharmony_ci			fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE);
36188c2ecf20Sopenharmony_ci
36198c2ecf20Sopenharmony_ci	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0))
36208c2ecf20Sopenharmony_ci		goto fail;
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_ci	/* read remaining FCP CMD config data from flash */
36238c2ecf20Sopenharmony_ci	fcp_prio_addr += (FCP_PRIO_CFG_HDR_SIZE >> 2);
36248c2ecf20Sopenharmony_ci	len = ha->fcp_prio_cfg->num_entries * sizeof(struct qla_fcp_prio_entry);
36258c2ecf20Sopenharmony_ci	max_len = FCP_PRIO_CFG_SIZE - FCP_PRIO_CFG_HDR_SIZE;
36268c2ecf20Sopenharmony_ci
36278c2ecf20Sopenharmony_ci	ha->isp_ops->read_optrom(vha, &ha->fcp_prio_cfg->entry[0],
36288c2ecf20Sopenharmony_ci			fcp_prio_addr << 2, (len < max_len ? len : max_len));
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_ci	/* revalidate the entire FCP priority config data, including entries */
36318c2ecf20Sopenharmony_ci	if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1))
36328c2ecf20Sopenharmony_ci		goto fail;
36338c2ecf20Sopenharmony_ci
36348c2ecf20Sopenharmony_ci	ha->flags.fcp_prio_enabled = 1;
36358c2ecf20Sopenharmony_ci	return QLA_SUCCESS;
36368c2ecf20Sopenharmony_cifail:
36378c2ecf20Sopenharmony_ci	vfree(ha->fcp_prio_cfg);
36388c2ecf20Sopenharmony_ci	ha->fcp_prio_cfg = NULL;
36398c2ecf20Sopenharmony_ci	return QLA_FUNCTION_FAILED;
36408c2ecf20Sopenharmony_ci}
3641