diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2004-03-06 03:03:08 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2004-03-06 03:03:08 +0000 |
commit | 090676220cee4b607f70cde0e711df0b9ba5ed2a (patch) | |
tree | f4624cc2281a7af52363edfc384623c26f08c21f /sys/dev/ic/mpt.c | |
parent | c9c5a960b51917f9efd8121b39d7cc34f28e9e27 (diff) |
Initial version of driver for LSI MPT devices, like the U320 1030.
Basically works but much still to fix/implement.
From NetBSD via Milos Urbanek and Marco Peereboom.
Diffstat (limited to 'sys/dev/ic/mpt.c')
-rw-r--r-- | sys/dev/ic/mpt.c | 1217 |
1 files changed, 1217 insertions, 0 deletions
diff --git a/sys/dev/ic/mpt.c b/sys/dev/ic/mpt.c new file mode 100644 index 00000000000..f21241b5052 --- /dev/null +++ b/sys/dev/ic/mpt.c @@ -0,0 +1,1217 @@ +/* $OpenBSD: mpt.c,v 1.1 2004/03/06 03:03:07 krw Exp $ */ +/* $NetBSD: mpt.c,v 1.4 2003/11/02 11:07:45 wiz Exp $ */ + +/* + * Copyright (c) 2000, 2001 by Greg Ansley + * + * 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 immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ +/* + * Additional Copyright (c) 2002 by Matthew Jacob under same license. + */ + +/* + * mpt.c: + * + * Generic routines for LSI Fusion adapters. + * + * Adapted from the FreeBSD "mpt" driver by Jason R. Thorpe for + * Wasabi Systems, Inc. + */ + +#include <sys/cdefs.h> +/* __KERNEL_RCSID(0, "$NetBSD: mpt.c,v 1.4 2003/11/02 11:07:45 wiz Exp $"); */ + +#include <dev/ic/mpt.h> + +#define MPT_MAX_TRYS 3 +#define MPT_MAX_WAIT 300000 + +static int maxwait_ack = 0; +static int maxwait_int = 0; +static int maxwait_state = 0; + +static __inline u_int32_t +mpt_rd_db(mpt_softc_t *mpt) +{ + return mpt_read(mpt, MPT_OFFSET_DOORBELL); +} + +static __inline u_int32_t +mpt_rd_intr(mpt_softc_t *mpt) +{ + return mpt_read(mpt, MPT_OFFSET_INTR_STATUS); +} + +/* Busy wait for a door bell to be read by IOC */ +static int +mpt_wait_db_ack(mpt_softc_t *mpt) +{ + int i; + for (i=0; i < MPT_MAX_WAIT; i++) { + if (!MPT_DB_IS_BUSY(mpt_rd_intr(mpt))) { + maxwait_ack = i > maxwait_ack ? i : maxwait_ack; + return MPT_OK; + } + + DELAY(100); + } + return MPT_FAIL; +} + +/* Busy wait for a door bell interrupt */ +static int +mpt_wait_db_int(mpt_softc_t *mpt) +{ + int i; + for (i=0; i < MPT_MAX_WAIT; i++) { + if (MPT_DB_INTR(mpt_rd_intr(mpt))) { + maxwait_int = i > maxwait_int ? i : maxwait_int; + return MPT_OK; + } + DELAY(100); + } + return MPT_FAIL; +} + +/* Wait for IOC to transition to a give state */ +void +mpt_check_doorbell(mpt_softc_t *mpt) +{ + u_int32_t db = mpt_rd_db(mpt); + if (MPT_STATE(db) != MPT_DB_STATE_RUNNING) { + mpt_prt(mpt, "Device not running"); + mpt_print_db(db); + } +} + +/* Wait for IOC to transition to a give state */ +static int +mpt_wait_state(mpt_softc_t *mpt, enum DB_STATE_BITS state) +{ + int i; + + for (i = 0; i < MPT_MAX_WAIT; i++) { + u_int32_t db = mpt_rd_db(mpt); + if (MPT_STATE(db) == state) { + maxwait_state = i > maxwait_state ? i : maxwait_state; + return (MPT_OK); + } + DELAY(100); + } + return (MPT_FAIL); +} + + +/* Issue the reset COMMAND to the IOC */ +int +mpt_soft_reset(mpt_softc_t *mpt) +{ + if (mpt->verbose) { + mpt_prt(mpt, "soft reset"); + } + + /* Have to use hard reset if we are not in Running state */ + if (MPT_STATE(mpt_rd_db(mpt)) != MPT_DB_STATE_RUNNING) { + mpt_prt(mpt, "soft reset failed: device not running"); + return MPT_FAIL; + } + + /* If door bell is in use we don't have a chance of getting + * a word in since the IOC probably crashed in message + * processing. So don't waste our time. + */ + if (MPT_DB_IS_IN_USE(mpt_rd_db(mpt))) { + mpt_prt(mpt, "soft reset failed: doorbell wedged"); + return MPT_FAIL; + } + + /* Send the reset request to the IOC */ + mpt_write(mpt, MPT_OFFSET_DOORBELL, + MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET << MPI_DOORBELL_FUNCTION_SHIFT); + if (mpt_wait_db_ack(mpt) != MPT_OK) { + mpt_prt(mpt, "soft reset failed: ack timeout"); + return MPT_FAIL; + } + + /* Wait for the IOC to reload and come out of reset state */ + if (mpt_wait_state(mpt, MPT_DB_STATE_READY) != MPT_OK) { + mpt_prt(mpt, "soft reset failed: device did not start running"); + return MPT_FAIL; + } + + return MPT_OK; +} + +/* This is a magic diagnostic reset that resets all the ARM + * processors in the chip. + */ +void +mpt_hard_reset(mpt_softc_t *mpt) +{ + /* This extra read comes for the Linux source + * released by LSI. It's function is undocumented! + */ + if (mpt->verbose) { + mpt_prt(mpt, "hard reset"); + } + mpt_read(mpt, MPT_OFFSET_FUBAR); + + /* Enable diagnostic registers */ + mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_1); + mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_2); + mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_3); + mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_4); + mpt_write(mpt, MPT_OFFSET_SEQUENCE, MPT_DIAG_SEQUENCE_5); + + /* Diag. port is now active so we can now hit the reset bit */ + mpt_write(mpt, MPT_OFFSET_DIAGNOSTIC, MPT_DIAG_RESET_IOC); + + DELAY(10000); + + /* Disable Diagnostic Register */ + mpt_write(mpt, MPT_OFFSET_SEQUENCE, 0xFF); + + /* Restore the config register values */ + /* Hard resets are known to screw up the BAR for diagnostic + memory accesses (Mem1). */ + mpt_set_config_regs(mpt); + if (mpt->mpt2 != NULL) { + mpt_set_config_regs(mpt->mpt2); + } + + /* Note that if there is no valid firmware to run, the doorbell will + remain in the reset state (0x00000000) */ +} + +/* + * Reset the IOC when needed. Try software command first then if needed + * poke at the magic diagnostic reset. Note that a hard reset resets + * *both* IOCs on dual function chips (FC929 && LSI1030) as well as + * fouls up the PCI configuration registers. + */ +int +mpt_reset(mpt_softc_t *mpt) +{ + int ret; + + /* Try a soft reset */ + if ((ret = mpt_soft_reset(mpt)) != MPT_OK) { + /* Failed; do a hard reset */ + mpt_hard_reset(mpt); + + /* Wait for the IOC to reload and come out of reset state */ + ret = mpt_wait_state(mpt, MPT_DB_STATE_READY); + if (ret != MPT_OK) { + mpt_prt(mpt, "failed to reset device"); + } + } + + return ret; +} + +/* Return a command buffer to the free queue */ +void +mpt_free_request(mpt_softc_t *mpt, request_t *req) +{ + if (req == NULL || req != &mpt->request_pool[req->index]) { + panic("mpt_free_request bad req ptr\n"); + return; + } + if (req->debug == REQ_FREE) { + /* + * XXX MU this should not happen but do not corrupt the free + * list if it does + */ + mpt_prt(mpt, "request %d already free\n", req->index); + return; + } + req->sequence = 0; + req->xfer = NULL; + req->debug = REQ_FREE; + SLIST_INSERT_HEAD(&mpt->request_free_list, req, link); +} + +/* Initialize command buffer */ +void +mpt_init_request(mpt_softc_t *mpt, request_t *req) +{ + if (req == NULL || req != &mpt->request_pool[req->index]) { + panic("mpt_init_request bad req ptr\n"); + return; + } + req->sequence = 0; + req->xfer = NULL; + req->debug = REQ_FREE; + SLIST_INSERT_HEAD(&mpt->request_free_list, req, link); +} +/* Get a command buffer from the free queue */ +request_t * +mpt_get_request(mpt_softc_t *mpt) +{ + request_t *req; + req = SLIST_FIRST(&mpt->request_free_list); + if (req != NULL) { + if (req != &mpt->request_pool[req->index]) { + panic("mpt_get_request: corrupted request free list\n"); + } + if (req->xfer != NULL) { + panic("mpt_get_request: corrupted request free list (xfer)\n"); + } + SLIST_REMOVE_HEAD(&mpt->request_free_list, link); + req->debug = REQ_IN_PROGRESS; + } + return req; +} + +/* Pass the command to the IOC */ +void +mpt_send_cmd(mpt_softc_t *mpt, request_t *req) +{ + req->sequence = mpt->sequence++; + if (mpt->verbose > 1) { + u_int32_t *pReq; + pReq = req->req_vbuf; + mpt_prt(mpt, "Send Request %d (0x%x):", + req->index, req->req_pbuf); + mpt_prt(mpt, "%08x %08x %08x %08x", + pReq[0], pReq[1], pReq[2], pReq[3]); + mpt_prt(mpt, "%08x %08x %08x %08x", + pReq[4], pReq[5], pReq[6], pReq[7]); + mpt_prt(mpt, "%08x %08x %08x %08x", + pReq[8], pReq[9], pReq[10], pReq[11]); + mpt_prt(mpt, "%08x %08x %08x %08x", + pReq[12], pReq[13], pReq[14], pReq[15]); + } + MPT_SYNC_REQ(mpt, req, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + req->debug = REQ_ON_CHIP; + mpt_write(mpt, MPT_OFFSET_REQUEST_Q, (u_int32_t) req->req_pbuf); +} + +/* + * Give the reply buffer back to the IOC after we have + * finished processing it. + */ +void +mpt_free_reply(mpt_softc_t *mpt, u_int32_t ptr) +{ + mpt_write(mpt, MPT_OFFSET_REPLY_Q, ptr); +} + +/* Get a reply from the IOC */ +u_int32_t +mpt_pop_reply_queue(mpt_softc_t *mpt) +{ + return mpt_read(mpt, MPT_OFFSET_REPLY_Q); +} + +/* + * Send a command to the IOC via the handshake register. + * + * Only done at initialization time and for certain unusual + * commands such as device/bus reset as specified by LSI. + */ +int +mpt_send_handshake_cmd(mpt_softc_t *mpt, size_t len, void *cmd) +{ + int i; + u_int32_t data, *data32; + + /* Check condition of the IOC */ + data = mpt_rd_db(mpt); + if (((MPT_STATE(data) != MPT_DB_STATE_READY) && + (MPT_STATE(data) != MPT_DB_STATE_RUNNING) && + (MPT_STATE(data) != MPT_DB_STATE_FAULT)) || + ( MPT_DB_IS_IN_USE(data) )) { + mpt_prt(mpt, "handshake aborted due to invalid doorbell state"); + mpt_print_db(data); + return(EBUSY); + } + + /* We move things in 32 bit chunks */ + len = (len + 3) >> 2; + data32 = cmd; + + /* Clear any left over pending doorbell interrupts */ + if (MPT_DB_INTR(mpt_rd_intr(mpt))) + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + + /* + * Tell the handshake reg. we are going to send a command + * and how long it is going to be. + */ + data = (MPI_FUNCTION_HANDSHAKE << MPI_DOORBELL_FUNCTION_SHIFT) | + (len << MPI_DOORBELL_ADD_DWORDS_SHIFT); + mpt_write(mpt, MPT_OFFSET_DOORBELL, data); + + /* Wait for the chip to notice */ + if (mpt_wait_db_int(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_send_handshake_cmd timeout1"); + return ETIMEDOUT; + } + + /* Clear the interrupt */ + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + + if (mpt_wait_db_ack(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_send_handshake_cmd timeout2"); + return ETIMEDOUT; + } + + /* Send the command */ + for (i = 0; i < len; i++) { + mpt_write(mpt, MPT_OFFSET_DOORBELL, *data32++); + if (mpt_wait_db_ack(mpt) != MPT_OK) { + mpt_prt(mpt, + "mpt_send_handshake_cmd timeout! index = %d", i); + return ETIMEDOUT; + } + } + return MPT_OK; +} + +/* Get the response from the handshake register */ +int +mpt_recv_handshake_reply(mpt_softc_t *mpt, size_t reply_len, void *reply) +{ + int left, reply_left; + u_int16_t *data16; + MSG_DEFAULT_REPLY *hdr; + + /* We move things out in 16 bit chunks */ + reply_len >>= 1; + data16 = (u_int16_t *)reply; + + hdr = (MSG_DEFAULT_REPLY *)reply; + + /* Get first word */ + if (mpt_wait_db_int(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_recv_handshake_cmd timeout1"); + return ETIMEDOUT; + } + *data16++ = mpt_read(mpt, MPT_OFFSET_DOORBELL) & MPT_DB_DATA_MASK; + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + + /* Get Second Word */ + if (mpt_wait_db_int(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_recv_handshake_cmd timeout2"); + return ETIMEDOUT; + } + *data16++ = mpt_read(mpt, MPT_OFFSET_DOORBELL) & MPT_DB_DATA_MASK; + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + + /* With the second word, we can now look at the length */ + if (mpt->verbose > 1 && ((reply_len >> 1) != hdr->MsgLength)) { + mpt_prt(mpt, "reply length does not match message length: " + "got 0x%02x, expected 0x%02x", + hdr->MsgLength << 2, reply_len << 1); + } + + /* Get rest of the reply; but don't overflow the provided buffer */ + left = (hdr->MsgLength << 1) - 2; + reply_left = reply_len - 2; + while (left--) { + u_int16_t datum; + + if (mpt_wait_db_int(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_recv_handshake_cmd timeout3"); + return ETIMEDOUT; + } + datum = mpt_read(mpt, MPT_OFFSET_DOORBELL); + + if (reply_left-- > 0) + *data16++ = datum & MPT_DB_DATA_MASK; + + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + } + + /* One more wait & clear at the end */ + if (mpt_wait_db_int(mpt) != MPT_OK) { + mpt_prt(mpt, "mpt_recv_handshake_cmd timeout4"); + return ETIMEDOUT; + } + mpt_write(mpt, MPT_OFFSET_INTR_STATUS, 0); + + if ((hdr->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + if (mpt->verbose > 1) + mpt_print_reply(hdr); + return (MPT_FAIL | hdr->IOCStatus); + } + + return (0); +} + +static int +mpt_get_iocfacts(mpt_softc_t *mpt, MSG_IOC_FACTS_REPLY *freplp) +{ + MSG_IOC_FACTS f_req; + int error; + + bzero(&f_req, sizeof f_req); + f_req.Function = MPI_FUNCTION_IOC_FACTS; + f_req.MsgContext = 0x12071942; + error = mpt_send_handshake_cmd(mpt, sizeof f_req, &f_req); + if (error) + return(error); + error = mpt_recv_handshake_reply(mpt, sizeof (*freplp), freplp); + return (error); +} + +static int +mpt_get_portfacts(mpt_softc_t *mpt, MSG_PORT_FACTS_REPLY *freplp) +{ + MSG_PORT_FACTS f_req; + int error; + + /* XXX: Only getting PORT FACTS for Port 0 */ + bzero(&f_req, sizeof f_req); + f_req.Function = MPI_FUNCTION_PORT_FACTS; + f_req.MsgContext = 0x12071943; + error = mpt_send_handshake_cmd(mpt, sizeof f_req, &f_req); + if (error) + return(error); + error = mpt_recv_handshake_reply(mpt, sizeof (*freplp), freplp); + return (error); +} + +/* + * Send the initialization request. This is where we specify how many + * SCSI busses and how many devices per bus we wish to emulate. + * This is also the command that specifies the max size of the reply + * frames from the IOC that we will be allocating. + */ +static int +mpt_send_ioc_init(mpt_softc_t *mpt, u_int32_t who) +{ + int error = 0; + MSG_IOC_INIT init; + MSG_IOC_INIT_REPLY reply; + + bzero(&init, sizeof init); + init.WhoInit = who; + init.Function = MPI_FUNCTION_IOC_INIT; + if (mpt->is_fc) { + init.MaxDevices = 255; + } else { + init.MaxDevices = 16; + } + init.MaxBuses = 1; + init.ReplyFrameSize = MPT_REPLY_SIZE; + init.MsgContext = 0x12071941; + + if ((error = mpt_send_handshake_cmd(mpt, sizeof init, &init)) != 0) { + return(error); + } + + error = mpt_recv_handshake_reply(mpt, sizeof reply, &reply); + return (error); +} + + +/* + * Utiltity routine to read configuration headers and pages + */ + +static int +mpt_read_cfg_header(mpt_softc_t *, int, int, int, fCONFIG_PAGE_HEADER *); + +static int +mpt_read_cfg_header(mpt_softc_t *mpt, int PageType, int PageNumber, + int PageAddress, fCONFIG_PAGE_HEADER *rslt) +{ + int count; + request_t *req; + MSG_CONFIG *cfgp; + MSG_CONFIG_REPLY *reply; + + req = mpt_get_request(mpt); + + cfgp = req->req_vbuf; + bzero(cfgp, sizeof *cfgp); + + cfgp->Action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfgp->Function = MPI_FUNCTION_CONFIG; + cfgp->Header.PageNumber = (U8) PageNumber; + cfgp->Header.PageType = (U8) PageType; + cfgp->PageAddress = PageAddress; + MPI_pSGE_SET_FLAGS(((SGE_SIMPLE32 *) &cfgp->PageBufferSGE), + (MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_SIMPLE_ELEMENT | MPI_SGE_FLAGS_END_OF_LIST)); + cfgp->MsgContext = req->index | 0x80000000; + + mpt_check_doorbell(mpt); + mpt_send_cmd(mpt, req); + count = 0; + do { + DELAY(500); + mpt_intr(mpt); + if (++count == 1000) { + mpt_prt(mpt, "read_cfg_header timed out"); + return (-1); + } + } while (req->debug == REQ_ON_CHIP); + + reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); + if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + mpt_prt(mpt, "mpt_read_cfg_header: Config Info Status %x", + reply->IOCStatus); + mpt_free_reply(mpt, (req->sequence << 1)); + return (-1); + } + bcopy(&reply->Header, rslt, sizeof (fCONFIG_PAGE_HEADER)); + mpt_free_reply(mpt, (req->sequence << 1)); + mpt_free_request(mpt, req); + return (0); +} + +#define CFG_DATA_OFF 128 + +int +mpt_read_cfg_page(mpt_softc_t *mpt, int PageAddress, fCONFIG_PAGE_HEADER *hdr) +{ + int count; + request_t *req; + SGE_SIMPLE32 *se; + MSG_CONFIG *cfgp; + size_t amt; + MSG_CONFIG_REPLY *reply; + + req = mpt_get_request(mpt); + + cfgp = req->req_vbuf; + bzero(cfgp, MPT_REQUEST_AREA); + cfgp->Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + cfgp->Function = MPI_FUNCTION_CONFIG; + cfgp->Header = *hdr; + amt = (cfgp->Header.PageLength * sizeof (u_int32_t)); + cfgp->Header.PageType &= MPI_CONFIG_PAGETYPE_MASK; + cfgp->PageAddress = PageAddress; + se = (SGE_SIMPLE32 *) &cfgp->PageBufferSGE; + se->Address = req->req_pbuf + CFG_DATA_OFF; + MPI_pSGE_SET_LENGTH(se, amt); + MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_END_OF_LIST)); + + cfgp->MsgContext = req->index | 0x80000000; + + mpt_check_doorbell(mpt); + mpt_send_cmd(mpt, req); + count = 0; + do { + DELAY(500); + mpt_intr(mpt); + if (++count == 1000) { + mpt_prt(mpt, "read_cfg_page timed out"); + return (-1); + } + } while (req->debug == REQ_ON_CHIP); + + reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); + if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + mpt_prt(mpt, "mpt_read_cfg_page: Config Info Status %x", + reply->IOCStatus); + mpt_free_reply(mpt, (req->sequence << 1)); + return (-1); + } + mpt_free_reply(mpt, (req->sequence << 1)); +#if 0 /* XXXJRT */ + bus_dmamap_sync(mpt->request_dmat, mpt->request_dmap, + BUS_DMASYNC_POSTREAD); +#endif + if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 0) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_0); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 1) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_1); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 2) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_2); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && + cfgp->Header.PageNumber == 0) { + amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_0); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && + cfgp->Header.PageNumber == 1) { + amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_1); + } + bcopy(((caddr_t)req->req_vbuf)+CFG_DATA_OFF, hdr, amt); + mpt_free_request(mpt, req); + return (0); +} + +int +mpt_write_cfg_page(mpt_softc_t *mpt, int PageAddress, fCONFIG_PAGE_HEADER *hdr) +{ + int count, hdr_attr; + request_t *req; + SGE_SIMPLE32 *se; + MSG_CONFIG *cfgp; + size_t amt; + MSG_CONFIG_REPLY *reply; + + req = mpt_get_request(mpt); + + cfgp = req->req_vbuf; + bzero(cfgp, sizeof *cfgp); + + hdr_attr = hdr->PageType & MPI_CONFIG_PAGEATTR_MASK; + if (hdr_attr != MPI_CONFIG_PAGEATTR_CHANGEABLE && + hdr_attr != MPI_CONFIG_PAGEATTR_PERSISTENT) { + mpt_prt(mpt, "page type 0x%x not changeable", + hdr->PageType & MPI_CONFIG_PAGETYPE_MASK); + return (-1); + } + hdr->PageType &= MPI_CONFIG_PAGETYPE_MASK; + + cfgp->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfgp->Function = MPI_FUNCTION_CONFIG; + cfgp->Header = *hdr; + amt = (cfgp->Header.PageLength * sizeof (u_int32_t)); + cfgp->PageAddress = PageAddress; + + se = (SGE_SIMPLE32 *) &cfgp->PageBufferSGE; + se->Address = req->req_pbuf + CFG_DATA_OFF; + MPI_pSGE_SET_LENGTH(se, amt); + MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | + MPI_SGE_FLAGS_END_OF_LIST | MPI_SGE_FLAGS_HOST_TO_IOC)); + + cfgp->MsgContext = req->index | 0x80000000; + + if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 0) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_0); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 1) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_1); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_PORT && + cfgp->Header.PageNumber == 2) { + amt = sizeof (fCONFIG_PAGE_SCSI_PORT_2); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && + cfgp->Header.PageNumber == 0) { + amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_0); + } else if (cfgp->Header.PageType == MPI_CONFIG_PAGETYPE_SCSI_DEVICE && + cfgp->Header.PageNumber == 1) { + amt = sizeof (fCONFIG_PAGE_SCSI_DEVICE_1); + } + bcopy(hdr, ((caddr_t)req->req_vbuf)+CFG_DATA_OFF, amt); + /* Restore stripped out attributes */ + hdr->PageType |= hdr_attr; + + mpt_check_doorbell(mpt); + mpt_send_cmd(mpt, req); + count = 0; + do { + DELAY(500); + mpt_intr(mpt); + if (++count == 1000) { + hdr->PageType |= hdr_attr; + mpt_prt(mpt, "mpt_write_cfg_page timed out"); + return (-1); + } + } while (req->debug == REQ_ON_CHIP); + + reply = (MSG_CONFIG_REPLY *) MPT_REPLY_PTOV(mpt, req->sequence); + if ((reply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + mpt_prt(mpt, "mpt_write_cfg_page: Config Info Status %x", + reply->IOCStatus); + mpt_free_reply(mpt, (req->sequence << 1)); + return (-1); + } + mpt_free_reply(mpt, (req->sequence << 1)); + + mpt_free_request(mpt, req); + return (0); +} + +/* + * Read SCSI configuration information + */ +static int +mpt_read_config_info_spi(mpt_softc_t *mpt) +{ + int rv, i; + + rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 0, + 0, &mpt->mpt_port_page0.Header); + if (rv) { + return (-1); + } + if (mpt->verbose > 1) { + mpt_prt(mpt, "SPI Port Page 0 Header: %x %x %x %x", + mpt->mpt_port_page0.Header.PageVersion, + mpt->mpt_port_page0.Header.PageLength, + mpt->mpt_port_page0.Header.PageNumber, + mpt->mpt_port_page0.Header.PageType); + } + + rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 1, + 0, &mpt->mpt_port_page1.Header); + if (rv) { + return (-1); + } + if (mpt->verbose > 1) { + mpt_prt(mpt, "SPI Port Page 1 Header: %x %x %x %x", + mpt->mpt_port_page1.Header.PageVersion, + mpt->mpt_port_page1.Header.PageLength, + mpt->mpt_port_page1.Header.PageNumber, + mpt->mpt_port_page1.Header.PageType); + } + + rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_PORT, 2, + 0, &mpt->mpt_port_page2.Header); + if (rv) { + return (-1); + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, "SPI Port Page 2 Header: %x %x %x %x", + mpt->mpt_port_page1.Header.PageVersion, + mpt->mpt_port_page1.Header.PageLength, + mpt->mpt_port_page1.Header.PageNumber, + mpt->mpt_port_page1.Header.PageType); + } + + for (i = 0; i < 16; i++) { + rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, + 0, i, &mpt->mpt_dev_page0[i].Header); + if (rv) { + return (-1); + } + if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Target %d Device Page 0 Header: %x %x %x %x", + i, mpt->mpt_dev_page0[i].Header.PageVersion, + mpt->mpt_dev_page0[i].Header.PageLength, + mpt->mpt_dev_page0[i].Header.PageNumber, + mpt->mpt_dev_page0[i].Header.PageType); + } + + rv = mpt_read_cfg_header(mpt, MPI_CONFIG_PAGETYPE_SCSI_DEVICE, + 1, i, &mpt->mpt_dev_page1[i].Header); + if (rv) { + return (-1); + } + if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Target %d Device Page 1 Header: %x %x %x %x", + i, mpt->mpt_dev_page1[i].Header.PageVersion, + mpt->mpt_dev_page1[i].Header.PageLength, + mpt->mpt_dev_page1[i].Header.PageNumber, + mpt->mpt_dev_page1[i].Header.PageType); + } + } + + /* + * At this point, we don't *have* to fail. As long as we have + * valid config header information, we can (barely) lurch + * along. + */ + + rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page0.Header); + if (rv) { + mpt_prt(mpt, "failed to read SPI Port Page 0"); + } else if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Port Page 0: Capabilities %x PhysicalInterface %x", + mpt->mpt_port_page0.Capabilities, + mpt->mpt_port_page0.PhysicalInterface); + } + + rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page1.Header); + if (rv) { + mpt_prt(mpt, "failed to read SPI Port Page 1"); + } else if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Port Page 1: Configuration %x OnBusTimerValue %x", + mpt->mpt_port_page1.Configuration, + mpt->mpt_port_page1.OnBusTimerValue); + } + + rv = mpt_read_cfg_page(mpt, 0, &mpt->mpt_port_page2.Header); + if (rv) { + mpt_prt(mpt, "failed to read SPI Port Page 2"); + } else if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Port Page 2: Flags %x Settings %x", + mpt->mpt_port_page2.PortFlags, + mpt->mpt_port_page2.PortSettings); + for (i = 0; i < 16; i++) { + mpt_prt(mpt, + "SPI Port Page 2 Tgt %d: timo %x SF %x Flags %x", + i, mpt->mpt_port_page2.DeviceSettings[i].Timeout, + mpt->mpt_port_page2.DeviceSettings[i].SyncFactor, + mpt->mpt_port_page2.DeviceSettings[i].DeviceFlags); + } + } + + for (i = 0; i < 16; i++) { + rv = mpt_read_cfg_page(mpt, i, &mpt->mpt_dev_page0[i].Header); + if (rv) { + mpt_prt(mpt, "cannot read SPI Tgt %d Device Page 0", i); + continue; + } + if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Tgt %d Page 0: NParms %x Information %x", + i, mpt->mpt_dev_page0[i].NegotiatedParameters, + mpt->mpt_dev_page0[i].Information); + } + rv = mpt_read_cfg_page(mpt, i, &mpt->mpt_dev_page1[i].Header); + if (rv) { + mpt_prt(mpt, "cannot read SPI Tgt %d Device Page 1", i); + continue; + } + if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Tgt %d Page 1: RParms %x Configuration %x", + i, mpt->mpt_dev_page1[i].RequestedParameters, + mpt->mpt_dev_page1[i].Configuration); + } + } + return (0); +} + +/* + * Validate SPI configuration information. + * + * In particular, validate SPI Port Page 1. + */ +static int +mpt_set_initial_config_spi(mpt_softc_t *mpt) +{ + int i, pp1val = ((1 << mpt->mpt_ini_id) << 16) | mpt->mpt_ini_id; + + mpt->mpt_disc_enable = 0xff; + mpt->mpt_tag_enable = 0; + + if (mpt->mpt_port_page1.Configuration != pp1val) { + fCONFIG_PAGE_SCSI_PORT_1 tmp; + mpt_prt(mpt, + "SPI Port Page 1 Config value bad (%x)- should be %x", + mpt->mpt_port_page1.Configuration, pp1val); + tmp = mpt->mpt_port_page1; + tmp.Configuration = pp1val; + if (mpt_write_cfg_page(mpt, 0, &tmp.Header)) { + return (-1); + } + if (mpt_read_cfg_page(mpt, 0, &tmp.Header)) { + return (-1); + } + if (tmp.Configuration != pp1val) { + mpt_prt(mpt, + "failed to reset SPI Port Page 1 Config value"); + return (-1); + } + mpt->mpt_port_page1 = tmp; + } + + for (i = 0; i < 16; i++) { + fCONFIG_PAGE_SCSI_DEVICE_1 tmp; + tmp = mpt->mpt_dev_page1[i]; + tmp.RequestedParameters = 0; + tmp.Configuration = 0; + if (mpt->verbose > 1) { + mpt_prt(mpt, + "Set Tgt %d SPI DevicePage 1 values to %x 0 %x", + i, tmp.RequestedParameters, tmp.Configuration); + } + if (mpt_write_cfg_page(mpt, i, &tmp.Header)) { + return (-1); + } + if (mpt_read_cfg_page(mpt, i, &tmp.Header)) { + return (-1); + } + mpt->mpt_dev_page1[i] = tmp; + if (mpt->verbose > 1) { + mpt_prt(mpt, + "SPI Tgt %d Page 1: RParm %x Configuration %x", i, + mpt->mpt_dev_page1[i].RequestedParameters, + mpt->mpt_dev_page1[i].Configuration); + } + } + return (0); +} + +/* + * Enable IOC port + */ +static int +mpt_send_port_enable(mpt_softc_t *mpt, int port) +{ + int count; + request_t *req; + MSG_PORT_ENABLE *enable_req; + + req = mpt_get_request(mpt); + + enable_req = req->req_vbuf; + bzero(enable_req, sizeof *enable_req); + + enable_req->Function = MPI_FUNCTION_PORT_ENABLE; + enable_req->MsgContext = req->index | 0x80000000; + enable_req->PortNumber = port; + + mpt_check_doorbell(mpt); + if (mpt->verbose > 1) { + mpt_prt(mpt, "enabling port %d", port); + } + mpt_send_cmd(mpt, req); + + count = 0; + do { + DELAY(500); + mpt_intr(mpt); + if (++count == 100000) { + mpt_prt(mpt, "port enable timed out"); + return (-1); + } + } while (req->debug == REQ_ON_CHIP); + mpt_free_request(mpt, req); + return (0); +} + +/* + * Enable/Disable asynchronous event reporting. + * + * NB: this is the first command we send via shared memory + * instead of the handshake register. + */ +static int +mpt_send_event_request(mpt_softc_t *mpt, int onoff) +{ + request_t *req; + MSG_EVENT_NOTIFY *enable_req; + + req = mpt_get_request(mpt); + + enable_req = req->req_vbuf; + bzero(enable_req, sizeof *enable_req); + + enable_req->Function = MPI_FUNCTION_EVENT_NOTIFICATION; + enable_req->MsgContext = req->index | 0x80000000; + enable_req->Switch = onoff; + + mpt_check_doorbell(mpt); + if (mpt->verbose > 1) { + mpt_prt(mpt, "%sabling async events", onoff? "en" : "dis"); + } + mpt_send_cmd(mpt, req); + + return (0); +} + +/* + * Un-mask the interrupts on the chip. + */ +void +mpt_enable_ints(mpt_softc_t *mpt) +{ + /* Unmask every thing except door bell int */ + mpt_write(mpt, MPT_OFFSET_INTR_MASK, MPT_INTR_DB_MASK); +} + +/* + * Mask the interrupts on the chip. + */ +void +mpt_disable_ints(mpt_softc_t *mpt) +{ + /* Mask all interrupts */ + mpt_write(mpt, MPT_OFFSET_INTR_MASK, + MPT_INTR_REPLY_MASK | MPT_INTR_DB_MASK); +} + +/* (Re)Initialize the chip for use */ +int +mpt_init(mpt_softc_t *mpt, u_int32_t who) +{ + int try; + MSG_IOC_FACTS_REPLY facts; + MSG_PORT_FACTS_REPLY pfp; + u_int32_t pptr; + int val; + + /* Put all request buffers (back) on the free list */ + SLIST_INIT(&mpt->request_free_list); + for (val = 0; val < MPT_MAX_REQUESTS(mpt); val++) { + mpt_init_request(mpt, &mpt->request_pool[val]); + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, "doorbell req = %s", + mpt_ioc_diag(mpt_read(mpt, MPT_OFFSET_DOORBELL))); + } + + /* + * Start by making sure we're not at FAULT or RESET state + */ + switch (mpt_rd_db(mpt) & MPT_DB_STATE_MASK) { + case MPT_DB_STATE_RESET: + case MPT_DB_STATE_FAULT: + if (mpt_reset(mpt) != MPT_OK) { + return (EIO); + } + default: + break; + } + + for (try = 0; try < MPT_MAX_TRYS; try++) { + /* + * No need to reset if the IOC is already in the READY state. + * + * Force reset if initialization failed previously. + * Note that a hard_reset of the second channel of a '929 + * will stop operation of the first channel. Hopefully, if the + * first channel is ok, the second will not require a hard + * reset. + */ + if ((mpt_rd_db(mpt) & MPT_DB_STATE_MASK) != + MPT_DB_STATE_READY) { + if (mpt_reset(mpt) != MPT_OK) { + DELAY(10000); + continue; + } + } + + if (mpt_get_iocfacts(mpt, &facts) != MPT_OK) { + mpt_prt(mpt, "mpt_get_iocfacts failed"); + continue; + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, + "IOCFACTS: GlobalCredits=%d BlockSize=%u " + "Request Frame Size %u\n", facts.GlobalCredits, + facts.BlockSize, facts.RequestFrameSize); + } + mpt->mpt_global_credits = facts.GlobalCredits; + mpt->request_frame_size = facts.RequestFrameSize; + + if (mpt_get_portfacts(mpt, &pfp) != MPT_OK) { + mpt_prt(mpt, "mpt_get_portfacts failed"); + continue; + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, + "PORTFACTS: Type %x PFlags %x IID %d MaxDev %d\n", + pfp.PortType, pfp.ProtocolFlags, pfp.PortSCSIID, + pfp.MaxDevices); + } + + if (pfp.PortType != MPI_PORTFACTS_PORTTYPE_SCSI && + pfp.PortType != MPI_PORTFACTS_PORTTYPE_FC) { + mpt_prt(mpt, "Unsupported Port Type (%x)", + pfp.PortType); + return (ENXIO); + } + if (!(pfp.ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) { + mpt_prt(mpt, "initiator role unsupported"); + return (ENXIO); + } + if (pfp.PortType == MPI_PORTFACTS_PORTTYPE_FC) { + mpt->is_fc = 1; + } else { + mpt->is_fc = 0; + } + mpt->mpt_ini_id = pfp.PortSCSIID; + + if (mpt_send_ioc_init(mpt, who) != MPT_OK) { + mpt_prt(mpt, "mpt_send_ioc_init failed"); + continue; + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, "mpt_send_ioc_init ok"); + } + + if (mpt_wait_state(mpt, MPT_DB_STATE_RUNNING) != MPT_OK) { + mpt_prt(mpt, "IOC failed to go to run state"); + continue; + } + if (mpt->verbose > 1) { + mpt_prt(mpt, "IOC now at RUNSTATE"); + } + + /* + * Give it reply buffers + * + * Do *not* except global credits. + */ + for (val = 0, pptr = mpt->reply_phys; + (pptr + MPT_REPLY_SIZE) < (mpt->reply_phys + PAGE_SIZE); + pptr += MPT_REPLY_SIZE) { + mpt_free_reply(mpt, pptr); + if (++val == mpt->mpt_global_credits - 1) + break; + } + + /* + * Enable asynchronous event reporting + */ + mpt_send_event_request(mpt, 1); + + + /* + * Read set up initial configuration information + * (SPI only for now) + */ + + if (mpt->is_fc == 0) { + if (mpt_read_config_info_spi(mpt)) { + return (EIO); + } + if (mpt_set_initial_config_spi(mpt)) { + return (EIO); + } + } + + /* + * Now enable the port + */ + if (mpt_send_port_enable(mpt, 0) != MPT_OK) { + mpt_prt(mpt, "failed to enable port 0"); + continue; + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, "enabled port 0"); + } + + /* Everything worked */ + break; + } + + if (try >= MPT_MAX_TRYS) { + mpt_prt(mpt, "failed to initialize IOC"); + return (EIO); + } + + if (mpt->verbose > 1) { + mpt_prt(mpt, "enabling interrupts"); + } + + mpt_enable_ints(mpt); + return (0); +} |