/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 QLogic Corporation. All rights reserved.
*/
#include <qlge.h>
/*
* Local Function Prototypes.
*/
static int ql_read_flash(qlge_t *, uint32_t, uint32_t *);
static int ql_write_flash(qlge_t *, uint32_t, uint32_t);
static int ql_protect_flash(qlge_t *);
static int ql_unprotect_flash(qlge_t *);
/*
* ql_flash_id
* The flash memory chip exports 3 ID bytes in the order of manufacturer, id,
* capability
*/
int
ql_flash_id(qlge_t *qlge)
{
int rval;
uint32_t fdata = 0;
/*
* Send Restore command (0xAB) to release Flash from
* possible deep power down state
*/
rval = ql_read_flash(qlge, FLASH_CONF_ADDR | 0x300 | FLASH_RES_CMD,
&fdata);
QL_PRINT(DBG_FLASH, ("%s(%d) flash electronic signature is %x \n",
__func__, qlge->instance, fdata));
fdata = 0;
/* 0x9F */
rval = ql_read_flash(qlge, FLASH_CONF_ADDR | 0x0400 | FLASH_RDID_CMD,
&fdata);
if ((rval != DDI_SUCCESS) || (fdata == 0)) {
cmn_err(CE_WARN, "%s(%d) read_flash failed 0x%x.",
__func__, qlge->instance, fdata);
} else {
qlge->flash_info.flash_manuf = LSB(LSW(fdata));
qlge->flash_info.flash_id = MSB(LSW(fdata));
qlge->flash_info.flash_cap = LSB(MSW(fdata));
QL_PRINT(DBG_FLASH, ("%s(%d) flash manufacturer 0x%x,"
" flash id 0x%x, flash cap 0x%x\n",
__func__, qlge->instance,
qlge->flash_info.flash_manuf, qlge->flash_info.flash_id,
qlge->flash_info.flash_cap));
}
return (rval);
}
/*
* qlge_dump_fcode
* Dumps fcode from flash.
*/
int
qlge_dump_fcode(qlge_t *qlge, uint8_t *dp, uint32_t size, uint32_t startpos)
{
uint32_t cnt, data, addr;
int rval = DDI_SUCCESS;
QL_PRINT(DBG_FLASH, ("%s(%d) entered to read address %x, %x bytes\n",
__func__, qlge->instance, startpos, size));
/* make sure startpos+size doesn't exceed flash */
if (size + startpos > qlge->fdesc.flash_size) {
cmn_err(CE_WARN, "%s(%d) exceeded flash range, sz=%xh, stp=%xh,"
" flsz=%xh", __func__, qlge->instance,
size, startpos, qlge->fdesc.flash_size);
return (DDI_FAILURE);
}
/* check start addr is 32 bit or 4 byte aligned for M25Pxx */
if ((startpos & 0x3) != 0) {
cmn_err(CE_WARN, "%s(%d) incorrect buffer size alignment",
__func__, qlge->instance);
return (DDI_FAILURE);
}
/* adjust flash start addr for 32 bit words */
addr = startpos / 4;
/* Read fcode data from flash. */
cnt = startpos;
size += startpos;
while (cnt < size) {
/* Allow other system activity. */
if (cnt % 0x1000 == 0) {
drv_usecwait(1);
}
rval = ql_read_flash(qlge, addr++, &data);
if (rval != DDI_SUCCESS) {
break;
}
*dp++ = LSB(LSW(data));
*dp++ = MSB(LSW(data));
*dp++ = LSB(MSW(data));
*dp++ = MSB(MSW(data));
cnt += 4;
}
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "failed, rval = %xh", rval);
}
return (rval);
}
int
ql_erase_and_write_to_flash(qlge_t *qlge, uint8_t *dp, uint32_t size,
uint32_t faddr)
{
int rval = DDI_FAILURE;
uint32_t cnt, rest_addr, fdata;
QL_PRINT(DBG_FLASH, ("%s(%d) entered to write addr %x, %d bytes\n",
__func__, qlge->instance, faddr, size));
/* start address must be 32 bit word aligned */
if ((faddr & 0x3) != 0) {
cmn_err(CE_WARN, "%s(%d) incorrect buffer size alignment",
__func__, qlge->instance);
return (DDI_FAILURE);
}
/* setup mask of address range within a sector */
rest_addr = (qlge->fdesc.block_size - 1) >> 2;
faddr = faddr >> 2; /* flash gets 32 bit words */
/*
* Write data to flash.
*/
cnt = 0;
size = (size + 3) >> 2; /* Round up & convert to dwords */
while (cnt < size) {
/* Beginning of a sector? do a sector erase */
if ((faddr & rest_addr) == 0) {
fdata = (faddr & ~rest_addr) << 2;
fdata = (fdata & 0xff00) |
(fdata << 16 & 0xff0000) |
(fdata >> 16 & 0xff);
/* 64k bytes sector erase */
rval = ql_write_flash(qlge, /* 0xd8 */
FLASH_CONF_ADDR | 0x0300 | qlge->fdesc.erase_cmd,
fdata);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "Unable to flash sector: "
"address=%xh", faddr);
goto out;
}
}
/* Write data */
fdata = *dp++;
fdata |= *dp++ << 8;
fdata |= *dp++ << 16;
fdata |= *dp++ << 24;
rval = ql_write_flash(qlge, faddr, fdata);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "Unable to program flash "
"address=%xh data=%xh", faddr,
*dp);
goto out;
}
cnt++;
faddr++;
/* Allow other system activity. */
if (cnt % 0x1000 == 0) {
qlge_delay(10000);
}
}
rval = DDI_SUCCESS;
out:
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d failed=%xh",
__func__, qlge->instance, rval);
}
return (rval);
}
void
get_sector_number(qlge_t *qlge, uint32_t faddr, uint32_t *psector)
{
*psector = faddr / qlge->fdesc.block_size; /* 0x10000 */
}
/*
* qlge_load_flash
* Write "size" bytes from memory "dp" to flash address "faddr".
* faddr = 32bit word flash address.
*/
int
qlge_load_flash(qlge_t *qlge, uint8_t *dp, uint32_t len, uint32_t faddr)
{
int rval = DDI_FAILURE;
uint32_t start_block, end_block;
uint32_t start_byte, end_byte;
uint32_t num;
uint32_t sector_size, addr_src, addr_desc;
uint8_t *temp;
caddr_t bp, bdesc;
QL_PRINT(DBG_FLASH, ("%s(%d) entered to write addr %x, %d bytes\n",
__func__, qlge->instance, faddr, len));
sector_size = qlge->fdesc.block_size;
if (faddr > qlge->fdesc.flash_size) {
cmn_err(CE_WARN, "%s(%d): invalid flash write address %x",
__func__, qlge->instance, faddr);
return (DDI_FAILURE);
}
/* Get semaphore to access Flash Address and Flash Data Registers */
if (ql_sem_spinlock(qlge, QL_FLASH_SEM_MASK) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
temp = kmem_zalloc(sector_size, KM_SLEEP);
if (temp == NULL) {
cmn_err(CE_WARN, "%s(%d): Unable to allocate buffer",
__func__, qlge->instance);
ql_sem_unlock(qlge, QL_FLASH_SEM_MASK);
return (DDI_FAILURE);
}
(void) ql_unprotect_flash(qlge);
get_sector_number(qlge, faddr, &start_block);
get_sector_number(qlge, faddr + len - 1, &end_block);
QL_PRINT(DBG_FLASH, ("%s(%d) start_block %x, end_block %x\n",
__func__, qlge->instance, start_block, end_block));
for (num = start_block; num <= end_block; num++) {
QL_PRINT(DBG_FLASH,
("%s(%d) sector_size 0x%x, sector read addr %x\n",
__func__, qlge->instance, sector_size, num * sector_size));
/* read one whole sector flash data to buffer */
rval = qlge_dump_fcode(qlge, (uint8_t *)temp, sector_size,
num * sector_size);
start_byte = num * sector_size;
end_byte = start_byte + sector_size -1;
if (start_byte < faddr)
start_byte = faddr;
if (end_byte > (faddr + len))
end_byte = (faddr + len - 1);
addr_src = start_byte - faddr;
addr_desc = start_byte - num * sector_size;
bp = (caddr_t)dp + addr_src;
bdesc = (caddr_t)temp + addr_desc;
bcopy(bp, bdesc, (end_byte - start_byte + 1));
/* write the whole sector data to flash */
if (ql_erase_and_write_to_flash(qlge, temp, sector_size,
num * sector_size) != DDI_SUCCESS)
goto out;
}
rval = DDI_SUCCESS;
out:
(void) ql_protect_flash(qlge);
kmem_free(temp, sector_size);
ql_sem_unlock(qlge, QL_FLASH_SEM_MASK);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d failed=%xh",
__func__, qlge->instance, rval);
}
return (rval);
}
/*
* ql_check_pci
* checks the passed buffer for a valid pci signature and
* expected (and in range) pci length values.
* On successful pci check, nextpos adjusted to next pci header.
*/
static int
ql_check_pci(qlge_t *qlge, uint8_t *buf, uint32_t *nextpos)
{
pci_header_t *pcih;
pci_data_t *pcid;
uint32_t doff;
uint8_t *pciinfo;
uint32_t image_size = 0;
int rval = CONTINUE_SEARCH;
QL_PRINT(DBG_FLASH, ("%s(%d) check image at 0x%x\n",
__func__, qlge->instance, *nextpos));
if (buf != NULL) {
pciinfo = buf;
} else {
cmn_err(CE_WARN, "%s(%d) failed, null buf ptr passed",
__func__, qlge->instance);
return (STOP_SEARCH);
}
/* get the pci header image length */
pcih = (pci_header_t *)pciinfo;
doff = pcih->dataoffset[1];
doff <<= 8;
doff |= pcih->dataoffset[0];
/* some header section sanity check */
if (pcih->signature[0] != PCI_HEADER0 /* '55' */ ||
pcih->signature[1] != PCI_HEADER1 /* 'AA' */ || doff > 50) {
cmn_err(CE_WARN, "%s(%d) image format error: s0=%xh, s1=%xh,"
"off=%xh\n", __func__, qlge->instance,
pcih->signature[0], pcih->signature[1], doff);
return (STOP_SEARCH);
}
pcid = (pci_data_t *)(pciinfo + doff);
/* a slight sanity data section check */
if (pcid->signature[0] != 'P' || pcid->signature[1] != 'C' ||
pcid->signature[2] != 'I' || pcid->signature[3] != 'R') {
cmn_err(CE_WARN, "%s(%d) failed, data sig mismatch!",
__func__, qlge->instance);
return (STOP_SEARCH);
}
image_size =
(pcid->imagelength[0] | (pcid->imagelength[1] << 8))*
PCI_SECTOR_SIZE /* 512 */;
switch (pcid->codetype) {
case PCI_CODE_X86PC:
QL_PRINT(DBG_FLASH, ("%s(%d) boot image is FTYPE_BIOS \n",
__func__, qlge->instance));
break;
case PCI_CODE_FCODE:
QL_PRINT(DBG_FLASH, ("%s(%d) boot image is FTYPE_FCODE \n",
__func__, qlge->instance));
break;
case PCI_CODE_EFI:
QL_PRINT(DBG_FLASH, ("%s(%d) boot image is FTYPE_EFI \n",
__func__, qlge->instance));
break;
case PCI_CODE_HPPA:
QL_PRINT(DBG_FLASH, ("%s(%d) boot image is PCI_CODE_HPPA \n",
__func__, qlge->instance));
break;
default:
QL_PRINT(DBG_FLASH, ("%s(%d) boot image is FTYPE_UNKNOWN \n",
__func__, qlge->instance));
break;
}
QL_PRINT(DBG_FLASH, ("%s(%d) image size %x at %x\n",
__func__, qlge->instance, image_size, *nextpos));
if (pcid->indicator == PCI_IND_LAST_IMAGE) {
QL_PRINT(DBG_FLASH, ("%s(%d) last boot image found \n",
__func__, qlge->instance));
rval = LAST_IMAGE_FOUND;
} else {
rval = CONTINUE_SEARCH;
}
/* Get the next flash image address */
*nextpos += image_size;
return (rval);
}
/*
* ql_find_flash_layout_table_data_structure
* Find Flash Layout Table Data Structure (FLTDS) that
* is located at the end of last boot image.
* Assume FLTDS is located with first 2M bytes.
* Note:
* Driver must be in stalled state prior to entering or
* add code to this function prior to calling ql_setup_flash()
*/
int
ql_find_flash_layout_table_data_structure_addr(qlge_t *qlge)
{
int rval = DDI_FAILURE;
int result = CONTINUE_SEARCH;
uint32_t freadpos = 0;
uint8_t buf[FBUFSIZE];
if (qlge->flash_fltds_addr != 0) {
QL_PRINT(DBG_FLASH, ("%s(%d) done already\n",
__func__, qlge->instance));
return (DDI_SUCCESS);
}
/*
* Temporarily set the fdesc.flash_size to
* 1M flash size to avoid failing of ql_dump_focde.
*/
qlge->fdesc.flash_size = FLASH_FIRMWARE_IMAGE_ADDR;
while (result == CONTINUE_SEARCH) {
if ((rval = qlge_dump_fcode(qlge, buf, FBUFSIZE, freadpos))
!= DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d) qlge_dump_fcode failed"
" pos=%xh rval=%xh",
__func__, qlge->instance, freadpos, rval);
break;
}
/*
* checkout the pci boot image format
* and get next read address
*/
result = ql_check_pci(qlge, buf, &freadpos);
/*
* find last image? If so, then the freadpos
* is the address of FLTDS
*/
if (result == LAST_IMAGE_FOUND) {
QL_PRINT(DBG_FLASH,
("%s(%d) flash layout table data structure "
"(FLTDS) address is at %x \n", __func__,
qlge->instance, freadpos));
qlge->flash_fltds_addr = freadpos;
rval = DDI_SUCCESS;
break;
} else if (result == STOP_SEARCH) {
cmn_err(CE_WARN, "%s(%d) flash header incorrect,"
"stop searching",
__func__, qlge->instance);
break;
}
}
return (rval);
}
/*
* ql_flash_fltds
* Get flash layout table data structure table.
*/
static int
ql_flash_fltds(qlge_t *qlge)
{
uint32_t cnt;
uint16_t chksum, *bp, data;
int rval;
rval = qlge_dump_fcode(qlge, (uint8_t *)&qlge->fltds,
sizeof (ql_fltds_t), qlge->flash_fltds_addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d)read error",
__func__, qlge->instance);
bzero(&qlge->fltds, sizeof (ql_fltds_t));
return (rval);
}
QL_DUMP(DBG_FLASH, "flash layout table data structure:\n",
&qlge->fltds, 8, sizeof (ql_fltds_t));
chksum = 0;
data = 0;
bp = (uint16_t *)&qlge->fltds;
for (cnt = 0; cnt < (sizeof (ql_fltds_t)) / 2; cnt++) {
data = *bp;
LITTLE_ENDIAN_16(&data);
chksum += data;
bp++;
}
LITTLE_ENDIAN_32(&qlge->fltds.signature);
LITTLE_ENDIAN_16(&qlge->fltds.flt_addr_lo);
LITTLE_ENDIAN_16(&qlge->fltds.flt_addr_hi);
LITTLE_ENDIAN_16(&qlge->fltds.checksum);
QL_PRINT(DBG_FLASH, ("%s(%d) signature %xh\n",
__func__, qlge->instance, qlge->fltds.signature));
QL_PRINT(DBG_FLASH, ("%s(%d) flt_addr_lo %xh\n",
__func__, qlge->instance, qlge->fltds.flt_addr_lo));
QL_PRINT(DBG_FLASH, ("%s(%d) flt_addr_hi %xh\n",
__func__, qlge->instance, qlge->fltds.flt_addr_hi));
QL_PRINT(DBG_FLASH, ("%s(%d) version %xh\n",
__func__, qlge->instance, qlge->fltds.version));
QL_PRINT(DBG_FLASH, ("%s(%d) checksum %xh\n",
__func__, qlge->instance, qlge->fltds.checksum));
/* QFLT */
if (chksum != 0 || qlge->fltds.signature != FLASH_FLTDS_SIGNATURE) {
cmn_err(CE_WARN, "%s(%d) invalid flash layout table data"
" structure", __func__, qlge->instance);
bzero(&qlge->fltds, sizeof (ql_fltds_t));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* ql_flash_flt
* Get flash layout table.
*/
int
ql_flash_flt(qlge_t *qlge)
{
uint32_t addr, cnt;
int rval = DDI_FAILURE;
ql_flt_entry_t *entry;
uint8_t region;
addr = qlge->fltds.flt_addr_hi;
addr <<= 16;
addr |= qlge->fltds.flt_addr_lo;
/* first read flt header to know how long the table is */
rval = qlge_dump_fcode(qlge, (uint8_t *)&qlge->flt.header,
sizeof (ql_flt_header_t), addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d) read flt header at %x error",
__func__, qlge->instance, addr);
bzero(&qlge->flt, sizeof (ql_flt_header_t));
return (rval);
}
LITTLE_ENDIAN_16(&qlge->flt.header.version);
LITTLE_ENDIAN_16(&qlge->flt.header.length);
LITTLE_ENDIAN_16(&qlge->flt.header.checksum);
LITTLE_ENDIAN_16(&qlge->flt.header.reserved);
if ((qlge->flt.header.version != 1) &&
(qlge->flt.header.version != 0)) {
cmn_err(CE_WARN, "%s(%d) flt header version %x unsupported",
__func__, qlge->instance, qlge->flt.header.version);
bzero(&qlge->flt, sizeof (ql_flt_header_t));
return (DDI_FAILURE);
}
/* 2.allocate memory to save all flt table entries */
if ((qlge->flt.ql_flt_entry_ptr = (ql_flt_entry_t *)
(kmem_zalloc(qlge->flt.header.length, KM_SLEEP))) == NULL) {
cmn_err(CE_WARN, "%s(%d) flt table alloc failed",
__func__, qlge->instance);
goto err;
}
/* how many tables? */
qlge->flt.num_entries = (uint16_t)(qlge->flt.header.length /
sizeof (ql_flt_entry_t));
/* 3. read the rest of flt table */
addr += (uint32_t)sizeof (ql_flt_header_t);
QL_PRINT(DBG_FLASH, ("%s(%d) flt has %x entries \n",
__func__, qlge->instance, qlge->flt.num_entries));
rval = qlge_dump_fcode(qlge,
(uint8_t *)qlge->flt.ql_flt_entry_ptr, qlge->flt.header.length,
addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "read flt table entry error");
goto err;
}
entry = (ql_flt_entry_t *)qlge->flt.ql_flt_entry_ptr;
for (cnt = 0; cnt < qlge->flt.num_entries; cnt++) {
LITTLE_ENDIAN_32(&entry->size);
LITTLE_ENDIAN_32(&entry->begin_addr);
LITTLE_ENDIAN_32(&entry->end_addr);
entry++;
}
/* TO Do :4. Checksum verification */
/* 5.search index of Flash Descriptor Table in the Flash Layout Table */
entry = (ql_flt_entry_t *)qlge->flt.ql_flt_entry_ptr;
qlge->flash_fdt_addr = 0;
for (cnt = 0; cnt < qlge->flt.num_entries; cnt++) {
if (entry->region == FLT_REGION_FDT) {
qlge->flash_flt_fdt_index = cnt;
qlge->flash_fdt_addr = entry->begin_addr;
qlge->flash_fdt_size = entry->size;
QL_PRINT(DBG_FLASH, ("%s(%d) flash_flt_fdt_index is"
" %x, addr %x,size %x \n", __func__,
qlge->instance,
cnt, entry->begin_addr, entry->size));
break;
}
entry++;
}
if (qlge->flash_fdt_addr == 0) {
cmn_err(CE_WARN, "%s(%d) flash descriptor table not found",
__func__, qlge->instance);
goto err;
}
/* 6.search index of Nic Config. Table in the Flash Layout Table */
entry = (ql_flt_entry_t *)qlge->flt.ql_flt_entry_ptr;
if (qlge->func_number == qlge->fn0_net)
region = FLT_REGION_NIC_PARAM0;
else
region = FLT_REGION_NIC_PARAM1;
qlge->flash_nic_config_table_addr = 0;
for (cnt = 0; cnt < qlge->flt.num_entries; cnt++) {
if (entry->region == region) {
qlge->flash_flt_nic_config_table_index = cnt;
qlge->flash_nic_config_table_addr = entry->begin_addr;
qlge->flash_nic_config_table_size = entry->size;
QL_PRINT(DBG_FLASH, ("%s(%d) "
"flash_flt_nic_config_table_index "
"is %x, address %x, size %x \n",
__func__, qlge->instance,
cnt, entry->begin_addr, entry->size));
break;
}
entry++;
}
if (qlge->flash_nic_config_table_addr == 0) {
cmn_err(CE_WARN, "%s(%d) NIC Configuration Table not found",
__func__, qlge->instance);
goto err;
}
return (DDI_SUCCESS);
err:
bzero(&qlge->flt, sizeof (ql_flt_header_t));
if (qlge->flt.ql_flt_entry_ptr != NULL) {
bzero(&qlge->flt.ql_flt_entry_ptr, qlge->flt.header.length);
kmem_free(qlge->flt.ql_flt_entry_ptr, qlge->flt.header.length);
qlge->flt.ql_flt_entry_ptr = NULL;
}
cmn_err(CE_WARN, "%s(%d) read FLT failed", __func__, qlge->instance);
return (DDI_FAILURE);
}
/*
* ql_flash_desc
* Get flash descriptor table.
*/
static int
ql_flash_desc(qlge_t *qlge)
{
uint8_t w8;
uint32_t cnt, addr;
uint16_t chksum, *bp, data;
int rval;
addr = qlge->flash_fdt_addr;
rval = qlge_dump_fcode(qlge, (uint8_t *)&qlge->fdesc,
sizeof (flash_desc_t), addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d) read Flash Descriptor Table error",
__func__, qlge->instance);
bzero(&qlge->fdesc, sizeof (flash_desc_t));
return (rval);
}
chksum = 0;
data = 0;
bp = (uint16_t *)&qlge->fdesc;
for (cnt = 0; cnt < (sizeof (flash_desc_t)) / 2; cnt++) {
data = *bp;
LITTLE_ENDIAN_16(&data);
chksum += data;
bp++;
}
/* endian adjustment */
LITTLE_ENDIAN_32(&qlge->fdesc.flash_valid);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_version);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_len);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_checksum);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_unused);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_manuf);
LITTLE_ENDIAN_16(&qlge->fdesc.flash_id);
LITTLE_ENDIAN_32(&qlge->fdesc.block_size);
LITTLE_ENDIAN_32(&qlge->fdesc.alt_block_size);
LITTLE_ENDIAN_32(&qlge->fdesc.flash_size);
LITTLE_ENDIAN_32(&qlge->fdesc.write_enable_data);
LITTLE_ENDIAN_32(&qlge->fdesc.read_timeout);
/* flash size in desc table is in 1024 bytes */
QL_PRINT(DBG_FLASH, ("flash_valid=%xh\n", qlge->fdesc.flash_valid));
QL_PRINT(DBG_FLASH, ("flash_version=%xh\n", qlge->fdesc.flash_version));
QL_PRINT(DBG_FLASH, ("flash_len=%xh\n", qlge->fdesc.flash_len));
QL_PRINT(DBG_FLASH, ("flash_checksum=%xh\n",
qlge->fdesc.flash_checksum));
w8 = qlge->fdesc.flash_model[15];
qlge->fdesc.flash_model[15] = 0;
QL_PRINT(DBG_FLASH, ("flash_model=%s\n", qlge->fdesc.flash_model));
qlge->fdesc.flash_model[15] = w8;
QL_PRINT(DBG_FLASH, ("flash_size=%xK bytes\n", qlge->fdesc.flash_size));
qlge->fdesc.flash_size = qlge->fdesc.flash_size * 0x400;
qlge->flash_info.flash_size = qlge->fdesc.flash_size;
if (chksum != 0 || qlge->fdesc.flash_valid != FLASH_DESC_VAILD ||
qlge->fdesc.flash_version != FLASH_DESC_VERSION) {
cmn_err(CE_WARN, "invalid descriptor table");
bzero(&qlge->fdesc, sizeof (flash_desc_t));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* ql_flash_nic_config
* Get flash NIC Configuration table.
*/
static int
ql_flash_nic_config(qlge_t *qlge)
{
uint32_t cnt, addr;
uint16_t chksum, *bp, data;
int rval;
addr = qlge->flash_nic_config_table_addr;
rval = qlge_dump_fcode(qlge, (uint8_t *)&qlge->nic_config,
sizeof (ql_nic_config_t), addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "fail to read nic_cfg image %xh", rval);
bzero(&qlge->nic_config, sizeof (ql_nic_config_t));
return (rval);
}
chksum = 0;
data = 0;
bp = (uint16_t *)&qlge->nic_config;
for (cnt = 0; cnt < (sizeof (ql_nic_config_t)) / 2; cnt++) {
data = *bp;
LITTLE_ENDIAN_16(&data);
chksum += data;
bp++;
}
LITTLE_ENDIAN_32(&qlge->nic_config.signature);
LITTLE_ENDIAN_16(&qlge->nic_config.version);
LITTLE_ENDIAN_16(&qlge->nic_config.size);
LITTLE_ENDIAN_16(&qlge->nic_config.checksum);
LITTLE_ENDIAN_16(&qlge->nic_config.total_data_size);
LITTLE_ENDIAN_16(&qlge->nic_config.num_of_entries);
LITTLE_ENDIAN_16(&qlge->nic_config.vlan_id);
LITTLE_ENDIAN_16(&qlge->nic_config.last_entry);
LITTLE_ENDIAN_16(&qlge->nic_config.subsys_vendor_id);
LITTLE_ENDIAN_16(&qlge->nic_config.subsys_device_id);
QL_PRINT(DBG_FLASH, ("(%d): signature=%xh\n",
qlge->instance, qlge->nic_config.signature));
QL_PRINT(DBG_FLASH, ("(%d): size=%xh\n",
qlge->instance, qlge->nic_config.size));
QL_PRINT(DBG_FLASH, ("(%d): checksum=%xh\n",
qlge->instance, qlge->nic_config.checksum));
QL_PRINT(DBG_FLASH, ("(%d): version=%xh\n",
qlge->instance, qlge->nic_config.version));
QL_PRINT(DBG_FLASH, ("(%d): total_data_size=%xh\n",
qlge->instance, qlge->nic_config.total_data_size));
QL_PRINT(DBG_FLASH, ("(%d): num_of_entries=%xh\n",
qlge->instance, qlge->nic_config.num_of_entries));
QL_PRINT(DBG_FLASH, ("(%d): data_type=%xh\n",
qlge->instance, qlge->nic_config.factory_data_type));
QL_PRINT(DBG_FLASH, ("(%d): data_type_size=%xh\n",
qlge->instance, qlge->nic_config.factory_data_type_size));
QL_PRINT(DBG_FLASH,
("(%d): factory mac=%02x %02x %02x %02x %02x %02x h\n",
qlge->instance,
qlge->nic_config.factory_MAC[0],
qlge->nic_config.factory_MAC[1],
qlge->nic_config.factory_MAC[2],
qlge->nic_config.factory_MAC[3],
qlge->nic_config.factory_MAC[4],
qlge->nic_config.factory_MAC[5]));
QL_PRINT(DBG_FLASH, ("(%d): data_type=%xh\n",
qlge->instance, qlge->nic_config.clp_data_type));
QL_PRINT(DBG_FLASH, ("(%d): data_type_size=%xh\n",
qlge->instance, qlge->nic_config.clp_data_type_size));
QL_PRINT(DBG_FLASH, ("(%d): clp mac=%x %x %x %x %x %x h\n",
qlge->instance,
qlge->nic_config.clp_MAC[0],
qlge->nic_config.clp_MAC[1],
qlge->nic_config.clp_MAC[2],
qlge->nic_config.clp_MAC[3],
qlge->nic_config.clp_MAC[4],
qlge->nic_config.clp_MAC[5]));
QL_PRINT(DBG_FLASH, ("(%d): data_type=%xh\n",
qlge->instance, qlge->nic_config.clp_vlan_data_type));
QL_PRINT(DBG_FLASH, ("(%d): data_type_size=%xh\n",
qlge->instance, qlge->nic_config.clp_vlan_data_type_size));
QL_PRINT(DBG_FLASH, ("(%d): vlan_id=%xh\n",
qlge->instance, qlge->nic_config.vlan_id));
QL_PRINT(DBG_FLASH, ("(%d): data_type=%xh\n",
qlge->instance, qlge->nic_config.last_data_type));
QL_PRINT(DBG_FLASH, ("(%d): data_type_size=%xh\n",
qlge->instance, qlge->nic_config.last_data_type_size));
QL_PRINT(DBG_FLASH, ("(%d): last_entry=%xh\n",
qlge->instance, qlge->nic_config.last_entry));
QL_PRINT(DBG_FLASH, ("(%d): subsys_vendor_id=%xh\n",
qlge->instance, qlge->nic_config.subsys_vendor_id));
QL_PRINT(DBG_FLASH, ("(%d): subsys_device_id=%xh\n",
qlge->instance, qlge->nic_config.subsys_device_id));
if (chksum != 0 || qlge->nic_config.signature !=
FLASH_NIC_CONFIG_SIGNATURE || qlge->nic_config.version != 1) {
cmn_err(CE_WARN,
"invalid flash nic configuration table: chksum %x, "
"signature %x, version %x",
chksum, qlge->nic_config.signature,
qlge->nic_config.version);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
ql_flash_vpd(qlge_t *qlge, uint8_t *buf)
{
uint32_t cnt;
uint16_t chksum, *bp, data;
int rval;
uint32_t vpd_size;
if (buf == NULL) {
cmn_err(CE_WARN, "%s(%d) buffer is not available.",
__func__, qlge->instance);
return (DDI_FAILURE);
}
if (!qlge->flash_vpd_addr) {
if (qlge->func_number == qlge->fn0_net)
qlge->flash_vpd_addr = ISP_8100_VPD0_ADDR;
else
qlge->flash_vpd_addr = ISP_8100_VPD1_ADDR;
vpd_size = ISP_8100_VPD0_SIZE;
}
rval = qlge_dump_fcode(qlge, buf, vpd_size, qlge->flash_vpd_addr);
if (rval != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s(%d)read error",
__func__, qlge->instance);
bzero(buf, vpd_size);
return (rval);
}
QL_DUMP(DBG_FLASH, "flash vpd table raw data:\n", buf, 8, vpd_size);
chksum = 0;
data = 0;
bp = (uint16_t *)(void *)buf;
for (cnt = 0; cnt < (vpd_size/2); cnt++) {
data = *bp;
LITTLE_ENDIAN_16(&data);
chksum += data;
bp++;
}
if (chksum != 0) {
cmn_err(CE_WARN, "%s(%d) invalid flash vpd table",
__func__, qlge->instance);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
ql_get_flash_params(qlge_t *qlge)
{
int rval = DDI_SUCCESS;
/* Get semaphore to access Flash Address and Flash Data Registers */
if (ql_sem_spinlock(qlge, QL_FLASH_SEM_MASK)) {
rval = DDI_FAILURE;
goto out;
}
/* do test read of flash ID */
rval = ql_flash_id(qlge);
if (rval != DDI_SUCCESS)
goto out;
/*
* Temporarily set the fdesc.flash_size to
* 4M flash size to avoid failing of ql_dump_focde.
*/
qlge->fdesc.flash_size = 4096 * 1024; /* ie. 4M bytes */
/* Default flash descriptor table. */
qlge->fdesc.write_statusreg_cmd = 1;
qlge->fdesc.write_enable_bits = 0;
qlge->fdesc.unprotect_sector_cmd = 0;
qlge->fdesc.protect_sector_cmd = 0;
qlge->fdesc.write_disable_bits = 0x9c;
qlge->fdesc.block_size = 0x10000;
qlge->fdesc.erase_cmd = 0xd8;
/* ! todo : should read from fltds! */
/* !ql_get_flash_params(qlge); */
qlge->fltds.flt_addr_hi = 0x36;
qlge->fltds.flt_addr_lo = 0x1000;
/* read all other tables from Flash memory */
if (ql_flash_flt(qlge) != DDI_SUCCESS) {
if (CFG_IST(qlge, CFG_CHIP_8100)) {
qlge->flash_fdt_addr = ISP_8100_FDT_ADDR; /* 0x360000 */
if (qlge->func_number == qlge->fn0_net)
/* 0x140200 */
qlge->flash_nic_config_table_addr =
ISP_8100_NIC_PARAM0_ADDR;
else
/* 0x140600 */
qlge->flash_nic_config_table_addr =
ISP_8100_NIC_PARAM1_ADDR;
}
}
(void) ql_flash_desc(qlge);
(void) ql_flash_nic_config(qlge);
out:
ql_sem_unlock(qlge, QL_FLASH_SEM_MASK);
return (rval);
}
/*
* ql_setup_flash
* Gets the manufacturer and id number of the flash chip,
* and sets up the size parameter.
*/
int
ql_setup_flash(qlge_t *qlge)
{
int rval = DDI_SUCCESS;
if (qlge->flash_fltds_addr != 0) {
return (rval);
}
if (ql_sem_spinlock(qlge, QL_FLASH_SEM_MASK)) {
rval = DDI_FAILURE;
goto out;
}
/* try reading flash ID */
rval = ql_flash_id(qlge);
if (rval != DDI_SUCCESS)
goto out;
/* Default flash descriptor table. */
qlge->fdesc.write_statusreg_cmd = 1;
qlge->fdesc.write_enable_bits = 0;
qlge->fdesc.unprotect_sector_cmd = 0;
qlge->fdesc.protect_sector_cmd = 0;
qlge->fdesc.write_disable_bits = 0x9c;
qlge->fdesc.block_size = 0x10000;
qlge->fdesc.erase_cmd = 0xd8;
/* 1 Get the location of Flash Layout Table Data Structure (FLTDS) */
if (ql_find_flash_layout_table_data_structure_addr(qlge)
== DDI_SUCCESS) {
/* 2,read fltds */
if (ql_flash_fltds(qlge) == DDI_SUCCESS) {
/*
* 3,search for flash descriptor table (FDT)
* and Nic Configuration Table indices
*/
if ((qlge->flash_fdt_addr == 0) ||
(qlge->flash_nic_config_table_addr == 0)) {
rval = ql_flash_flt(qlge);
if (rval == DDI_SUCCESS) {
(void) ql_flash_desc(qlge);
(void) ql_flash_nic_config(qlge);
} else {
rval = DDI_FAILURE;
goto out;
}
}
} else {
rval = DDI_FAILURE;
goto out;
}
} else {
rval = DDI_FAILURE;
goto out;
}
out:
ql_sem_unlock(qlge, QL_FLASH_SEM_MASK);
return (rval);
}
/*
* ql_change_endian
* Change endianess of byte array.
*/
void
ql_change_endian(uint8_t buf[], size_t size)
{
uint8_t byte;
size_t cnt1;
size_t cnt;
cnt1 = size - 1;
for (cnt = 0; cnt < size / 2; cnt++) {
byte = buf[cnt1];
buf[cnt1] = buf[cnt];
buf[cnt] = byte;
cnt1--;
}
}
static int
ql_wait_flash_reg_ready(qlge_t *qlge, uint32_t wait_bit)
{
uint32_t reg_status;
int rtn_val = DDI_SUCCESS;
uint32_t delay = 300000;
do {
reg_status = ql_read_reg(qlge, REG_FLASH_ADDRESS);
if (reg_status & FLASH_ERR_FLAG) {
cmn_err(CE_WARN,
"%s(%d) flash address register error bit set!",
__func__, qlge->instance);
rtn_val = DDI_FAILURE;
break;
}
if (reg_status & wait_bit) {
break;
}
drv_usecwait(10);
} while (--delay);
if (delay == 0) {
cmn_err(CE_WARN,
"%s(%d) timeout error!", __func__, qlge->instance);
if (qlge->fm_enable) {
ql_fm_ereport(qlge, DDI_FM_DEVICE_NO_RESPONSE);
atomic_or_32(&qlge->flags, ADAPTER_ERROR);
ddi_fm_service_impact(qlge->dip, DDI_SERVICE_LOST);
}
rtn_val = DDI_FAILURE;
}
return (rtn_val);
}
/*
* ql_read_flash
* Reads a 32bit word from FLASH.
*/
static int
ql_read_flash(qlge_t *qlge, uint32_t faddr, uint32_t *bp)
{
int rval = DDI_SUCCESS;
ql_write_reg(qlge, REG_FLASH_ADDRESS, faddr | FLASH_R_FLAG);
/* Wait for READ cycle to complete. */
rval = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG);
if (rval == DDI_SUCCESS) {
*bp = ql_read_reg(qlge, REG_FLASH_DATA);
}
return (rval);
}
static int
ql_read_flash_status(qlge_t *qlge, uint8_t *value)
{
int rtn_val = DDI_SUCCESS;
uint32_t data, cmd = FLASH_CONF_ADDR | FLASH_R_FLAG;
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
!= DDI_SUCCESS) {
return (rtn_val);
}
cmd |= FLASH_RDSR_CMD /* 0x05 */;
ql_write_reg(qlge, REG_FLASH_ADDRESS, cmd);
if ((rtn_val = ql_wait_flash_reg_ready(qlge,
FLASH_RDY_FLAG | FLASH_R_FLAG)) != DDI_SUCCESS) {
return (rtn_val);
}
data = ql_read_reg(qlge, REG_FLASH_DATA);
*value = (uint8_t)(data & 0xff);
return (rtn_val);
}
static int
ql_flash_write_enable(qlge_t *qlge)
{
uint8_t reg_status;
int rtn_val = DDI_SUCCESS;
uint32_t cmd = FLASH_CONF_ADDR;
uint32_t delay = 300000;
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
!= DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s(%d) timeout!", __func__, qlge->instance);
rtn_val = DDI_FAILURE;
return (rtn_val);
}
cmd |= qlge->fdesc.write_enable_cmd;
ql_write_reg(qlge, REG_FLASH_ADDRESS, cmd);
/* wait for WEL bit set */
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
== DDI_SUCCESS) {
do {
(void) ql_read_flash_status(qlge, &reg_status);
if (reg_status & BIT_1)
break;
drv_usecwait(10);
} while (--delay);
}
if (delay == 0) {
cmn_err(CE_WARN,
"%s(%d) timeout error! flash status reg: %x",
__func__, qlge->instance, reg_status);
rtn_val = DDI_FAILURE;
}
return (rtn_val);
}
static int
ql_flash_erase_sector(qlge_t *qlge, uint32_t sectorAddr)
{
int rtn_val = DDI_SUCCESS;
uint32_t data, cmd = FLASH_CONF_ADDR;
uint32_t delay = 300000;
uint8_t flash_status;
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
!= DDI_SUCCESS) {
return (rtn_val);
}
cmd |= (0x0300 | qlge->fdesc.erase_cmd);
data = ((sectorAddr & 0xff) << 16) | (sectorAddr & 0xff00) |
((sectorAddr & 0xff0000) >> 16);
ql_write_reg(qlge, REG_FLASH_DATA, data);
ql_write_reg(qlge, REG_FLASH_ADDRESS, cmd);
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
== DDI_SUCCESS) {
/* wait Write In Progress (WIP) bit to reset */
do {
(void) ql_read_flash_status(qlge, &flash_status);
if ((flash_status & BIT_0 /* WIP */) == 0)
break;
drv_usecwait(10);
} while (--delay);
} else {
return (rtn_val);
}
if (delay == 0) {
cmn_err(CE_WARN,
"%s(%d) timeout error! flash status reg: %x",
__func__, qlge->instance, flash_status);
rtn_val = DDI_FAILURE;
}
return (rtn_val);
}
/*
* ql_write_flash
* Writes a 32bit word to FLASH.
*/
static int
ql_write_flash(qlge_t *qlge, uint32_t addr, uint32_t data)
{
int rval = DDI_SUCCESS;
uint32_t delay = 300000;
uint8_t flash_status;
ql_write_reg(qlge, REG_FLASH_DATA, data);
(void) ql_read_reg(qlge, REG_FLASH_DATA);
ql_write_reg(qlge, REG_FLASH_ADDRESS, addr);
if ((rval = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
== DDI_SUCCESS) {
if ((addr & FLASH_ADDR_MASK) == FLASH_CONF_ADDR) {
/* wait Write In Progress (WIP) bit to reset */
do {
(void) ql_read_flash_status(qlge,
&flash_status);
if ((flash_status & BIT_0 /* WIP */) == 0)
break;
drv_usecwait(10);
} while (--delay);
}
} else {
return (rval);
}
if (delay == 0) {
cmn_err(CE_WARN,
"%s(%d) timeout error! flash status reg: %x",
__func__, qlge->instance, flash_status);
rval = DDI_FAILURE;
}
return (rval);
}
/*
* ql_unprotect_flash
* Enable writes
*/
static int
ql_unprotect_flash(qlge_t *qlge)
{
int fdata, rtn_val;
if ((rtn_val = ql_flash_write_enable(qlge)) != DDI_SUCCESS) {
return (rtn_val);
}
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
!= DDI_SUCCESS) {
return (rtn_val);
}
/*
* Remove block write protection (SST and ST) and
* Sector/Block Protection Register Lock (SST, ST, ATMEL).
* Unprotect sectors.
*/
(void) ql_write_flash(qlge,
FLASH_CONF_ADDR | 0x100 | qlge->fdesc.write_statusreg_cmd,
qlge->fdesc.write_enable_bits);
if (qlge->fdesc.unprotect_sector_cmd != 0) {
for (fdata = 0; fdata < 0x10; fdata++) {
(void) ql_write_flash(qlge, FLASH_CONF_ADDR |
0x300 | qlge->fdesc.unprotect_sector_cmd, fdata);
}
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x300 |
qlge->fdesc.unprotect_sector_cmd, 0x00400f);
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x300 |
qlge->fdesc.unprotect_sector_cmd, 0x00600f);
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x300 |
qlge->fdesc.unprotect_sector_cmd, 0x00800f);
}
rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG);
return (rtn_val);
}
/*
* ql_protect_flash
* Disable writes
*/
static int
ql_protect_flash(qlge_t *qlge)
{
int fdata, rtn_val;
if ((rtn_val = ql_flash_write_enable(qlge)) != DDI_SUCCESS) {
return (rtn_val);
}
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
!= DDI_SUCCESS) {
return (rtn_val);
}
/*
* Protect sectors.
* Set block write protection (SST and ST) and
* Sector/Block Protection Register Lock (SST, ST, ATMEL).
*/
if (qlge->fdesc.protect_sector_cmd != 0) {
for (fdata = 0; fdata < 0x10; fdata++) {
(void) ql_write_flash(qlge, FLASH_CONF_ADDR |
0x330 | qlge->fdesc.protect_sector_cmd, fdata);
}
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x330 |
qlge->fdesc.protect_sector_cmd, 0x00400f);
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x330 |
qlge->fdesc.protect_sector_cmd, 0x00600f);
(void) ql_write_flash(qlge, FLASH_CONF_ADDR | 0x330 |
qlge->fdesc.protect_sector_cmd, 0x00800f);
(void) ql_write_flash(qlge,
FLASH_CONF_ADDR | 0x101, 0x80);
} else {
(void) ql_write_flash(qlge,
FLASH_CONF_ADDR | 0x100 | qlge->fdesc.write_statusreg_cmd,
qlge->fdesc.write_disable_bits /* 0x9c */);
}
rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG);
return (rtn_val);
}
/*
* ql_write_flash_test
* test write to a flash sector that is not being used
*/
void
ql_write_flash_test(qlge_t *qlge, uint32_t test_addr)
{
uint32_t old_data, data;
uint32_t addr = 0;
addr = (test_addr / 4);
(void) ql_read_flash(qlge, addr, &old_data);
QL_PRINT(DBG_FLASH, ("read addr %x old value %x\n", test_addr,
old_data));
/* enable writing to flash */
(void) ql_unprotect_flash(qlge);
/* erase the sector */
(void) ql_flash_erase_sector(qlge, test_addr);
(void) ql_read_flash(qlge, addr, &data);
QL_PRINT(DBG_FLASH, ("after sector erase, addr %x value %x\n",
test_addr, data));
/* write new value to it and read back to confirm */
data = 0x33445566;
(void) ql_write_flash(qlge, addr, data);
QL_PRINT(DBG_FLASH, ("new value written to addr %x value %x\n",
test_addr, data));
(void) ql_read_flash(qlge, addr, &data);
if (data != 0x33445566) {
cmn_err(CE_WARN, "flash write test failed, get data %x"
" after writing", data);
}
/* write old value to it and read back to restore */
(void) ql_flash_erase_sector(qlge, test_addr);
(void) ql_write_flash(qlge, addr, old_data);
(void) ql_read_flash(qlge, addr, &data);
QL_PRINT(DBG_FLASH, ("write back old value addr %x value %x\n",
test_addr, data));
/* test done, protect the flash to forbid any more flash writting */
(void) ql_protect_flash(qlge);
}
void
ql_write_flash_test2(qlge_t *qlge, uint32_t test_addr)
{
uint32_t data, old_data;
(void) qlge_dump_fcode(qlge, (uint8_t *)&old_data, sizeof (old_data),
test_addr);
QL_PRINT(DBG_FLASH, ("read addr %x old value %x\n",
test_addr, old_data));
data = 0x12345678;
QL_PRINT(DBG_FLASH, ("write new test value %x\n", data));
(void) qlge_load_flash(qlge, (uint8_t *)&data, sizeof (data),
test_addr);
(void) qlge_dump_fcode(qlge, (uint8_t *)&data, sizeof (data),
test_addr);
if (data != 0x12345678) {
cmn_err(CE_WARN,
"flash write test failed, get data %x after writing",
data);
}
/* write old value to it and read back to restore */
(void) qlge_load_flash(qlge, (uint8_t *)&old_data, sizeof (old_data),
test_addr);
(void) qlge_dump_fcode(qlge, (uint8_t *)&data, sizeof (data),
test_addr);
QL_PRINT(DBG_FLASH, ("write back old value addr %x value %x verified\n",
test_addr, data));
}
/*
* ql_sem_flash_lock
* Flash memory is a shared resource amoung various PCI Functions, so,
* anyone wants to access flash memory, it needs to lock it first.
*/
int
ql_sem_flash_lock(qlge_t *qlge)
{
int rval = DDI_SUCCESS;
/* Get semaphore to access Flash Address and Flash Data Registers */
if (ql_sem_spinlock(qlge, QL_FLASH_SEM_MASK)) {
rval = DDI_FAILURE;
}
return (rval);
}
void
ql_sem_flash_unlock(qlge_t *qlge)
{
ql_sem_unlock(qlge, QL_FLASH_SEM_MASK);
}