#include <qlge.h>
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 *);
int
ql_flash_id(qlge_t *qlge)
{
int rval;
uint32_t fdata = 0;
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;
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);
}
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));
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);
}
if ((startpos & 0x3) != 0) {
cmn_err(CE_WARN, "%s(%d) incorrect buffer size alignment",
__func__, qlge->instance);
return (DDI_FAILURE);
}
addr = startpos / 4;
cnt = startpos;
size += startpos;
while (cnt < size) {
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));
if ((faddr & 0x3) != 0) {
cmn_err(CE_WARN, "%s(%d) incorrect buffer size alignment",
__func__, qlge->instance);
return (DDI_FAILURE);
}
rest_addr = (qlge->fdesc.block_size - 1) >> 2;
faddr = faddr >> 2;
cnt = 0;
size = (size + 3) >> 2;
while (cnt < size) {
if ((faddr & rest_addr) == 0) {
fdata = (faddr & ~rest_addr) << 2;
fdata = (fdata & 0xff00) |
(fdata << 16 & 0xff0000) |
(fdata >> 16 & 0xff);
rval = ql_write_flash(qlge,
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;
}
}
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++;
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;
}
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);
}
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));
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));
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);
}
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);
}
pcih = (pci_header_t *)pciinfo;
doff = pcih->dataoffset[1];
doff <<= 8;
doff |= pcih->dataoffset[0];
if (pcih->signature[0] != PCI_HEADER0 ||
pcih->signature[1] != PCI_HEADER1 || 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);
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 ;
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;
}
*nextpos += image_size;
return (rval);
}
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);
}
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;
}
result = ql_check_pci(qlge, buf, &freadpos);
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);
}
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));
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);
}
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;
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);
}
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;
}
qlge->flt.num_entries = (uint16_t)(qlge->flt.header.length /
sizeof (ql_flt_entry_t));
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++;
}
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;
}
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);
}
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++;
}
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);
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);
}
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;
if (ql_sem_spinlock(qlge, QL_FLASH_SEM_MASK)) {
rval = DDI_FAILURE;
goto out;
}
rval = ql_flash_id(qlge);
if (rval != DDI_SUCCESS)
goto out;
qlge->fdesc.flash_size = 4096 * 1024;
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;
qlge->fltds.flt_addr_hi = 0x36;
qlge->fltds.flt_addr_lo = 0x1000;
if (ql_flash_flt(qlge) != DDI_SUCCESS) {
if (CFG_IST(qlge, CFG_CHIP_8100)) {
qlge->flash_fdt_addr = ISP_8100_FDT_ADDR;
if (qlge->func_number == qlge->fn0_net)
qlge->flash_nic_config_table_addr =
ISP_8100_NIC_PARAM0_ADDR;
else
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);
}
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;
}
rval = ql_flash_id(qlge);
if (rval != DDI_SUCCESS)
goto out;
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;
if (ql_find_flash_layout_table_data_structure_addr(qlge)
== DDI_SUCCESS) {
if (ql_flash_fltds(qlge) == DDI_SUCCESS) {
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);
}
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);
}
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);
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 ;
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);
if ((rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG))
== DDI_SUCCESS) {
do {
(void) ql_read_flash_status(qlge, ®_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) {
do {
(void) ql_read_flash_status(qlge, &flash_status);
if ((flash_status & BIT_0 ) == 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);
}
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) {
do {
(void) ql_read_flash_status(qlge,
&flash_status);
if ((flash_status & BIT_0 ) == 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);
}
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);
}
(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);
}
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);
}
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 );
}
rtn_val = ql_wait_flash_reg_ready(qlge, FLASH_RDY_FLAG);
return (rtn_val);
}
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));
(void) ql_unprotect_flash(qlge);
(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));
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);
}
(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));
(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);
}
(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));
}
int
ql_sem_flash_lock(qlge_t *qlge)
{
int rval = DDI_SUCCESS;
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);
}