aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/qlxgbe/ql_misc.c
diff options
context:
space:
mode:
authorDavid C Somayajulu <davidcs@FreeBSD.org>2013-05-15 17:03:09 +0000
committerDavid C Somayajulu <davidcs@FreeBSD.org>2013-05-15 17:03:09 +0000
commitf10a77bb82dac2ecab9c2ccfa25a920eb77765ef (patch)
tree86503bbdad05dec2946b8f85430c49c6ac0e7028 /sys/dev/qlxgbe/ql_misc.c
parent2afea814ac977b45eca2d17e74f4e664df52d1fd (diff)
downloadsrc-f10a77bb82dac2ecab9c2ccfa25a920eb77765ef.tar.gz
src-f10a77bb82dac2ecab9c2ccfa25a920eb77765ef.zip
Add Qlogic 10Gigabit Ethernet & CNA Adapter Driver Version 3.10.10 for
QLogic 8300 Series Adapters Submitted by: David C Somayajulu (davidcs@freebsd.org) QLogic Corporation Approved by: George Neville-Neil (gnn@freebsd.org)
Notes
Notes: svn path=/head/; revision=250661
Diffstat (limited to 'sys/dev/qlxgbe/ql_misc.c')
-rw-r--r--sys/dev/qlxgbe/ql_misc.c1304
1 files changed, 1304 insertions, 0 deletions
diff --git a/sys/dev/qlxgbe/ql_misc.c b/sys/dev/qlxgbe/ql_misc.c
new file mode 100644
index 000000000000..6c7292ff1d75
--- /dev/null
+++ b/sys/dev/qlxgbe/ql_misc.c
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (c) 2013-2014 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 <sys/cdefs.h>
+__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 (count == 0 && 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 (count == 0 && 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 (count == 0 && 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 (count == 0 && 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 (count == 0 && 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);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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);
+}
+
+
+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;
+ }
+
+ qla_load_fw_from_flash(ha);
+ WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0);
+
+ 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);
+ }
+
+ qla_load_fw_from_flash(ha);
+ WRITE_REG32(ha, Q8_FW_IMAGE_VALID, 0);
+
+ 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);
+}
+