summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/PC/BIOS/lsilogic.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Devices/PC/BIOS/lsilogic.c537
1 files changed, 537 insertions, 0 deletions
diff --git a/src/VBox/Devices/PC/BIOS/lsilogic.c b/src/VBox/Devices/PC/BIOS/lsilogic.c
new file mode 100644
index 00000000..6194b48c
--- /dev/null
+++ b/src/VBox/Devices/PC/BIOS/lsilogic.c
@@ -0,0 +1,537 @@
+/* $Id: lsilogic.c $ */
+/** @file
+ * LsiLogic SCSI host adapter driver to boot from disks.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "biosint.h"
+#include "ebda.h"
+#include "inlines.h"
+#include "pciutil.h"
+#include "vds.h"
+#include "scsi.h"
+
+//#define DEBUG_LSILOGIC 1
+#if DEBUG_LSILOGIC
+# define DBG_LSILOGIC(...) BX_INFO(__VA_ARGS__)
+#else
+# define DBG_LSILOGIC(...)
+#endif
+
+#define RT_BIT(bit) (1L << (bit))
+
+/**
+ * A simple SG element for a 32bit address.
+ */
+typedef struct MptSGEntrySimple32
+{
+ /** Length of the buffer this entry describes. */
+ uint32_t u24Length: 24;
+ /** Flag whether this element is the end of the list. */
+ uint32_t fEndOfList: 1;
+ /** Flag whether the address is 32bit or 64bits wide. */
+ uint32_t f64BitAddress: 1;
+ /** Flag whether this buffer contains data to be transferred or is the destination. */
+ uint32_t fBufferContainsData: 1;
+ /** Flag whether this is a local address or a system address. */
+ uint32_t fLocalAddress: 1;
+ /** Element type. */
+ uint32_t u2ElementType: 2;
+ /** Flag whether this is the last element of the buffer. */
+ uint32_t fEndOfBuffer: 1;
+ /** Flag whether this is the last element of the current segment. */
+ uint32_t fLastElement: 1;
+ /** Lower 32bits of the address of the data buffer. */
+ uint32_t u32DataBufferAddressLow: 32;
+} MptSGEntrySimple32, *PMptSGEntrySimple32;
+
+/** Defined function codes found in the message header. */
+#define MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST (0x00)
+#define MPT_MESSAGE_HDR_FUNCTION_IOC_INIT (0x02)
+
+/**
+ * SCSI IO Request
+ */
+typedef struct MptSCSIIORequest
+{
+ /** Target ID */
+ uint8_t u8TargetID;
+ /** Bus number */
+ uint8_t u8Bus;
+ /** Chain offset */
+ uint8_t u8ChainOffset;
+ /** Function number. */
+ uint8_t u8Function;
+ /** CDB length. */
+ uint8_t u8CDBLength;
+ /** Sense buffer length. */
+ uint8_t u8SenseBufferLength;
+ /** Reserved */
+ uint8_t u8Reserved;
+ /** Message flags. */
+ uint8_t u8MessageFlags;
+ /** Message context ID. */
+ uint32_t u32MessageContext;
+ /** LUN */
+ uint8_t au8LUN[8];
+ /** Control values. */
+ uint32_t u32Control;
+ /** The CDB. */
+ uint8_t au8CDB[16];
+ /** Data length. */
+ uint32_t u32DataLength;
+ /** Sense buffer low 32bit address. */
+ uint32_t u32SenseBufferLowAddress;
+} MptSCSIIORequest, *PMptSCSIIORequest;
+
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE (0x0L)
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE (0x1L)
+#define MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ (0x2L)
+
+/**
+ * SCSI IO error reply.
+ */
+typedef struct MptSCSIIOErrorReply
+{
+ /** Target ID */
+ uint8_t u8TargetID;
+ /** Bus number */
+ uint8_t u8Bus;
+ /** Message length. */
+ uint8_t u8MessageLength;
+ /** Function number. */
+ uint8_t u8Function;
+ /** CDB length */
+ uint8_t u8CDBLength;
+ /** Sense buffer length */
+ uint8_t u8SenseBufferLength;
+ /** Reserved */
+ uint8_t u8Reserved;
+ /** Message flags */
+ uint8_t u8MessageFlags;
+ /** Message context ID */
+ uint32_t u32MessageContext;
+ /** SCSI status. */
+ uint8_t u8SCSIStatus;
+ /** SCSI state */
+ uint8_t u8SCSIState;
+ /** IO controller status */
+ uint16_t u16IOCStatus;
+ /** IO controller log information */
+ uint32_t u32IOCLogInfo;
+ /** Transfer count */
+ uint32_t u32TransferCount;
+ /** Sense count */
+ uint32_t u32SenseCount;
+ /** Response information */
+ uint32_t u32ResponseInfo;
+} MptSCSIIOErrorReply, *PMptSCSIIOErrorReply;
+
+/**
+ * IO controller init request.
+ */
+typedef struct MptIOCInitRequest
+{
+ /** Which system send this init request. */
+ uint8_t u8WhoInit;
+ /** Reserved */
+ uint8_t u8Reserved;
+ /** Chain offset in the SG list. */
+ uint8_t u8ChainOffset;
+ /** Function to execute. */
+ uint8_t u8Function;
+ /** Flags */
+ uint8_t u8Flags;
+ /** Maximum number of devices the driver can handle. */
+ uint8_t u8MaxDevices;
+ /** Maximum number of buses the driver can handle. */
+ uint8_t u8MaxBuses;
+ /** Message flags. */
+ uint8_t u8MessageFlags;
+ /** Message context ID. */
+ uint32_t u32MessageContext;
+ /** Reply frame size. */
+ uint16_t u16ReplyFrameSize;
+ /** Reserved */
+ uint16_t u16Reserved;
+ /** Upper 32bit part of the 64bit address the message frames are in.
+ * That means all frames must be in the same 4GB segment. */
+ uint32_t u32HostMfaHighAddr;
+ /** Upper 32bit of the sense buffer. */
+ uint32_t u32SenseBufferHighAddr;
+} MptIOCInitRequest, *PMptIOCInitRequest;
+
+#define LSILOGICWHOINIT_SYSTEM_BIOS 0x01
+
+
+/**
+ * IO controller init reply.
+ */
+typedef struct MptIOCInitReply
+{
+ /** Which subsystem send this init request. */
+ uint8_t u8WhoInit;
+ /** Reserved */
+ uint8_t u8Reserved;
+ /** Message length */
+ uint8_t u8MessageLength;
+ /** Function. */
+ uint8_t u8Function;
+ /** Flags */
+ uint8_t u8Flags;
+ /** Maximum number of devices the driver can handle. */
+ uint8_t u8MaxDevices;
+ /** Maximum number of busses the driver can handle. */
+ uint8_t u8MaxBuses;
+ /** Message flags. */
+ uint8_t u8MessageFlags;
+ /** Message context ID */
+ uint32_t u32MessageContext;
+ /** Reserved */
+ uint16_t u16Reserved;
+ /** IO controller status. */
+ uint16_t u16IOCStatus;
+ /** IO controller log information. */
+ uint32_t u32IOCLogInfo;
+} MptIOCInitReply, *PMptIOCInitReply;
+
+/**
+ * Doorbell register - Used to get the status of the controller and
+ * initialise it.
+ */
+#define LSILOGIC_REG_DOORBELL 0x00
+# define LSILOGIC_REG_DOORBELL_SET_STATE(enmState) (((enmState) & 0x0f) << 28)
+# define LSILOGIC_REG_DOORBELL_SET_USED(enmDoorbell) (((enmDoorbell != LSILOGICDOORBELLSTATE_NOT_IN_USE) ? 1 : 0) << 27)
+# define LSILOGIC_REG_DOORBELL_SET_WHOINIT(enmWhoInit) (((enmWhoInit) & 0x07) << 24)
+# define LSILOGIC_REG_DOORBELL_SET_FAULT_CODE(u16Code) (u16Code)
+# define LSILOGIC_REG_DOORBELL_GET_FUNCTION(x) (((x) & 0xff000000) >> 24)
+# define LSILOGIC_REG_DOORBELL_GET_SIZE(x) (((x) & 0x00ff0000) >> 16)
+
+/**
+ * Functions which can be passed through the system doorbell.
+ */
+#define LSILOGIC_DOORBELL_FUNCTION_IOC_MSG_UNIT_RESET 0x40L
+#define LSILOGIC_DOORBELL_FUNCTION_IO_UNIT_RESET 0x41L
+#define LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE 0x42L
+#define LSILOGIC_DOORBELL_FUNCTION_REPLY_FRAME_REMOVAL 0x43L
+
+/**
+ * Write sequence register for the diagnostic register.
+ */
+#define LSILOGIC_REG_WRITE_SEQUENCE 0x04
+
+/**
+ * Diagnostic register - used to reset the controller.
+ */
+#define LSILOGIC_REG_HOST_DIAGNOSTIC 0x08
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_MEM_ENABLE (RT_BIT(0))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_DISABLE_ARM (RT_BIT(1))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER (RT_BIT(2))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_DIAG_RW_ENABLE (RT_BIT(4))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_HISTORY (RT_BIT(5))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_FLASH_BAD_SIG (RT_BIT(6))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_DRWE (RT_BIT(7))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_PREVENT_IOC_BOOT (RT_BIT(9))
+# define LSILOGIC_REG_HOST_DIAGNOSTIC_CLEAR_FLASH_BAD_SIG (RT_BIT(10))
+
+#define LSILOGIC_REG_TEST_BASE_ADDRESS 0x0c
+#define LSILOGIC_REG_DIAG_RW_DATA 0x10
+#define LSILOGIC_REG_DIAG_RW_ADDRESS 0x14
+
+/**
+ * Interrupt status register.
+ */
+#define LSILOGIC_REG_HOST_INTR_STATUS 0x30
+# define LSILOGIC_REG_HOST_INTR_STATUS_W_MASK (RT_BIT(3))
+# define LSILOGIC_REG_HOST_INTR_STATUS_DOORBELL_STS (RT_BIT(31))
+# define LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR (RT_BIT(3))
+# define LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL (RT_BIT(0))
+
+/**
+ * Interrupt mask register.
+ */
+#define LSILOGIC_REG_HOST_INTR_MASK 0x34
+# define LSILOGIC_REG_HOST_INTR_MASK_W_MASK (RT_BIT(0) | RT_BIT(3) | RT_BIT(8) | RT_BIT(9))
+# define LSILOGIC_REG_HOST_INTR_MASK_IRQ_ROUTING (RT_BIT(8) | RT_BIT(9))
+# define LSILOGIC_REG_HOST_INTR_MASK_DOORBELL RT_BIT(0)
+# define LSILOGIC_REG_HOST_INTR_MASK_REPLY RT_BIT(3)
+
+/**
+ * Queue registers.
+ */
+#define LSILOGIC_REG_REQUEST_QUEUE 0x40
+#define LSILOGIC_REG_REPLY_QUEUE 0x44
+
+/**
+ * LsiLogic-SCSI controller data.
+ */
+typedef struct
+{
+ /** The SCSI I/O request structure. */
+ MptSCSIIORequest ScsiIoReq;
+ /** S/G elements being used, must come after the I/O request structure. */
+ MptSGEntrySimple32 Sge;
+ /** The reply frame used for address replies. */
+ uint8_t abReply[128];
+ /** I/O base of device. */
+ uint16_t u16IoBase;
+} lsilogic_t;
+
+/* The BusLogic specific data must fit into 1KB (statically allocated). */
+ct_assert(sizeof(lsilogic_t) <= 1024);
+
+#define VBOX_LSILOGIC_NO_DEVICE 0xffff
+
+/* Warning: Destroys high bits of EAX. */
+uint32_t inpd(uint16_t port);
+#pragma aux inpd = \
+ ".386" \
+ "in eax, dx" \
+ "mov dx, ax" \
+ "shr eax, 16" \
+ "xchg ax, dx" \
+ parm [dx] value [dx ax] modify nomemory;
+
+/* Warning: Destroys high bits of EAX. */
+void outpd(uint16_t port, uint32_t val);
+#pragma aux outpd = \
+ ".386" \
+ "xchg ax, cx" \
+ "shl eax, 16" \
+ "mov ax, cx" \
+ "out dx, eax" \
+ parm [dx] [cx ax] modify nomemory;
+
+/**
+ * Converts a segment:offset pair into a 32bit physical address.
+ */
+static uint32_t lsilogic_addr_to_phys(void __far *ptr)
+{
+ return ((uint32_t)FP_SEG(ptr) << 4) + FP_OFF(ptr);
+}
+
+static int lsilogic_cmd(lsilogic_t __far *lsilogic, const void __far *pvReq, uint16_t cbReq,
+ void __far *pvReply, uint16_t cbReply)
+{
+ uint16_t i;
+ const uint32_t __far *pu32Req = (const uint32_t __far *)pvReq;
+ uint16_t __far *pu16Reply = (uint16_t *)pvReply;
+ uint32_t cMsg = cbReq / sizeof(uint32_t);
+ uint16_t cReply = cbReply / sizeof(uint16_t);
+ uint32_t u32Fn = (LSILOGIC_DOORBELL_FUNCTION_HANDSHAKE << 24) | (cMsg << 16);
+
+ if ( cbReq % sizeof(uint32_t)
+ || cbReply % sizeof(uint16_t))
+ return 1;
+
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL, u32Fn);
+ for (i = 0; i < cMsg; i++)
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL, pu32Req[i]);
+
+ for (i = 0; i < cReply; i++)
+ {
+ /* Wait for the system doorbell interrupt status to be set. */
+ while (!(inpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS) & LSILOGIC_REG_HOST_INTR_STATUS_SYSTEM_DOORBELL));
+
+ pu16Reply[i] = (uint16_t)inpd(lsilogic->u16IoBase + LSILOGIC_REG_DOORBELL); /* The lower 16bits contain the reply data. */
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS, 1);
+ }
+
+ return 0;
+}
+
+static int lsilogic_scsi_cmd_exec(lsilogic_t __far *lsilogic)
+{
+ uint32_t u32Reply = 0;
+ uint32_t u32ReplyDummy = 0;
+
+ /* Send it off. */
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_REQUEST_QUEUE, lsilogic_addr_to_phys(&lsilogic->ScsiIoReq));
+
+ /* Wait for it to finish. */
+ while (!(inpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS) & LSILOGIC_REG_HOST_INTR_STATUS_REPLY_INTR));
+
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_INTR_STATUS, 1);
+
+ /* Read the reply queue. */
+ u32Reply = inpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE);
+ u32ReplyDummy = inpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE);
+ if (u32ReplyDummy != 0xffffffff)
+ return 5;
+ if (u32Reply & RT_BIT(31))
+ {
+ /*
+ * This is an address reply indicating a failed transaction, so just return an error without
+ * bothering to check the exact failure reason for now.
+ *
+ * Just provide the reply frame to the reply queue again.
+ */
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE, lsilogic_addr_to_phys(&lsilogic->abReply));
+ return 4;
+ }
+
+ if (u32Reply != 0xcafe) /* Getting a different context ID should never ever happen. */
+ return 3;
+
+ return 0;
+}
+
+int lsilogic_scsi_cmd_data_out(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
+ uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
+{
+ lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
+ int i;
+
+ _fmemset(&lsilogic->ScsiIoReq, 0, sizeof(lsilogic->ScsiIoReq));
+
+ lsilogic->ScsiIoReq.u8TargetID = idTgt;
+ lsilogic->ScsiIoReq.u8Bus = 0;
+ lsilogic->ScsiIoReq.u8ChainOffset = 0;
+ lsilogic->ScsiIoReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
+ lsilogic->ScsiIoReq.u8CDBLength = cbCDB;
+ lsilogic->ScsiIoReq.u8SenseBufferLength = 0;
+ lsilogic->ScsiIoReq.u32MessageContext = 0xcafe;
+ lsilogic->ScsiIoReq.u32Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE << 24;
+ lsilogic->ScsiIoReq.u32DataLength = length;
+ for (i = 0; i < cbCDB; i++)
+ lsilogic->ScsiIoReq.au8CDB[i] = aCDB[i];
+
+ lsilogic->Sge.u24Length = length;
+ lsilogic->Sge.fEndOfList = 1;
+ lsilogic->Sge.f64BitAddress = 0;
+ lsilogic->Sge.fBufferContainsData = 0;
+ lsilogic->Sge.fLocalAddress = 0;
+ lsilogic->Sge.u2ElementType = 0x01; /* Simple type */
+ lsilogic->Sge.fEndOfBuffer = 1;
+ lsilogic->Sge.fLastElement = 1;
+ lsilogic->Sge.u32DataBufferAddressLow = lsilogic_addr_to_phys(buffer);
+
+ return lsilogic_scsi_cmd_exec(lsilogic);
+}
+
+int lsilogic_scsi_cmd_data_in(void __far *pvHba, uint8_t idTgt, uint8_t __far *aCDB,
+ uint8_t cbCDB, uint8_t __far *buffer, uint32_t length)
+{
+ lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
+ int i;
+
+ _fmemset(&lsilogic->ScsiIoReq, 0, sizeof(lsilogic->ScsiIoReq));
+
+ lsilogic->ScsiIoReq.u8TargetID = idTgt;
+ lsilogic->ScsiIoReq.u8Bus = 0;
+ lsilogic->ScsiIoReq.u8ChainOffset = 0;
+ lsilogic->ScsiIoReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
+ lsilogic->ScsiIoReq.u8CDBLength = cbCDB;
+ lsilogic->ScsiIoReq.u8SenseBufferLength = 0;
+ lsilogic->ScsiIoReq.u32MessageContext = 0xcafe;
+ lsilogic->ScsiIoReq.u32Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ << 24;
+ lsilogic->ScsiIoReq.u32DataLength = length;
+ for (i = 0; i < cbCDB; i++)
+ lsilogic->ScsiIoReq.au8CDB[i] = aCDB[i];
+
+ lsilogic->Sge.u24Length = length;
+ lsilogic->Sge.fEndOfList = 1;
+ lsilogic->Sge.f64BitAddress = 0;
+ lsilogic->Sge.fBufferContainsData = 0;
+ lsilogic->Sge.fLocalAddress = 0;
+ lsilogic->Sge.u2ElementType = 0x01; /* Simple type */
+ lsilogic->Sge.fEndOfBuffer = 1;
+ lsilogic->Sge.fLastElement = 1;
+ lsilogic->Sge.u32DataBufferAddressLow = lsilogic_addr_to_phys(buffer);
+
+ return lsilogic_scsi_cmd_exec(lsilogic);
+}
+
+/**
+ * Initializes the LsiLogic SCSI HBA and detects attached devices.
+ */
+static int lsilogic_scsi_hba_init(lsilogic_t __far *lsilogic)
+{
+ int rc;
+ MptIOCInitRequest IocInitReq;
+ MptIOCInitReply IocInitReply;
+
+ /*
+ * The following initialization sequence is stripped down to the point to work with
+ * our emulated LsiLogic controller, it will most certainly fail on real hardware.
+ */
+
+ /* Hard reset, write the sequence to enable the diagnostic access. */
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x04);
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x02);
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x07);
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_WRITE_SEQUENCE, 0x0d);
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_HOST_DIAGNOSTIC, LSILOGIC_REG_HOST_DIAGNOSTIC_RESET_ADAPTER);
+
+ IocInitReq.u8WhoInit = LSILOGICWHOINIT_SYSTEM_BIOS;
+ IocInitReq.u8Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT;
+ IocInitReq.u32HostMfaHighAddr = 0;
+ IocInitReq.u32SenseBufferHighAddr = 0;
+ IocInitReq.u8MaxBuses = 1;
+ IocInitReq.u8MaxDevices = 4;
+ IocInitReq.u16ReplyFrameSize = sizeof(lsilogic->abReply);
+ rc = lsilogic_cmd(lsilogic, &IocInitReq, sizeof(IocInitReq), &IocInitReply, sizeof(IocInitReply));
+ if (!rc)
+ {
+ /* Provide a single reply frame for SCSI I/O errors. */
+ outpd(lsilogic->u16IoBase + LSILOGIC_REG_REPLY_QUEUE, lsilogic_addr_to_phys(&lsilogic->abReply));
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Init the LsiLogic SCSI driver and detect attached disks.
+ */
+int lsilogic_scsi_init(void __far *pvHba, uint8_t u8Bus, uint8_t u8DevFn)
+{
+ lsilogic_t __far *lsilogic = (lsilogic_t __far *)pvHba;
+ uint32_t u32Bar;
+
+ DBG_LSILOGIC("LsiLogic SCSI HBA at Bus %u DevFn 0x%x (raw 0x%x)\n", u8Bus, u8DevFn);
+
+ u32Bar = pci_read_config_dword(u8Bus, u8DevFn, 0x10);
+
+ DBG_LSILOGIC("BAR at 0x10 : 0x%x\n", u32Bar);
+
+ if ((u32Bar & 0x01) != 0)
+ {
+ uint16_t u16IoBase = (u32Bar & 0xfff0);
+
+ /* Enable PCI memory, I/O, bus mastering access in command register. */
+ pci_write_config_word(u8Bus, u8DevFn, 4, 0x7);
+
+ DBG_LSILOGIC("I/O base: 0x%x\n", u16IoBase);
+ lsilogic->u16IoBase = u16IoBase;
+ return lsilogic_scsi_hba_init(lsilogic);
+ }
+ else
+ DBG_LSILOGIC("BAR is MMIO\n");
+
+ return 1;
+}
+