/* * Copyright (c) 2013-2016 Qlogic Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * File : ql_misc.c * Author : David C Somayajulu, Qlogic Corporation, Aliso Viejo, CA 92656. */ #include __FBSDID("$FreeBSD$"); #include "ql_os.h" #include "ql_hw.h" #include "ql_def.h" #include "ql_inline.h" #include "ql_glbl.h" #include "ql_dbg.h" #include "ql_tmplt.h" #define QL_FDT_OFFSET 0x3F0000 #define Q8_FLASH_SECTOR_SIZE 0x10000 static int qla_ld_fw_init(qla_host_t *ha); /* * structure encapsulating the value to read/write to offchip memory */ typedef struct _offchip_mem_val { uint32_t data_lo; uint32_t data_hi; uint32_t data_ulo; uint32_t data_uhi; } offchip_mem_val_t; /* * Name: ql_rdwr_indreg32 * Function: Read/Write an Indirect Register */ int ql_rdwr_indreg32(qla_host_t *ha, uint32_t addr, uint32_t *val, uint32_t rd) { uint32_t wnd_reg; uint32_t count = 100; wnd_reg = (Q8_CRB_WINDOW_PF0 | (ha->pci_func << 2)); WRITE_REG32(ha, wnd_reg, addr); while (count--) { if (READ_REG32(ha, wnd_reg) == addr) break; qla_mdelay(__func__, 1); } if (!count || QL_ERR_INJECT(ha, INJCT_RDWR_INDREG_FAILURE)) { device_printf(ha->pci_dev, "%s: [0x%08x, 0x%08x, %d] failed\n", __func__, addr, *val, rd); ha->qla_initiate_recovery = 1; return -1; } if (rd) { *val = READ_REG32(ha, Q8_WILD_CARD); } else { WRITE_REG32(ha, Q8_WILD_CARD, *val); } return 0; } /* * Name: ql_rdwr_offchip_mem * Function: Read/Write OffChip Memory */ int ql_rdwr_offchip_mem(qla_host_t *ha, uint64_t addr, q80_offchip_mem_val_t *val, uint32_t rd) { uint32_t count = 100; uint32_t data, step = 0; if (QL_ERR_INJECT(ha, INJCT_RDWR_OFFCHIPMEM_FAILURE)) goto exit_ql_rdwr_offchip_mem; data = (uint32_t)addr; if (ql_rdwr_indreg32(ha, Q8_MS_ADDR_LO, &data, 0)) { step = 1; goto exit_ql_rdwr_offchip_mem; } data = (uint32_t)(addr >> 32); if (ql_rdwr_indreg32(ha, Q8_MS_ADDR_HI, &data, 0)) { step = 2; goto exit_ql_rdwr_offchip_mem; } data = BIT_1; if (ql_rdwr_indreg32(ha, Q8_MS_CNTRL, &data, 0)) { step = 3; goto exit_ql_rdwr_offchip_mem; } if (!rd) { data = val->data_lo; if (ql_rdwr_indreg32(ha, Q8_MS_WR_DATA_0_31, &data, 0)) { step = 4; goto exit_ql_rdwr_offchip_mem; } data = val->data_hi; if (ql_rdwr_indreg32(ha, Q8_MS_WR_DATA_32_63, &data, 0)) { step = 5; goto exit_ql_rdwr_offchip_mem; } data = val->data_ulo; if (ql_rdwr_indreg32(ha, Q8_MS_WR_DATA_64_95, &data, 0)) { step = 6; goto exit_ql_rdwr_offchip_mem; } data = val->data_uhi; if (ql_rdwr_indreg32(ha, Q8_MS_WR_DATA_96_127, &data, 0)) { step = 7; goto exit_ql_rdwr_offchip_mem; } data = (BIT_2|BIT_1|BIT_0); if (ql_rdwr_indreg32(ha, Q8_MS_CNTRL, &data, 0)) { step = 7; goto exit_ql_rdwr_offchip_mem; } } else { data = (BIT_1|BIT_0); if (ql_rdwr_indreg32(ha, Q8_MS_CNTRL, &data, 0)) { step = 8; goto exit_ql_rdwr_offchip_mem; } } while (count--) { if (ql_rdwr_indreg32(ha, Q8_MS_CNTRL, &data, 1)) { step = 9; goto exit_ql_rdwr_offchip_mem; } if (!(data & BIT_3)) { if (rd) { if (ql_rdwr_indreg32(ha, Q8_MS_RD_DATA_0_31, &data, 1)) { step = 10; goto exit_ql_rdwr_offchip_mem; } val->data_lo = data; if (ql_rdwr_indreg32(ha, Q8_MS_RD_DATA_32_63, &data, 1)) { step = 11; goto exit_ql_rdwr_offchip_mem; } val->data_hi = data; if (ql_rdwr_indreg32(ha, Q8_MS_RD_DATA_64_95, &data, 1)) { step = 12; goto exit_ql_rdwr_offchip_mem; } val->data_ulo = data; if (ql_rdwr_indreg32(ha, Q8_MS_RD_DATA_96_127, &data, 1)) { step = 13; goto exit_ql_rdwr_offchip_mem; } val->data_uhi = data; } return 0; } else qla_mdelay(__func__, 1); } exit_ql_rdwr_offchip_mem: device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x : 0x%08x 0x%08x 0x%08x 0x%08x]" " [%d] [%d] failed\n", __func__, (uint32_t)(addr >> 32), (uint32_t)(addr), val->data_lo, val->data_hi, val->data_ulo, val->data_uhi, rd, step); ha->qla_initiate_recovery = 1; return (-1); } /* * Name: ql_rd_flash32 * Function: Read Flash Memory */ int ql_rd_flash32(qla_host_t *ha, uint32_t addr, uint32_t *data) { uint32_t data32; if (qla_sem_lock(ha, Q8_FLASH_LOCK, Q8_FLASH_LOCK_ID, 0xABCDABCD)) { device_printf(ha->pci_dev, "%s: Q8_FLASH_LOCK failed\n", __func__); return (-1); } data32 = addr; if (ql_rdwr_indreg32(ha, Q8_FLASH_DIRECT_WINDOW, &data32, 0)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Q8_FLASH_DIRECT_WINDOW[0x%08x] failed\n", __func__, data32); return (-1); } data32 = Q8_FLASH_DIRECT_DATA | (addr & 0xFFFF); if (ql_rdwr_indreg32(ha, data32, data, 1)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: data32:data [0x%08x] failed\n", __func__, data32); return (-1); } qla_sem_unlock(ha, Q8_FLASH_UNLOCK); return 0; } static int qla_get_fdt(qla_host_t *ha) { uint32_t data32; int count; qla_hw_t *hw; hw = &ha->hw; for (count = 0; count < sizeof(qla_flash_desc_table_t); count+=4) { if (ql_rd_flash32(ha, QL_FDT_OFFSET + count, (uint32_t *)&hw->fdt + (count >> 2))) { device_printf(ha->pci_dev, "%s: Read QL_FDT_OFFSET + %d failed\n", __func__, count); return (-1); } } if (qla_sem_lock(ha, Q8_FLASH_LOCK, Q8_FLASH_LOCK_ID, Q8_FDT_LOCK_MAGIC_ID)) { device_printf(ha->pci_dev, "%s: Q8_FLASH_LOCK failed\n", __func__); return (-1); } data32 = Q8_FDT_FLASH_ADDR_VAL; if (ql_rdwr_indreg32(ha, Q8_FLASH_ADDRESS, &data32, 0)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_ADDRESS failed\n", __func__); return (-1); } data32 = Q8_FDT_FLASH_CTRL_VAL; if (ql_rdwr_indreg32(ha, Q8_FLASH_CONTROL, &data32, 0)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_CONTROL failed\n", __func__); return (-1); } count = 0; do { if (count < 1000) { QLA_USEC_DELAY(10); count += 10; } else { qla_mdelay(__func__, 1); count += 1000; } data32 = 0; if (ql_rdwr_indreg32(ha, Q8_FLASH_STATUS, &data32, 1)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Read Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 &= 0x6; } while ((count < 10000) && (data32 != 0x6)); if (data32 != 0x6) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); return (-1); } if (ql_rdwr_indreg32(ha, Q8_FLASH_RD_DATA, &data32, 1)) { qla_sem_unlock(ha, Q8_FLASH_UNLOCK); device_printf(ha->pci_dev, "%s: Read Q8_FLASH_RD_DATA failed\n", __func__); return (-1); } qla_sem_unlock(ha, Q8_FLASH_UNLOCK); data32 &= Q8_FDT_MASK_VAL; if (hw->fdt.flash_manuf == data32) return (0); else return (-1); } static int qla_flash_write_enable(qla_host_t *ha, int enable) { uint32_t data32; int count = 0; data32 = Q8_WR_ENABLE_FL_ADDR | ha->hw.fdt.write_statusreg_cmd; if (ql_rdwr_indreg32(ha, Q8_FLASH_ADDRESS, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_ADDRESS failed\n", __func__); return (-1); } if (enable) data32 = ha->hw.fdt.write_enable_bits; else data32 = ha->hw.fdt.write_disable_bits; if (ql_rdwr_indreg32(ha, Q8_FLASH_WR_DATA, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_WR_DATA failed\n", __func__); return (-1); } data32 = Q8_WR_ENABLE_FL_CTRL; if (ql_rdwr_indreg32(ha, Q8_FLASH_CONTROL, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_CONTROL failed\n", __func__); return (-1); } do { if (count < 1000) { QLA_USEC_DELAY(10); count += 10; } else { qla_mdelay(__func__, 1); count += 1000; } data32 = 0; if (ql_rdwr_indreg32(ha, Q8_FLASH_STATUS, &data32, 1)) { device_printf(ha->pci_dev, "%s: Read Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 &= 0x6; } while ((count < 10000) && (data32 != 0x6)); if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); return (-1); } return 0; } static int qla_erase_flash_sector(qla_host_t *ha, uint32_t start) { uint32_t data32; int count = 0; do { qla_mdelay(__func__, 1); data32 = 0; if (ql_rdwr_indreg32(ha, Q8_FLASH_STATUS, &data32, 1)) { device_printf(ha->pci_dev, "%s: Read Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 &= 0x6; } while (((count++) < 1000) && (data32 != 0x6)); if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 = (start >> 16) & 0xFF; if (ql_rdwr_indreg32(ha, Q8_FLASH_WR_DATA, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_WR_DATA failed\n", __func__); return (-1); } data32 = Q8_ERASE_FL_ADDR_MASK | ha->hw.fdt.erase_cmd; if (ql_rdwr_indreg32(ha, Q8_FLASH_ADDRESS, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_ADDRESS failed\n", __func__); return (-1); } data32 = Q8_ERASE_FL_CTRL_MASK; if (ql_rdwr_indreg32(ha, Q8_FLASH_CONTROL, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_CONTROL failed\n", __func__); return (-1); } count = 0; do { qla_mdelay(__func__, 1); data32 = 0; if (ql_rdwr_indreg32(ha, Q8_FLASH_STATUS, &data32, 1)) { device_printf(ha->pci_dev, "%s: Read Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 &= 0x6; } while (((count++) < 1000) && (data32 != 0x6)); if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); return (-1); } return 0; } int ql_erase_flash(qla_host_t *ha, uint32_t off, uint32_t size) { int rval = 0; uint32_t start; if (off & (Q8_FLASH_SECTOR_SIZE -1)) return (-1); if (qla_sem_lock(ha, Q8_FLASH_LOCK, Q8_FLASH_LOCK_ID, Q8_ERASE_LOCK_MAGIC_ID)) { device_printf(ha->pci_dev, "%s: Q8_FLASH_LOCK failed\n", __func__); return (-1); } if (qla_flash_write_enable(ha, 1) != 0) { rval = -1; goto ql_erase_flash_exit; } for (start = off; start < (off + size); start = start + Q8_FLASH_SECTOR_SIZE) { if (qla_erase_flash_sector(ha, start)) { rval = -1; break; } } rval = qla_flash_write_enable(ha, 0); ql_erase_flash_exit: qla_sem_unlock(ha, Q8_FLASH_UNLOCK); return (rval); } static int qla_wr_flash32(qla_host_t *ha, uint32_t off, uint32_t *data) { uint32_t data32; int count = 0; data32 = Q8_WR_FL_ADDR_MASK | (off >> 2); if (ql_rdwr_indreg32(ha, Q8_FLASH_ADDRESS, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_ADDRESS failed\n", __func__); return (-1); } if (ql_rdwr_indreg32(ha, Q8_FLASH_WR_DATA, data, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_WR_DATA failed\n", __func__); return (-1); } data32 = Q8_WR_FL_CTRL_MASK; if (ql_rdwr_indreg32(ha, Q8_FLASH_CONTROL, &data32, 0)) { device_printf(ha->pci_dev, "%s: Write to Q8_FLASH_CONTROL failed\n", __func__); return (-1); } do { if (count < 1000) { QLA_USEC_DELAY(10); count += 10; } else { qla_mdelay(__func__, 1); count += 1000; } data32 = 0; if (ql_rdwr_indreg32(ha, Q8_FLASH_STATUS, &data32, 1)) { device_printf(ha->pci_dev, "%s: Read Q8_FLASH_STATUS failed\n", __func__); return (-1); } data32 &= 0x6; } while ((count < 10000) && (data32 != 0x6)); if (data32 != 0x6) { device_printf(ha->pci_dev, "%s: Poll Q8_FLASH_STATUS failed\n", __func__); return (-1); } return 0; } static int qla_flash_write_data(qla_host_t *ha, uint32_t off, uint32_t size, void *data) { int rval = 0; uint32_t start; uint32_t *data32 = data; if (qla_sem_lock(ha, Q8_FLASH_LOCK, Q8_FLASH_LOCK_ID, Q8_WR_FL_LOCK_MAGIC_ID)) { device_printf(ha->pci_dev, "%s: Q8_FLASH_LOCK failed\n", __func__); rval = -1; goto qla_flash_write_data_exit; } if ((qla_flash_write_enable(ha, 1) != 0)) { device_printf(ha->pci_dev, "%s: failed\n", __func__); rval = -1; goto qla_flash_write_data_unlock_exit; } for (start = off; start < (off + size); start = start + 4) { if (*data32 != 0xFFFFFFFF) { if (qla_wr_flash32(ha, start, data32)) { rval = -1; break; } } data32++; } rval = qla_flash_write_enable(ha, 0); qla_flash_write_data_unlock_exit: qla_sem_unlock(ha, Q8_FLASH_UNLOCK); qla_flash_write_data_exit: return (rval); } int ql_wr_flash_buffer(qla_host_t *ha, uint32_t off, uint32_t size, void *buf) { int rval = 0; void *data; if (size == 0) return 0; size = size << 2; if (buf == NULL) return -1; if ((data = malloc(size, M_QLA83XXBUF, M_NOWAIT)) == NULL) { device_printf(ha->pci_dev, "%s: malloc failed \n", __func__); rval = -1; goto ql_wr_flash_buffer_exit; } if ((rval = copyin(buf, data, size))) { device_printf(ha->pci_dev, "%s copyin failed\n", __func__); goto ql_wr_flash_buffer_free_exit; } rval = qla_flash_write_data(ha, off, size, data); ql_wr_flash_buffer_free_exit: free(data, M_QLA83XXBUF); ql_wr_flash_buffer_exit: return (rval); } #ifdef QL_LDFLASH_FW /* * Name: qla_load_fw_from_flash * Function: Reads the Bootloader from Flash and Loads into Offchip Memory */ static void qla_load_fw_from_flash(qla_host_t *ha) { uint32_t flash_off = 0x10000; uint64_t mem_off; uint32_t count, mem_size; q80_offchip_mem_val_t val; mem_off = (uint64_t)(READ_REG32(ha, Q8_BOOTLD_ADDR)); mem_size = READ_REG32(ha, Q8_BOOTLD_SIZE); device_printf(ha->pci_dev, "%s: [0x%08x][0x%08x]\n", __func__, (uint32_t)mem_off, mem_size); /* only bootloader needs to be loaded into memory */ for (count = 0; count < mem_size ; ) { ql_rd_flash32(ha, flash_off, &val.data_lo); count = count + 4; flash_off = flash_off + 4; ql_rd_flash32(ha, flash_off, &val.data_hi); count = count + 4; flash_off = flash_off + 4; ql_rd_flash32(ha, flash_off, &val.data_ulo); count = count + 4; flash_off = flash_off + 4; ql_rd_flash32(ha, flash_off, &val.data_uhi); count = count + 4; flash_off = flash_off + 4; ql_rdwr_offchip_mem(ha, mem_off, &val, 0); mem_off = mem_off + 16; } return; } #endif /* #ifdef QL_LDFLASH_FW */ /* * Name: qla_init_from_flash * Function: Performs Initialization which consists of the following sequence * - reset * - CRB Init * - Peg Init * - Read the Bootloader from Flash and Load into Offchip Memory * - Kick start the bootloader which loads the rest of the firmware * and performs the remaining steps in the initialization process. */ static int qla_init_from_flash(qla_host_t *ha) { uint32_t delay = 300; uint32_t data; qla_ld_fw_init(ha); do { data = READ_REG32(ha, Q8_CMDPEG_STATE); QL_DPRINT2(ha, (ha->pci_dev, "%s: func[%d] cmdpegstate 0x%08x\n", __func__, ha->pci_func, data)); if (data == 0xFF01) { QL_DPRINT2(ha, (ha->pci_dev, "%s: func[%d] init complete\n", __func__, ha->pci_func)); return(0); } qla_mdelay(__func__, 100); } while (delay--); return (-1); } /* * Name: ql_init_hw * Function: Initializes P3+ hardware. */ int ql_init_hw(qla_host_t *ha) { device_t dev; int ret = 0; uint32_t val, delay = 300; dev = ha->pci_dev; QL_DPRINT1(ha, (dev, "%s: enter\n", __func__)); if (ha->pci_func & 0x1) { while ((ha->pci_func & 0x1) && delay--) { val = READ_REG32(ha, Q8_CMDPEG_STATE); if (val == 0xFF01) { QL_DPRINT2(ha, (dev, "%s: func = %d init complete\n", __func__, ha->pci_func)); qla_mdelay(__func__, 100); goto qla_init_exit; } qla_mdelay(__func__, 100); } return (-1); } val = READ_REG32(ha, Q8_CMDPEG_STATE); if (!cold || (val != 0xFF01)) { ret = qla_init_from_flash(ha); qla_mdelay(__func__, 100); } qla_init_exit: ha->fw_ver_major = READ_REG32(ha, Q8_FW_VER_MAJOR); ha->fw_ver_minor = READ_REG32(ha, Q8_FW_VER_MINOR); ha->fw_ver_sub = READ_REG32(ha, Q8_FW_VER_SUB); if (qla_get_fdt(ha) != 0) { device_printf(dev, "%s: qla_get_fdt failed\n", __func__); } else { ha->hw.flags.fdt_valid = 1; } return (ret); } void ql_read_mac_addr(qla_host_t *ha) { uint8_t *macp; uint32_t mac_lo; uint32_t mac_hi; uint32_t flash_off; flash_off = Q8_BOARD_CONFIG_OFFSET + Q8_BOARD_CONFIG_MAC0_LO + (ha->pci_func << 3); ql_rd_flash32(ha, flash_off, &mac_lo); flash_off += 4; ql_rd_flash32(ha, flash_off, &mac_hi); macp = (uint8_t *)&mac_lo; ha->hw.mac_addr[5] = macp[0]; ha->hw.mac_addr[4] = macp[1]; ha->hw.mac_addr[3] = macp[2]; ha->hw.mac_addr[2] = macp[3]; macp = (uint8_t *)&mac_hi; ha->hw.mac_addr[1] = macp[0]; ha->hw.mac_addr[0] = macp[1]; //device_printf(ha->pci_dev, "%s: %02x:%02x:%02x:%02x:%02x:%02x\n", // __func__, ha->hw.mac_addr[0], ha->hw.mac_addr[1], // ha->hw.mac_addr[2], ha->hw.mac_addr[3], // ha->hw.mac_addr[4], ha->hw.mac_addr[5]); return; } /* * Stop/Start/Initialization Handling */ static uint16_t qla_tmplt_16bit_checksum(qla_host_t *ha, uint16_t *buf, uint32_t size) { uint32_t sum = 0; uint32_t count = size >> 1; /* size in 16 bit words */ while (count-- > 0) sum += *buf++; while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); return (~sum); } static int qla_wr_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { q8_wrl_e_t *wr_l; int i; wr_l = (q8_wrl_e_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, wr_l++) { if (ql_rdwr_indreg32(ha, wr_l->addr, &wr_l->value, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x] error\n", __func__, wr_l->addr, wr_l->value); return -1; } if (ce_hdr->delay_to) { DELAY(ce_hdr->delay_to); } } return 0; } static int qla_rd_wr_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { q8_rdwrl_e_t *rd_wr_l; uint32_t data; int i; rd_wr_l = (q8_rdwrl_e_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, rd_wr_l++) { if (ql_rdwr_indreg32(ha, rd_wr_l->rd_addr, &data, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, rd_wr_l->rd_addr); return -1; } if (ql_rdwr_indreg32(ha, rd_wr_l->wr_addr, &data, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x] error\n", __func__, rd_wr_l->wr_addr, data); return -1; } if (ce_hdr->delay_to) { DELAY(ce_hdr->delay_to); } } return 0; } static int qla_poll_reg(qla_host_t *ha, uint32_t addr, uint32_t ms_to, uint32_t tmask, uint32_t tvalue) { uint32_t data; while (ms_to) { if (ql_rdwr_indreg32(ha, addr, &data, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, addr); return -1; } if ((data & tmask) != tvalue) { ms_to--; } else break; qla_mdelay(__func__, 1); } return ((ms_to ? 0: -1)); } static int qla_poll_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { int i; q8_poll_hdr_t *phdr; q8_poll_e_t *pe; uint32_t data; phdr = (q8_poll_hdr_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); pe = (q8_poll_e_t *)((uint8_t *)phdr + sizeof(q8_poll_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, pe++) { if (ql_rdwr_indreg32(ha, pe->addr, &data, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, pe->addr); return -1; } if (ce_hdr->delay_to) { if ((data & phdr->tmask) == phdr->tvalue) break; if (qla_poll_reg(ha, pe->addr, ce_hdr->delay_to, phdr->tmask, phdr->tvalue)) { if (ql_rdwr_indreg32(ha, pe->to_addr, &data, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, pe->to_addr); return -1; } if (ql_rdwr_indreg32(ha, pe->addr, &data, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, pe->addr); return -1; } } } } return 0; } static int qla_poll_write_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { int i; q8_poll_hdr_t *phdr; q8_poll_wr_e_t *wr_e; phdr = (q8_poll_hdr_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); wr_e = (q8_poll_wr_e_t *)((uint8_t *)phdr + sizeof(q8_poll_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, wr_e++) { if (ql_rdwr_indreg32(ha, wr_e->dr_addr, &wr_e->dr_value, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x] error\n", __func__, wr_e->dr_addr, wr_e->dr_value); return -1; } if (ql_rdwr_indreg32(ha, wr_e->ar_addr, &wr_e->ar_value, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x] error\n", __func__, wr_e->ar_addr, wr_e->ar_value); return -1; } if (ce_hdr->delay_to) { if (qla_poll_reg(ha, wr_e->ar_addr, ce_hdr->delay_to, phdr->tmask, phdr->tvalue)) device_printf(ha->pci_dev, "%s: " "[ar_addr, ar_value, delay, tmask," "tvalue] [0x%08x 0x%08x 0x%08x 0x%08x" " 0x%08x]\n", __func__, wr_e->ar_addr, wr_e->ar_value, ce_hdr->delay_to, phdr->tmask, phdr->tvalue); } } return 0; } static int qla_poll_read_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { int i; q8_poll_hdr_t *phdr; q8_poll_rd_e_t *rd_e; uint32_t value; phdr = (q8_poll_hdr_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); rd_e = (q8_poll_rd_e_t *)((uint8_t *)phdr + sizeof(q8_poll_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, rd_e++) { if (ql_rdwr_indreg32(ha, rd_e->ar_addr, &rd_e->ar_value, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x 0x%08x] error\n", __func__, rd_e->ar_addr, rd_e->ar_value); return -1; } if (ce_hdr->delay_to) { if (qla_poll_reg(ha, rd_e->ar_addr, ce_hdr->delay_to, phdr->tmask, phdr->tvalue)) { return (-1); } else { if (ql_rdwr_indreg32(ha, rd_e->dr_addr, &value, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, rd_e->ar_addr); return -1; } ha->hw.rst_seq[ha->hw.rst_seq_idx++] = value; if (ha->hw.rst_seq_idx == Q8_MAX_RESET_SEQ_IDX) ha->hw.rst_seq_idx = 1; } } } return 0; } static int qla_rdmwr(qla_host_t *ha, uint32_t raddr, uint32_t waddr, q8_rdmwr_hdr_t *hdr) { uint32_t value; if (hdr->index_a >= Q8_MAX_RESET_SEQ_IDX) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, hdr->index_a); return -1; } if (hdr->index_a) { value = ha->hw.rst_seq[hdr->index_a]; } else { if (ql_rdwr_indreg32(ha, raddr, &value, 1)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, raddr); return -1; } } value &= hdr->and_value; value <<= hdr->shl; value >>= hdr->shr; value |= hdr->or_value; value ^= hdr->xor_value; if (ql_rdwr_indreg32(ha, waddr, &value, 0)) { device_printf(ha->pci_dev, "%s: [0x%08x] error\n", __func__, raddr); return -1; } return 0; } static int qla_read_modify_write_list(qla_host_t *ha, q8_ce_hdr_t *ce_hdr) { int i; q8_rdmwr_hdr_t *rdmwr_hdr; q8_rdmwr_e_t *rdmwr_e; rdmwr_hdr = (q8_rdmwr_hdr_t *)((uint8_t *)ce_hdr + sizeof (q8_ce_hdr_t)); rdmwr_e = (q8_rdmwr_e_t *)((uint8_t *)rdmwr_hdr + sizeof(q8_rdmwr_hdr_t)); for (i = 0; i < ce_hdr->opcount; i++, rdmwr_e++) { if (qla_rdmwr(ha, rdmwr_e->rd_addr, rdmwr_e->wr_addr, rdmwr_hdr)) { return -1; } if (ce_hdr->delay_to) { DELAY(ce_hdr->delay_to); } } return 0; } static int qla_tmplt_execute(qla_host_t *ha, uint8_t *buf, int start_idx, int *end_idx, uint32_t nentries) { int i, ret = 0, proc_end = 0; q8_ce_hdr_t *ce_hdr; for (i = start_idx; ((i < nentries) && (!proc_end)); i++) { ce_hdr = (q8_ce_hdr_t *)buf; ret = 0; switch (ce_hdr->opcode) { case Q8_CE_OPCODE_NOP: break; case Q8_CE_OPCODE_WRITE_LIST: ret = qla_wr_list(ha, ce_hdr); //printf("qla_wr_list %d\n", ret); break; case Q8_CE_OPCODE_READ_WRITE_LIST: ret = qla_rd_wr_list(ha, ce_hdr); //printf("qla_rd_wr_list %d\n", ret); break; case Q8_CE_OPCODE_POLL_LIST: ret = qla_poll_list(ha, ce_hdr); //printf("qla_poll_list %d\n", ret); break; case Q8_CE_OPCODE_POLL_WRITE_LIST: ret = qla_poll_write_list(ha, ce_hdr); //printf("qla_poll_write_list %d\n", ret); break; case Q8_CE_OPCODE_POLL_RD_LIST: ret = qla_poll_read_list(ha, ce_hdr); //printf("qla_poll_read_list %d\n", ret); break; case Q8_CE_OPCODE_READ_MODIFY_WRITE: ret = qla_read_modify_write_list(ha, ce_hdr); //printf("qla_read_modify_write_list %d\n", ret); break; case Q8_CE_OPCODE_SEQ_PAUSE: if (ce_hdr->delay_to) { qla_mdelay(__func__, ce_hdr->delay_to); } break; case Q8_CE_OPCODE_SEQ_END: proc_end = 1; break; case Q8_CE_OPCODE_TMPLT_END: *end_idx = i; return 0; } if (ret) break; buf += ce_hdr->size; } *end_idx = i; return (ret); } #ifndef QL_LDFLASH_FW static int qla_load_offchip_mem(qla_host_t *ha, uint64_t addr, uint32_t *data32, uint32_t len32) { q80_offchip_mem_val_t val; int ret = 0; while (len32) { if (len32 > 4) { val.data_lo = *data32++; val.data_hi = *data32++; val.data_ulo = *data32++; val.data_uhi = *data32++; len32 -= 4; if (ql_rdwr_offchip_mem(ha, addr, &val, 0)) return -1; addr += (uint64_t)16; } else { break; } } bzero(&val, sizeof(q80_offchip_mem_val_t)); switch (len32) { case 3: val.data_lo = *data32++; val.data_hi = *data32++; val.data_ulo = *data32++; ret = ql_rdwr_offchip_mem(ha, addr, &val, 0); break; case 2: val.data_lo = *data32++; val.data_hi = *data32++; ret = ql_rdwr_offchip_mem(ha, addr, &val, 0); break; case 1: val.data_lo = *data32++; ret = ql_rdwr_offchip_mem(ha, addr, &val, 0); break; default: break; } return ret; } static int qla_load_bootldr(qla_host_t *ha) { uint64_t addr; uint32_t *data32; uint32_t len32; int ret; addr = (uint64_t)(READ_REG32(ha, Q8_BOOTLD_ADDR)); data32 = (uint32_t *)ql83xx_bootloader; len32 = ql83xx_bootloader_len >> 2; ret = qla_load_offchip_mem(ha, addr, data32, len32); return (ret); } static int qla_load_fwimage(qla_host_t *ha) { uint64_t addr; uint32_t *data32; uint32_t len32; int ret; addr = (uint64_t)(READ_REG32(ha, Q8_FW_IMAGE_ADDR)); data32 = (uint32_t *)ql83xx_firmware; len32 = ql83xx_firmware_len >> 2; ret = qla_load_offchip_mem(ha, addr, data32, len32); return (ret); } #endif /* #ifndef QL_LDFLASH_FW */ static int qla_ld_fw_init(qla_host_t *ha) { uint8_t *buf; uint32_t index = 0, end_idx; q8_tmplt_hdr_t *hdr; bzero(ha->hw.rst_seq, sizeof (ha->hw.rst_seq)); hdr = (q8_tmplt_hdr_t *)ql83xx_resetseq; if (qla_tmplt_16bit_checksum(ha, (uint16_t *)ql83xx_resetseq, (uint32_t)hdr->size)) { device_printf(ha->pci_dev, "%s: reset seq checksum failed\n", __func__); return -1; } buf = ql83xx_resetseq + hdr->stop_seq_off; // device_printf(ha->pci_dev, "%s: stop sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: stop seq failed\n", __func__); return -1; } index = end_idx; buf = ql83xx_resetseq + hdr->init_seq_off; // device_printf(ha->pci_dev, "%s: init sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: init seq failed\n", __func__); return -1; } #ifdef QL_LDFLASH_FW qla_load_fw_from_flash(ha); WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0); #else if (qla_load_bootldr(ha)) return -1; if (qla_load_fwimage(ha)) return -1; WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0x12345678); #endif /* #ifdef QL_LDFLASH_FW */ index = end_idx; buf = ql83xx_resetseq + hdr->start_seq_off; // device_printf(ha->pci_dev, "%s: start sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: init seq failed\n", __func__); return -1; } return 0; } int ql_stop_sequence(qla_host_t *ha) { uint8_t *buf; uint32_t index = 0, end_idx; q8_tmplt_hdr_t *hdr; bzero(ha->hw.rst_seq, sizeof (ha->hw.rst_seq)); hdr = (q8_tmplt_hdr_t *)ql83xx_resetseq; if (qla_tmplt_16bit_checksum(ha, (uint16_t *)ql83xx_resetseq, (uint32_t)hdr->size)) { device_printf(ha->pci_dev, "%s: reset seq checksum failed\n", __func__); return (-1); } buf = ql83xx_resetseq + hdr->stop_seq_off; device_printf(ha->pci_dev, "%s: stop sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: stop seq failed\n", __func__); return (-1); } return end_idx; } int ql_start_sequence(qla_host_t *ha, uint16_t index) { uint8_t *buf; uint32_t end_idx; q8_tmplt_hdr_t *hdr; bzero(ha->hw.rst_seq, sizeof (ha->hw.rst_seq)); hdr = (q8_tmplt_hdr_t *)ql83xx_resetseq; if (qla_tmplt_16bit_checksum(ha, (uint16_t *)ql83xx_resetseq, (uint32_t)hdr->size)) { device_printf(ha->pci_dev, "%s: reset seq checksum failed\n", __func__); return (-1); } buf = ql83xx_resetseq + hdr->init_seq_off; device_printf(ha->pci_dev, "%s: init sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: init seq failed\n", __func__); return (-1); } #ifdef QL_LDFLASH_FW qla_load_fw_from_flash(ha); WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0); #else if (qla_load_bootldr(ha)) return -1; if (qla_load_fwimage(ha)) return -1; WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0x12345678); #endif /* #ifdef QL_LDFLASH_FW */ index = end_idx; buf = ql83xx_resetseq + hdr->start_seq_off; device_printf(ha->pci_dev, "%s: start sequence\n", __func__); if (qla_tmplt_execute(ha, buf, index , &end_idx, hdr->nentries)) { device_printf(ha->pci_dev, "%s: init seq failed\n", __func__); return -1; } return (0); }