1d3c7b9a0SKenneth D. Merry /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 4d3c7b9a0SKenneth D. Merry * Copyright (c) 2009 Yahoo! Inc. 5ef065d89SStephen McConnell * Copyright (c) 2011-2015 LSI Corp. 6ef065d89SStephen McConnell * Copyright (c) 2013-2015 Avago Technologies 7d043c564SKenneth D. Merry * All rights reserved. 8d043c564SKenneth D. Merry * 9d043c564SKenneth D. Merry * Redistribution and use in source and binary forms, with or without 10d043c564SKenneth D. Merry * modification, are permitted provided that the following conditions 11d043c564SKenneth D. Merry * are met: 12d043c564SKenneth D. Merry * 1. Redistributions of source code must retain the above copyright 13d043c564SKenneth D. Merry * notice, this list of conditions and the following disclaimer. 14d043c564SKenneth D. Merry * 2. Redistributions in binary form must reproduce the above copyright 15d043c564SKenneth D. Merry * notice, this list of conditions and the following disclaimer in the 16d043c564SKenneth D. Merry * documentation and/or other materials provided with the distribution. 17d043c564SKenneth D. Merry * 18d043c564SKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19d043c564SKenneth D. Merry * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20d043c564SKenneth D. Merry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21d043c564SKenneth D. Merry * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22d043c564SKenneth D. Merry * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23d043c564SKenneth D. Merry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24d043c564SKenneth D. Merry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25d043c564SKenneth D. Merry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26d043c564SKenneth D. Merry * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27d043c564SKenneth D. Merry * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28d043c564SKenneth D. Merry * SUCH DAMAGE. 29d043c564SKenneth D. Merry * 30ef065d89SStephen McConnell * Avago Technologies (LSI) MPT-Fusion Host Adapter FreeBSD 31d3c7b9a0SKenneth D. Merry */ 32d3c7b9a0SKenneth D. Merry 33d3c7b9a0SKenneth D. Merry #include <sys/cdefs.h> 34ef065d89SStephen McConnell /* Communications core for Avago Technologies (LSI) MPT2 */ 35d3c7b9a0SKenneth D. Merry 36d043c564SKenneth D. Merry /* TODO Move headers to mpsvar */ 37d3c7b9a0SKenneth D. Merry #include <sys/types.h> 38d3c7b9a0SKenneth D. Merry #include <sys/param.h> 39d3c7b9a0SKenneth D. Merry #include <sys/systm.h> 40d3c7b9a0SKenneth D. Merry #include <sys/kernel.h> 41d3c7b9a0SKenneth D. Merry #include <sys/selinfo.h> 42d3c7b9a0SKenneth D. Merry #include <sys/lock.h> 43d3c7b9a0SKenneth D. Merry #include <sys/mutex.h> 44d3c7b9a0SKenneth D. Merry #include <sys/module.h> 45d3c7b9a0SKenneth D. Merry #include <sys/bus.h> 46d3c7b9a0SKenneth D. Merry #include <sys/conf.h> 47d3c7b9a0SKenneth D. Merry #include <sys/bio.h> 48d3c7b9a0SKenneth D. Merry #include <sys/malloc.h> 49d3c7b9a0SKenneth D. Merry #include <sys/uio.h> 50d3c7b9a0SKenneth D. Merry #include <sys/sysctl.h> 51bec09074SScott Long #include <sys/smp.h> 52d043c564SKenneth D. Merry #include <sys/queue.h> 53d043c564SKenneth D. Merry #include <sys/kthread.h> 549b91b192SKenneth D. Merry #include <sys/taskqueue.h> 5549dfe4a2SKenneth D. Merry #include <sys/endian.h> 56d043c564SKenneth D. Merry #include <sys/eventhandler.h> 57867aa8cdSScott Long #include <sys/sbuf.h> 58cf6ea6f2SScott Long #include <sys/priv.h> 59d3c7b9a0SKenneth D. Merry 60d3c7b9a0SKenneth D. Merry #include <machine/bus.h> 61d3c7b9a0SKenneth D. Merry #include <machine/resource.h> 62d3c7b9a0SKenneth D. Merry #include <sys/rman.h> 63be4aa869SKenneth D. Merry #include <sys/proc.h> 64d3c7b9a0SKenneth D. Merry 65d043c564SKenneth D. Merry #include <dev/pci/pcivar.h> 66d043c564SKenneth D. Merry 679b91b192SKenneth D. Merry #include <cam/cam.h> 68d3c7b9a0SKenneth D. Merry #include <cam/scsi/scsi_all.h> 69d3c7b9a0SKenneth D. Merry 70d3c7b9a0SKenneth D. Merry #include <dev/mps/mpi/mpi2_type.h> 71d3c7b9a0SKenneth D. Merry #include <dev/mps/mpi/mpi2.h> 72d3c7b9a0SKenneth D. Merry #include <dev/mps/mpi/mpi2_ioc.h> 73d043c564SKenneth D. Merry #include <dev/mps/mpi/mpi2_sas.h> 74d3c7b9a0SKenneth D. Merry #include <dev/mps/mpi/mpi2_cnfg.h> 75d043c564SKenneth D. Merry #include <dev/mps/mpi/mpi2_init.h> 76d043c564SKenneth D. Merry #include <dev/mps/mpi/mpi2_tool.h> 77d043c564SKenneth D. Merry #include <dev/mps/mps_ioctl.h> 78d3c7b9a0SKenneth D. Merry #include <dev/mps/mpsvar.h> 79d3c7b9a0SKenneth D. Merry #include <dev/mps/mps_table.h> 80d3c7b9a0SKenneth D. Merry 81be4aa869SKenneth D. Merry static int mps_diag_reset(struct mps_softc *sc, int sleep_flag); 82d043c564SKenneth D. Merry static int mps_init_queues(struct mps_softc *sc); 833c5ac992SScott Long static void mps_resize_queues(struct mps_softc *sc); 84be4aa869SKenneth D. Merry static int mps_message_unit_reset(struct mps_softc *sc, int sleep_flag); 85d043c564SKenneth D. Merry static int mps_transition_operational(struct mps_softc *sc); 869b91b192SKenneth D. Merry static int mps_iocfacts_allocate(struct mps_softc *sc, uint8_t attaching); 879b91b192SKenneth D. Merry static void mps_iocfacts_free(struct mps_softc *sc); 88d3c7b9a0SKenneth D. Merry static void mps_startup(void *arg); 89d3c7b9a0SKenneth D. Merry static int mps_send_iocinit(struct mps_softc *sc); 909b91b192SKenneth D. Merry static int mps_alloc_queues(struct mps_softc *sc); 911415db6cSScott Long static int mps_alloc_hw_queues(struct mps_softc *sc); 929b91b192SKenneth D. Merry static int mps_alloc_replies(struct mps_softc *sc); 939b91b192SKenneth D. Merry static int mps_alloc_requests(struct mps_softc *sc); 94d3c7b9a0SKenneth D. Merry static int mps_attach_log(struct mps_softc *sc); 951610f95cSScott Long static __inline void mps_complete_command(struct mps_softc *sc, 961610f95cSScott Long struct mps_command *cm); 97d043c564SKenneth D. Merry static void mps_dispatch_event(struct mps_softc *sc, uintptr_t data, 98d043c564SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REPLY *reply); 99d3c7b9a0SKenneth D. Merry static void mps_config_complete(struct mps_softc *sc, struct mps_command *cm); 100d3c7b9a0SKenneth D. Merry static void mps_periodic(void *); 101d043c564SKenneth D. Merry static int mps_reregister_events(struct mps_softc *sc); 102d043c564SKenneth D. Merry static void mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm); 1039b91b192SKenneth D. Merry static int mps_get_iocfacts(struct mps_softc *sc, MPI2_IOC_FACTS_REPLY *facts); 104be4aa869SKenneth D. Merry static int mps_wait_db_ack(struct mps_softc *sc, int timeout, int sleep_flag); 105867aa8cdSScott Long static int mps_debug_sysctl(SYSCTL_HANDLER_ARGS); 106cf6ea6f2SScott Long static int mps_dump_reqs(SYSCTL_HANDLER_ARGS); 107867aa8cdSScott Long static void mps_parse_debug(struct mps_softc *sc, char *list); 108867aa8cdSScott Long 1097029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, mps, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 1107029da5cSPawel Biernacki "MPS Driver Parameters"); 111d3c7b9a0SKenneth D. Merry 112d3c7b9a0SKenneth D. Merry MALLOC_DEFINE(M_MPT2, "mps", "mpt2 driver memory"); 113e2997a03SKenneth D. Merry MALLOC_DECLARE(M_MPSUSER); 114d3c7b9a0SKenneth D. Merry 115d3c7b9a0SKenneth D. Merry /* 116d3c7b9a0SKenneth D. Merry * Do a "Diagnostic Reset" aka a hard reset. This should get the chip out of 117d3c7b9a0SKenneth D. Merry * any state and back to its initialization state machine. 118d3c7b9a0SKenneth D. Merry */ 119d3c7b9a0SKenneth D. Merry static char mpt2_reset_magic[] = { 0x00, 0x0f, 0x04, 0x0b, 0x02, 0x07, 0x0d }; 120d3c7b9a0SKenneth D. Merry 121be4aa869SKenneth D. Merry /* Added this union to smoothly convert le64toh cm->cm_desc.Words. 122be4aa869SKenneth D. Merry * Compiler only support unint64_t to be passed as argument. 123757ff642SScott Long * Otherwise it will throw below error 124be4aa869SKenneth D. Merry * "aggregate value used where an integer was expected" 125be4aa869SKenneth D. Merry */ 126be4aa869SKenneth D. Merry 1274bc604dcSScott Long typedef union { 128be4aa869SKenneth D. Merry u64 word; 129be4aa869SKenneth D. Merry struct { 130be4aa869SKenneth D. Merry u32 low; 131be4aa869SKenneth D. Merry u32 high; 132be4aa869SKenneth D. Merry } u; 1334bc604dcSScott Long } request_descriptor_t; 134be4aa869SKenneth D. Merry 135fe839103SScott Long /* Rate limit chain-fail messages to 1 per minute */ 136fe839103SScott Long static struct timeval mps_chainfail_interval = { 60, 0 }; 137fe839103SScott Long 138be4aa869SKenneth D. Merry /* 139be4aa869SKenneth D. Merry * sleep_flag can be either CAN_SLEEP or NO_SLEEP. 140be4aa869SKenneth D. Merry * If this function is called from process context, it can sleep 141be4aa869SKenneth D. Merry * and there is no harm to sleep, in case if this fuction is called 142be4aa869SKenneth D. Merry * from Interrupt handler, we can not sleep and need NO_SLEEP flag set. 143be4aa869SKenneth D. Merry * based on sleep flags driver will call either msleep, pause or DELAY. 144be4aa869SKenneth D. Merry * msleep and pause are of same variant, but pause is used when mps_mtx 145be4aa869SKenneth D. Merry * is not hold by driver. 146be4aa869SKenneth D. Merry * 147be4aa869SKenneth D. Merry */ 148d3c7b9a0SKenneth D. Merry static int 149be4aa869SKenneth D. Merry mps_diag_reset(struct mps_softc *sc,int sleep_flag) 150d3c7b9a0SKenneth D. Merry { 151d3c7b9a0SKenneth D. Merry uint32_t reg; 152d3c7b9a0SKenneth D. Merry int i, error, tries = 0; 1537571e7f6SSteven Hartland uint8_t first_wait_done = FALSE; 154d3c7b9a0SKenneth D. Merry 155757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 156d3c7b9a0SKenneth D. Merry 157d3c7b9a0SKenneth D. Merry /* Clear any pending interrupts */ 158d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 159d3c7b9a0SKenneth D. Merry 160757ff642SScott Long /* 161757ff642SScott Long * Force NO_SLEEP for threads prohibited to sleep 162be4aa869SKenneth D. Merry * e.a Thread from interrupt handler are prohibited to sleep. 163be4aa869SKenneth D. Merry */ 164f9379dc4SJohn Baldwin if (curthread->td_no_sleeping != 0) 165be4aa869SKenneth D. Merry sleep_flag = NO_SLEEP; 166be4aa869SKenneth D. Merry 167757ff642SScott Long mps_dprint(sc, MPS_INIT, "sequence start, sleep_flag= %d\n", sleep_flag); 168757ff642SScott Long 169d3c7b9a0SKenneth D. Merry /* Push the magic sequence */ 170d3c7b9a0SKenneth D. Merry error = ETIMEDOUT; 171d3c7b9a0SKenneth D. Merry while (tries++ < 20) { 172d3c7b9a0SKenneth D. Merry for (i = 0; i < sizeof(mpt2_reset_magic); i++) 173d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_WRITE_SEQUENCE_OFFSET, 174d3c7b9a0SKenneth D. Merry mpt2_reset_magic[i]); 175be4aa869SKenneth D. Merry /* wait 100 msec */ 176be4aa869SKenneth D. Merry if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP) 1779b91b192SKenneth D. Merry msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0, 1789b91b192SKenneth D. Merry "mpsdiag", hz/10); 179be4aa869SKenneth D. Merry else if (sleep_flag == CAN_SLEEP) 180be4aa869SKenneth D. Merry pause("mpsdiag", hz/10); 181be4aa869SKenneth D. Merry else 182d3c7b9a0SKenneth D. Merry DELAY(100 * 1000); 183d3c7b9a0SKenneth D. Merry 184d3c7b9a0SKenneth D. Merry reg = mps_regread(sc, MPI2_HOST_DIAGNOSTIC_OFFSET); 185d3c7b9a0SKenneth D. Merry if (reg & MPI2_DIAG_DIAG_WRITE_ENABLE) { 186d3c7b9a0SKenneth D. Merry error = 0; 187d3c7b9a0SKenneth D. Merry break; 188d3c7b9a0SKenneth D. Merry } 189d3c7b9a0SKenneth D. Merry } 190757ff642SScott Long if (error) { 191757ff642SScott Long mps_dprint(sc, MPS_INIT, "sequence failed, error=%d, exit\n", 192757ff642SScott Long error); 193d3c7b9a0SKenneth D. Merry return (error); 194757ff642SScott Long } 195d3c7b9a0SKenneth D. Merry 196d3c7b9a0SKenneth D. Merry /* Send the actual reset. XXX need to refresh the reg? */ 197757ff642SScott Long reg |= MPI2_DIAG_RESET_ADAPTER; 198757ff642SScott Long mps_dprint(sc, MPS_INIT, "sequence success, sending reset, reg= 0x%x\n", 199757ff642SScott Long reg); 200757ff642SScott Long mps_regwrite(sc, MPI2_HOST_DIAGNOSTIC_OFFSET, reg); 201d3c7b9a0SKenneth D. Merry 202d3c7b9a0SKenneth D. Merry /* Wait up to 300 seconds in 50ms intervals */ 203d3c7b9a0SKenneth D. Merry error = ETIMEDOUT; 2047571e7f6SSteven Hartland for (i = 0; i < 6000; i++) { 2057571e7f6SSteven Hartland /* 2067571e7f6SSteven Hartland * Wait 50 msec. If this is the first time through, wait 256 2077571e7f6SSteven Hartland * msec to satisfy Diag Reset timing requirements. 2087571e7f6SSteven Hartland */ 2097571e7f6SSteven Hartland if (first_wait_done) { 210be4aa869SKenneth D. Merry if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP) 2119b91b192SKenneth D. Merry msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0, 2129b91b192SKenneth D. Merry "mpsdiag", hz/20); 213be4aa869SKenneth D. Merry else if (sleep_flag == CAN_SLEEP) 214be4aa869SKenneth D. Merry pause("mpsdiag", hz/20); 215be4aa869SKenneth D. Merry else 216be4aa869SKenneth D. Merry DELAY(50 * 1000); 2177571e7f6SSteven Hartland } else { 2187571e7f6SSteven Hartland DELAY(256 * 1000); 2197571e7f6SSteven Hartland first_wait_done = TRUE; 2207571e7f6SSteven Hartland } 2217571e7f6SSteven Hartland /* 2227571e7f6SSteven Hartland * Check for the RESET_ADAPTER bit to be cleared first, then 2237571e7f6SSteven Hartland * wait for the RESET state to be cleared, which takes a little 2247571e7f6SSteven Hartland * longer. 2257571e7f6SSteven Hartland */ 2267571e7f6SSteven Hartland reg = mps_regread(sc, MPI2_HOST_DIAGNOSTIC_OFFSET); 2277571e7f6SSteven Hartland if (reg & MPI2_DIAG_RESET_ADAPTER) { 2287571e7f6SSteven Hartland continue; 2297571e7f6SSteven Hartland } 230d3c7b9a0SKenneth D. Merry reg = mps_regread(sc, MPI2_DOORBELL_OFFSET); 231d3c7b9a0SKenneth D. Merry if ((reg & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_RESET) { 232d3c7b9a0SKenneth D. Merry error = 0; 233d3c7b9a0SKenneth D. Merry break; 234d3c7b9a0SKenneth D. Merry } 235d3c7b9a0SKenneth D. Merry } 236757ff642SScott Long if (error) { 237757ff642SScott Long mps_dprint(sc, MPS_INIT, "reset failed, error= %d, exit\n", 238757ff642SScott Long error); 239d3c7b9a0SKenneth D. Merry return (error); 240757ff642SScott Long } 241d3c7b9a0SKenneth D. Merry 242d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_WRITE_SEQUENCE_OFFSET, 0x0); 243757ff642SScott Long mps_dprint(sc, MPS_INIT, "diag reset success, exit\n"); 244d3c7b9a0SKenneth D. Merry 245d3c7b9a0SKenneth D. Merry return (0); 246d3c7b9a0SKenneth D. Merry } 247d3c7b9a0SKenneth D. Merry 248d3c7b9a0SKenneth D. Merry static int 249be4aa869SKenneth D. Merry mps_message_unit_reset(struct mps_softc *sc, int sleep_flag) 250d3c7b9a0SKenneth D. Merry { 251757ff642SScott Long int error; 252d3c7b9a0SKenneth D. Merry 2531610f95cSScott Long MPS_FUNCTRACE(sc); 254d3c7b9a0SKenneth D. Merry 255757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 256757ff642SScott Long 257757ff642SScott Long error = 0; 258d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_DOORBELL_OFFSET, 259d3c7b9a0SKenneth D. Merry MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET << 260d3c7b9a0SKenneth D. Merry MPI2_DOORBELL_FUNCTION_SHIFT); 261be4aa869SKenneth D. Merry 262be4aa869SKenneth D. Merry if (mps_wait_db_ack(sc, 5, sleep_flag) != 0) { 263757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 264757ff642SScott Long "Doorbell handshake failed\n"); 265757ff642SScott Long error = ETIMEDOUT; 266be4aa869SKenneth D. Merry } 267d3c7b9a0SKenneth D. Merry 268757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 269757ff642SScott Long return (error); 270d3c7b9a0SKenneth D. Merry } 271d3c7b9a0SKenneth D. Merry 272d3c7b9a0SKenneth D. Merry static int 273d3c7b9a0SKenneth D. Merry mps_transition_ready(struct mps_softc *sc) 274d3c7b9a0SKenneth D. Merry { 275d3c7b9a0SKenneth D. Merry uint32_t reg, state; 276d3c7b9a0SKenneth D. Merry int error, tries = 0; 277be4aa869SKenneth D. Merry int sleep_flags; 278d3c7b9a0SKenneth D. Merry 2791610f95cSScott Long MPS_FUNCTRACE(sc); 280be4aa869SKenneth D. Merry /* If we are in attach call, do not sleep */ 281be4aa869SKenneth D. Merry sleep_flags = (sc->mps_flags & MPS_FLAGS_ATTACH_DONE) 282be4aa869SKenneth D. Merry ? CAN_SLEEP:NO_SLEEP; 283d3c7b9a0SKenneth D. Merry error = 0; 284757ff642SScott Long 285757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered, sleep_flags= %d\n", 286757ff642SScott Long __func__, sleep_flags); 287757ff642SScott Long 2887571e7f6SSteven Hartland while (tries++ < 1200) { 289d3c7b9a0SKenneth D. Merry reg = mps_regread(sc, MPI2_DOORBELL_OFFSET); 2901610f95cSScott Long mps_dprint(sc, MPS_INIT, " Doorbell= 0x%x\n", reg); 291d3c7b9a0SKenneth D. Merry 292d3c7b9a0SKenneth D. Merry /* 293d3c7b9a0SKenneth D. Merry * Ensure the IOC is ready to talk. If it's not, try 294d3c7b9a0SKenneth D. Merry * resetting it. 295d3c7b9a0SKenneth D. Merry */ 296d3c7b9a0SKenneth D. Merry if (reg & MPI2_DOORBELL_USED) { 297757ff642SScott Long mps_dprint(sc, MPS_INIT, " Not ready, sending diag " 298757ff642SScott Long "reset\n"); 299be4aa869SKenneth D. Merry mps_diag_reset(sc, sleep_flags); 300d3c7b9a0SKenneth D. Merry DELAY(50000); 301d3c7b9a0SKenneth D. Merry continue; 302d3c7b9a0SKenneth D. Merry } 303d3c7b9a0SKenneth D. Merry 304d3c7b9a0SKenneth D. Merry /* Is the adapter owned by another peer? */ 305d3c7b9a0SKenneth D. Merry if ((reg & MPI2_DOORBELL_WHO_INIT_MASK) == 306d3c7b9a0SKenneth D. Merry (MPI2_WHOINIT_PCI_PEER << MPI2_DOORBELL_WHO_INIT_SHIFT)) { 307757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "IOC is under the " 308757ff642SScott Long "control of another peer host, aborting " 309757ff642SScott Long "initialization.\n"); 310757ff642SScott Long error = ENXIO; 311757ff642SScott Long break; 312d3c7b9a0SKenneth D. Merry } 313d3c7b9a0SKenneth D. Merry 314d3c7b9a0SKenneth D. Merry state = reg & MPI2_IOC_STATE_MASK; 315d3c7b9a0SKenneth D. Merry if (state == MPI2_IOC_STATE_READY) { 316d3c7b9a0SKenneth D. Merry /* Ready to go! */ 317d3c7b9a0SKenneth D. Merry error = 0; 318d3c7b9a0SKenneth D. Merry break; 319d3c7b9a0SKenneth D. Merry } else if (state == MPI2_IOC_STATE_FAULT) { 320757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "IOC in fault " 321757ff642SScott Long "state 0x%x, resetting\n", 322d3c7b9a0SKenneth D. Merry state & MPI2_DOORBELL_FAULT_CODE_MASK); 323be4aa869SKenneth D. Merry mps_diag_reset(sc, sleep_flags); 324d3c7b9a0SKenneth D. Merry } else if (state == MPI2_IOC_STATE_OPERATIONAL) { 325d3c7b9a0SKenneth D. Merry /* Need to take ownership */ 326be4aa869SKenneth D. Merry mps_message_unit_reset(sc, sleep_flags); 327d3c7b9a0SKenneth D. Merry } else if (state == MPI2_IOC_STATE_RESET) { 328d3c7b9a0SKenneth D. Merry /* Wait a bit, IOC might be in transition */ 329757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 330d3c7b9a0SKenneth D. Merry "IOC in unexpected reset state\n"); 331d3c7b9a0SKenneth D. Merry } else { 332757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 333d3c7b9a0SKenneth D. Merry "IOC in unknown state 0x%x\n", state); 334d3c7b9a0SKenneth D. Merry error = EINVAL; 335d3c7b9a0SKenneth D. Merry break; 336d3c7b9a0SKenneth D. Merry } 337d3c7b9a0SKenneth D. Merry 338d3c7b9a0SKenneth D. Merry /* Wait 50ms for things to settle down. */ 339d3c7b9a0SKenneth D. Merry DELAY(50000); 340d3c7b9a0SKenneth D. Merry } 341d3c7b9a0SKenneth D. Merry 342d3c7b9a0SKenneth D. Merry if (error) 343757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 344757ff642SScott Long "Cannot transition IOC to ready\n"); 345757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 346d3c7b9a0SKenneth D. Merry 347d3c7b9a0SKenneth D. Merry return (error); 348d3c7b9a0SKenneth D. Merry } 349d3c7b9a0SKenneth D. Merry 350d3c7b9a0SKenneth D. Merry static int 351d3c7b9a0SKenneth D. Merry mps_transition_operational(struct mps_softc *sc) 352d3c7b9a0SKenneth D. Merry { 353d3c7b9a0SKenneth D. Merry uint32_t reg, state; 354d3c7b9a0SKenneth D. Merry int error; 355d3c7b9a0SKenneth D. Merry 3561610f95cSScott Long MPS_FUNCTRACE(sc); 357d3c7b9a0SKenneth D. Merry 358d3c7b9a0SKenneth D. Merry error = 0; 359d3c7b9a0SKenneth D. Merry reg = mps_regread(sc, MPI2_DOORBELL_OFFSET); 360757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered, Doorbell= 0x%x\n", __func__, reg); 361d3c7b9a0SKenneth D. Merry 362d3c7b9a0SKenneth D. Merry state = reg & MPI2_IOC_STATE_MASK; 363d3c7b9a0SKenneth D. Merry if (state != MPI2_IOC_STATE_READY) { 364757ff642SScott Long mps_dprint(sc, MPS_INIT, "IOC not ready\n"); 365d043c564SKenneth D. Merry if ((error = mps_transition_ready(sc)) != 0) { 366757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 367757ff642SScott Long "failed to transition ready, exit\n"); 368d3c7b9a0SKenneth D. Merry return (error); 369d3c7b9a0SKenneth D. Merry } 370d043c564SKenneth D. Merry } 371d3c7b9a0SKenneth D. Merry 372d3c7b9a0SKenneth D. Merry error = mps_send_iocinit(sc); 373757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 374757ff642SScott Long 375d3c7b9a0SKenneth D. Merry return (error); 376d3c7b9a0SKenneth D. Merry } 377d3c7b9a0SKenneth D. Merry 3783c5ac992SScott Long static void 3793c5ac992SScott Long mps_resize_queues(struct mps_softc *sc) 3803c5ac992SScott Long { 3814f5d6573SAlexander Motin u_int reqcr, prireqcr, maxio, sges_per_frame; 3823c5ac992SScott Long 3833c5ac992SScott Long /* 3843c5ac992SScott Long * Size the queues. Since the reply queues always need one free 3853c5ac992SScott Long * entry, we'll deduct one reply message here. The LSI documents 3863c5ac992SScott Long * suggest instead to add a count to the request queue, but I think 3873c5ac992SScott Long * that it's better to deduct from reply queue. 3883c5ac992SScott Long */ 3893c5ac992SScott Long prireqcr = MAX(1, sc->max_prireqframes); 3903c5ac992SScott Long prireqcr = MIN(prireqcr, sc->facts->HighPriorityCredit); 3913c5ac992SScott Long 3923c5ac992SScott Long reqcr = MAX(2, sc->max_reqframes); 3933c5ac992SScott Long reqcr = MIN(reqcr, sc->facts->RequestCredit); 3943c5ac992SScott Long 3953c5ac992SScott Long sc->num_reqs = prireqcr + reqcr; 39662a09ee9SAlexander Motin sc->num_prireqs = prireqcr; 3973c5ac992SScott Long sc->num_replies = MIN(sc->max_replyframes + sc->max_evtframes, 3983c5ac992SScott Long sc->facts->MaxReplyDescriptorPostQueueDepth) - 1; 3993c5ac992SScott Long 4004f5d6573SAlexander Motin /* Store the request frame size in bytes rather than as 32bit words */ 4014f5d6573SAlexander Motin sc->reqframesz = sc->facts->IOCRequestFrameSize * 4; 4024f5d6573SAlexander Motin 4034f5d6573SAlexander Motin /* 4044f5d6573SAlexander Motin * Max IO Size is Page Size * the following: 4054f5d6573SAlexander Motin * ((SGEs per frame - 1 for chain element) * Max Chain Depth) 4064f5d6573SAlexander Motin * + 1 for no chain needed in last frame 4074f5d6573SAlexander Motin * 4084f5d6573SAlexander Motin * If user suggests a Max IO size to use, use the smaller of the 4094f5d6573SAlexander Motin * user's value and the calculated value as long as the user's 4104f5d6573SAlexander Motin * value is larger than 0. The user's value is in pages. 4114f5d6573SAlexander Motin */ 4124f5d6573SAlexander Motin sges_per_frame = sc->reqframesz / sizeof(MPI2_SGE_SIMPLE64) - 1; 4134f5d6573SAlexander Motin maxio = (sges_per_frame * sc->facts->MaxChainDepth + 1) * PAGE_SIZE; 4144f5d6573SAlexander Motin 4154f5d6573SAlexander Motin /* 4164f5d6573SAlexander Motin * If I/O size limitation requested, then use it and pass up to CAM. 417cd853791SKonstantin Belousov * If not, use maxphys as an optimization hint, but report HW limit. 4184f5d6573SAlexander Motin */ 4194f5d6573SAlexander Motin if (sc->max_io_pages > 0) { 4204f5d6573SAlexander Motin maxio = min(maxio, sc->max_io_pages * PAGE_SIZE); 4214f5d6573SAlexander Motin sc->maxio = maxio; 4224f5d6573SAlexander Motin } else { 4234f5d6573SAlexander Motin sc->maxio = maxio; 424cd853791SKonstantin Belousov maxio = min(maxio, maxphys); 4254f5d6573SAlexander Motin } 4264f5d6573SAlexander Motin 4274f5d6573SAlexander Motin sc->num_chains = (maxio / PAGE_SIZE + sges_per_frame - 2) / 4284f5d6573SAlexander Motin sges_per_frame * reqcr; 4294f5d6573SAlexander Motin if (sc->max_chains > 0 && sc->max_chains < sc->num_chains) 4304f5d6573SAlexander Motin sc->num_chains = sc->max_chains; 4314f5d6573SAlexander Motin 4323c5ac992SScott Long /* 4333c5ac992SScott Long * Figure out the number of MSIx-based queues. If the firmware or 4343c5ac992SScott Long * user has done something crazy and not allowed enough credit for 4353c5ac992SScott Long * the queues to be useful then don't enable multi-queue. 4363c5ac992SScott Long */ 4373c5ac992SScott Long if (sc->facts->MaxMSIxVectors < 2) 4383c5ac992SScott Long sc->msi_msgs = 1; 4393c5ac992SScott Long 4403c5ac992SScott Long if (sc->msi_msgs > 1) { 4413c5ac992SScott Long sc->msi_msgs = MIN(sc->msi_msgs, mp_ncpus); 4423c5ac992SScott Long sc->msi_msgs = MIN(sc->msi_msgs, sc->facts->MaxMSIxVectors); 4433c5ac992SScott Long if (sc->num_reqs / sc->msi_msgs < 2) 4443c5ac992SScott Long sc->msi_msgs = 1; 4453c5ac992SScott Long } 4463c5ac992SScott Long 4473c5ac992SScott Long mps_dprint(sc, MPS_INIT, "Sized queues to q=%d reqs=%d replies=%d\n", 4483c5ac992SScott Long sc->msi_msgs, sc->num_reqs, sc->num_replies); 4493c5ac992SScott Long } 4503c5ac992SScott Long 451d043c564SKenneth D. Merry /* 4529b91b192SKenneth D. Merry * This is called during attach and when re-initializing due to a Diag Reset. 4539b91b192SKenneth D. Merry * IOC Facts is used to allocate many of the structures needed by the driver. 4549b91b192SKenneth D. Merry * If called from attach, de-allocation is not required because the driver has 4559b91b192SKenneth D. Merry * not allocated any structures yet, but if called from a Diag Reset, previously 4569b91b192SKenneth D. Merry * allocated structures based on IOC Facts will need to be freed and re- 4579b91b192SKenneth D. Merry * allocated bases on the latest IOC Facts. 4589b91b192SKenneth D. Merry */ 4599b91b192SKenneth D. Merry static int 4609b91b192SKenneth D. Merry mps_iocfacts_allocate(struct mps_softc *sc, uint8_t attaching) 4619b91b192SKenneth D. Merry { 462b7f77127SScott Long int error; 4639b91b192SKenneth D. Merry Mpi2IOCFactsReply_t saved_facts; 4649b91b192SKenneth D. Merry uint8_t saved_mode, reallocating; 4659b91b192SKenneth D. Merry 466757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_TRACE, "%s entered\n", __func__); 4679b91b192SKenneth D. Merry 4689b91b192SKenneth D. Merry /* Save old IOC Facts and then only reallocate if Facts have changed */ 4699b91b192SKenneth D. Merry if (!attaching) { 4709b91b192SKenneth D. Merry bcopy(sc->facts, &saved_facts, sizeof(MPI2_IOC_FACTS_REPLY)); 4719b91b192SKenneth D. Merry } 4729b91b192SKenneth D. Merry 4739b91b192SKenneth D. Merry /* 4749b91b192SKenneth D. Merry * Get IOC Facts. In all cases throughout this function, panic if doing 4759b91b192SKenneth D. Merry * a re-initialization and only return the error if attaching so the OS 4769b91b192SKenneth D. Merry * can handle it. 4779b91b192SKenneth D. Merry */ 4789b91b192SKenneth D. Merry if ((error = mps_get_iocfacts(sc, sc->facts)) != 0) { 4799b91b192SKenneth D. Merry if (attaching) { 480757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Failed to get " 481757ff642SScott Long "IOC Facts with error %d, exit\n", error); 4829b91b192SKenneth D. Merry return (error); 4839b91b192SKenneth D. Merry } else { 4849b91b192SKenneth D. Merry panic("%s failed to get IOC Facts with error %d\n", 4859b91b192SKenneth D. Merry __func__, error); 4869b91b192SKenneth D. Merry } 4879b91b192SKenneth D. Merry } 4889b91b192SKenneth D. Merry 489055e2653SScott Long MPS_DPRINT_PAGE(sc, MPS_XINFO, iocfacts, sc->facts); 4909b91b192SKenneth D. Merry 4919b91b192SKenneth D. Merry snprintf(sc->fw_version, sizeof(sc->fw_version), 4929b91b192SKenneth D. Merry "%02d.%02d.%02d.%02d", 4939b91b192SKenneth D. Merry sc->facts->FWVersion.Struct.Major, 4949b91b192SKenneth D. Merry sc->facts->FWVersion.Struct.Minor, 4959b91b192SKenneth D. Merry sc->facts->FWVersion.Struct.Unit, 4969b91b192SKenneth D. Merry sc->facts->FWVersion.Struct.Dev); 4979b91b192SKenneth D. Merry 49869e85eb8SScott Long snprintf(sc->msg_version, sizeof(sc->msg_version), "%d.%d", 49969e85eb8SScott Long (sc->facts->MsgVersion & MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK) >> 50069e85eb8SScott Long MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT, 50169e85eb8SScott Long (sc->facts->MsgVersion & MPI2_IOCFACTS_MSGVERSION_MINOR_MASK) >> 50269e85eb8SScott Long MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT); 50369e85eb8SScott Long 504757ff642SScott Long mps_dprint(sc, MPS_INFO, "Firmware: %s, Driver: %s\n", sc->fw_version, 5059b91b192SKenneth D. Merry MPS_DRIVER_VERSION); 506757ff642SScott Long mps_dprint(sc, MPS_INFO, "IOCCapabilities: %b\n", 507757ff642SScott Long sc->facts->IOCCapabilities, 5089b91b192SKenneth D. Merry "\20" "\3ScsiTaskFull" "\4DiagTrace" "\5SnapBuf" "\6ExtBuf" 5099b91b192SKenneth D. Merry "\7EEDP" "\10BiDirTarg" "\11Multicast" "\14TransRetry" "\15IR" 5109b91b192SKenneth D. Merry "\16EventReplay" "\17RaidAccel" "\20MSIXIndex" "\21HostDisc"); 5119b91b192SKenneth D. Merry 5129b91b192SKenneth D. Merry /* 5139b91b192SKenneth D. Merry * If the chip doesn't support event replay then a hard reset will be 5149b91b192SKenneth D. Merry * required to trigger a full discovery. Do the reset here then 5159b91b192SKenneth D. Merry * retransition to Ready. A hard reset might have already been done, 5169b91b192SKenneth D. Merry * but it doesn't hurt to do it again. Only do this if attaching, not 5179b91b192SKenneth D. Merry * for a Diag Reset. 5189b91b192SKenneth D. Merry */ 519757ff642SScott Long if (attaching && ((sc->facts->IOCCapabilities & 520757ff642SScott Long MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY) == 0)) { 521757ff642SScott Long mps_dprint(sc, MPS_INIT, "No event replay, reseting\n"); 5229b91b192SKenneth D. Merry mps_diag_reset(sc, NO_SLEEP); 5239b91b192SKenneth D. Merry if ((error = mps_transition_ready(sc)) != 0) { 524757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Failed to " 525757ff642SScott Long "transition to ready with error %d, exit\n", 526757ff642SScott Long error); 5279b91b192SKenneth D. Merry return (error); 5289b91b192SKenneth D. Merry } 5299b91b192SKenneth D. Merry } 5309b91b192SKenneth D. Merry 5319b91b192SKenneth D. Merry /* 5329b91b192SKenneth D. Merry * Set flag if IR Firmware is loaded. If the RAID Capability has 5339b91b192SKenneth D. Merry * changed from the previous IOC Facts, log a warning, but only if 5349b91b192SKenneth D. Merry * checking this after a Diag Reset and not during attach. 5359b91b192SKenneth D. Merry */ 5369b91b192SKenneth D. Merry saved_mode = sc->ir_firmware; 5379b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & 5389b91b192SKenneth D. Merry MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) 5399b91b192SKenneth D. Merry sc->ir_firmware = 1; 5409b91b192SKenneth D. Merry if (!attaching) { 5419b91b192SKenneth D. Merry if (sc->ir_firmware != saved_mode) { 542757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "new IR/IT mode " 543757ff642SScott Long "in IOC Facts does not match previous mode\n"); 5449b91b192SKenneth D. Merry } 5459b91b192SKenneth D. Merry } 5469b91b192SKenneth D. Merry 5479b91b192SKenneth D. Merry /* Only deallocate and reallocate if relevant IOC Facts have changed */ 5489b91b192SKenneth D. Merry reallocating = FALSE; 5496d4ffcb4SKenneth D. Merry sc->mps_flags &= ~MPS_FLAGS_REALLOCATED; 5506d4ffcb4SKenneth D. Merry 5519b91b192SKenneth D. Merry if ((!attaching) && 5529b91b192SKenneth D. Merry ((saved_facts.MsgVersion != sc->facts->MsgVersion) || 5539b91b192SKenneth D. Merry (saved_facts.HeaderVersion != sc->facts->HeaderVersion) || 5549b91b192SKenneth D. Merry (saved_facts.MaxChainDepth != sc->facts->MaxChainDepth) || 5559b91b192SKenneth D. Merry (saved_facts.RequestCredit != sc->facts->RequestCredit) || 5569b91b192SKenneth D. Merry (saved_facts.ProductID != sc->facts->ProductID) || 5579b91b192SKenneth D. Merry (saved_facts.IOCCapabilities != sc->facts->IOCCapabilities) || 5589b91b192SKenneth D. Merry (saved_facts.IOCRequestFrameSize != 5599b91b192SKenneth D. Merry sc->facts->IOCRequestFrameSize) || 5609b91b192SKenneth D. Merry (saved_facts.MaxTargets != sc->facts->MaxTargets) || 5619b91b192SKenneth D. Merry (saved_facts.MaxSasExpanders != sc->facts->MaxSasExpanders) || 5629b91b192SKenneth D. Merry (saved_facts.MaxEnclosures != sc->facts->MaxEnclosures) || 5639b91b192SKenneth D. Merry (saved_facts.HighPriorityCredit != sc->facts->HighPriorityCredit) || 5649b91b192SKenneth D. Merry (saved_facts.MaxReplyDescriptorPostQueueDepth != 5659b91b192SKenneth D. Merry sc->facts->MaxReplyDescriptorPostQueueDepth) || 5669b91b192SKenneth D. Merry (saved_facts.ReplyFrameSize != sc->facts->ReplyFrameSize) || 5679b91b192SKenneth D. Merry (saved_facts.MaxVolumes != sc->facts->MaxVolumes) || 5689b91b192SKenneth D. Merry (saved_facts.MaxPersistentEntries != 5699b91b192SKenneth D. Merry sc->facts->MaxPersistentEntries))) { 5709b91b192SKenneth D. Merry reallocating = TRUE; 5716d4ffcb4SKenneth D. Merry 5726d4ffcb4SKenneth D. Merry /* Record that we reallocated everything */ 5736d4ffcb4SKenneth D. Merry sc->mps_flags |= MPS_FLAGS_REALLOCATED; 5749b91b192SKenneth D. Merry } 5759b91b192SKenneth D. Merry 5769b91b192SKenneth D. Merry /* 5779b91b192SKenneth D. Merry * Some things should be done if attaching or re-allocating after a Diag 5789b91b192SKenneth D. Merry * Reset, but are not needed after a Diag Reset if the FW has not 5799b91b192SKenneth D. Merry * changed. 5809b91b192SKenneth D. Merry */ 5819b91b192SKenneth D. Merry if (attaching || reallocating) { 5829b91b192SKenneth D. Merry /* 5839b91b192SKenneth D. Merry * Check if controller supports FW diag buffers and set flag to 5849b91b192SKenneth D. Merry * enable each type. 5859b91b192SKenneth D. Merry */ 5869b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & 5879b91b192SKenneth D. Merry MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) 5889b91b192SKenneth D. Merry sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_TRACE]. 5899b91b192SKenneth D. Merry enabled = TRUE; 5909b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & 5919b91b192SKenneth D. Merry MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) 5929b91b192SKenneth D. Merry sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_SNAPSHOT]. 5939b91b192SKenneth D. Merry enabled = TRUE; 5949b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & 5959b91b192SKenneth D. Merry MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) 5969b91b192SKenneth D. Merry sc->fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_EXTENDED]. 5979b91b192SKenneth D. Merry enabled = TRUE; 5989b91b192SKenneth D. Merry 5999b91b192SKenneth D. Merry /* 6009b91b192SKenneth D. Merry * Set flag if EEDP is supported and if TLR is supported. 6019b91b192SKenneth D. Merry */ 6029b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) 6039b91b192SKenneth D. Merry sc->eedp_enabled = TRUE; 6049b91b192SKenneth D. Merry if (sc->facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) 6059b91b192SKenneth D. Merry sc->control_TLR = TRUE; 6069b91b192SKenneth D. Merry 6073c5ac992SScott Long mps_resize_queues(sc); 6089b91b192SKenneth D. Merry 6099b91b192SKenneth D. Merry /* 6109b91b192SKenneth D. Merry * Initialize all Tail Queues 6119b91b192SKenneth D. Merry */ 6129b91b192SKenneth D. Merry TAILQ_INIT(&sc->req_list); 6139b91b192SKenneth D. Merry TAILQ_INIT(&sc->high_priority_req_list); 6149b91b192SKenneth D. Merry TAILQ_INIT(&sc->chain_list); 6159b91b192SKenneth D. Merry TAILQ_INIT(&sc->tm_list); 6169b91b192SKenneth D. Merry } 6179b91b192SKenneth D. Merry 6189b91b192SKenneth D. Merry /* 6199b91b192SKenneth D. Merry * If doing a Diag Reset and the FW is significantly different 6209b91b192SKenneth D. Merry * (reallocating will be set above in IOC Facts comparison), then all 6219b91b192SKenneth D. Merry * buffers based on the IOC Facts will need to be freed before they are 6229b91b192SKenneth D. Merry * reallocated. 6239b91b192SKenneth D. Merry */ 6249b91b192SKenneth D. Merry if (reallocating) { 6259b91b192SKenneth D. Merry mps_iocfacts_free(sc); 626635e58c7SStephen McConnell mpssas_realloc_targets(sc, saved_facts.MaxTargets + 627635e58c7SStephen McConnell saved_facts.MaxVolumes); 6289b91b192SKenneth D. Merry } 6299b91b192SKenneth D. Merry 6309b91b192SKenneth D. Merry /* 6319b91b192SKenneth D. Merry * Any deallocation has been completed. Now start reallocating 6329b91b192SKenneth D. Merry * if needed. Will only need to reallocate if attaching or if the new 6339b91b192SKenneth D. Merry * IOC Facts are different from the previous IOC Facts after a Diag 6349b91b192SKenneth D. Merry * Reset. Targets have already been allocated above if needed. 6359b91b192SKenneth D. Merry */ 6361415db6cSScott Long error = 0; 6371415db6cSScott Long while (attaching || reallocating) { 6381415db6cSScott Long if ((error = mps_alloc_hw_queues(sc)) != 0) 6391415db6cSScott Long break; 6401415db6cSScott Long if ((error = mps_alloc_replies(sc)) != 0) 6411415db6cSScott Long break; 6421415db6cSScott Long if ((error = mps_alloc_requests(sc)) != 0) 6431415db6cSScott Long break; 6441415db6cSScott Long if ((error = mps_alloc_queues(sc)) != 0) 6451415db6cSScott Long break; 6461415db6cSScott Long 6471415db6cSScott Long break; 6481415db6cSScott Long } 6491415db6cSScott Long if (error) { 650757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, 6511415db6cSScott Long "Failed to alloc queues with error %d\n", error); 6529b91b192SKenneth D. Merry mps_free(sc); 6539b91b192SKenneth D. Merry return (error); 6549b91b192SKenneth D. Merry } 6559b91b192SKenneth D. Merry 6569b91b192SKenneth D. Merry /* Always initialize the queues */ 6579b91b192SKenneth D. Merry bzero(sc->free_queue, sc->fqdepth * 4); 6589b91b192SKenneth D. Merry mps_init_queues(sc); 6599b91b192SKenneth D. Merry 6609b91b192SKenneth D. Merry /* 6619b91b192SKenneth D. Merry * Always get the chip out of the reset state, but only panic if not 6629b91b192SKenneth D. Merry * attaching. If attaching and there is an error, that is handled by 6639b91b192SKenneth D. Merry * the OS. 6649b91b192SKenneth D. Merry */ 6659b91b192SKenneth D. Merry error = mps_transition_operational(sc); 6669b91b192SKenneth D. Merry if (error != 0) { 667757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Failed to " 668757ff642SScott Long "transition to operational with error %d\n", error); 6699b91b192SKenneth D. Merry mps_free(sc); 6709b91b192SKenneth D. Merry return (error); 6719b91b192SKenneth D. Merry } 6729b91b192SKenneth D. Merry 6739b91b192SKenneth D. Merry /* 6749b91b192SKenneth D. Merry * Finish the queue initialization. 6759b91b192SKenneth D. Merry * These are set here instead of in mps_init_queues() because the 6769b91b192SKenneth D. Merry * IOC resets these values during the state transition in 6779b91b192SKenneth D. Merry * mps_transition_operational(). The free index is set to 1 6789b91b192SKenneth D. Merry * because the corresponding index in the IOC is set to 0, and the 6799b91b192SKenneth D. Merry * IOC treats the queues as full if both are set to the same value. 6809b91b192SKenneth D. Merry * Hence the reason that the queue can't hold all of the possible 6819b91b192SKenneth D. Merry * replies. 6829b91b192SKenneth D. Merry */ 6839b91b192SKenneth D. Merry sc->replypostindex = 0; 6849b91b192SKenneth D. Merry mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex); 6859b91b192SKenneth D. Merry mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, 0); 6869b91b192SKenneth D. Merry 6879b91b192SKenneth D. Merry /* 6889b91b192SKenneth D. Merry * Attach the subsystems so they can prepare their event masks. 6891415db6cSScott Long * XXX Should be dynamic so that IM/IR and user modules can attach 6909b91b192SKenneth D. Merry */ 6911415db6cSScott Long error = 0; 6921415db6cSScott Long while (attaching) { 693757ff642SScott Long mps_dprint(sc, MPS_INIT, "Attaching subsystems\n"); 6941415db6cSScott Long if ((error = mps_attach_log(sc)) != 0) 6951415db6cSScott Long break; 6961415db6cSScott Long if ((error = mps_attach_sas(sc)) != 0) 6971415db6cSScott Long break; 6981415db6cSScott Long if ((error = mps_attach_user(sc)) != 0) 6991415db6cSScott Long break; 7001415db6cSScott Long break; 7011415db6cSScott Long } 7021415db6cSScott Long if (error) { 7031415db6cSScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Failed to attach all " 7041415db6cSScott Long "subsystems: error %d\n", error); 7059b91b192SKenneth D. Merry mps_free(sc); 7069b91b192SKenneth D. Merry return (error); 7079b91b192SKenneth D. Merry } 7089b91b192SKenneth D. Merry 70910695417SScott Long /* 71010695417SScott Long * XXX If the number of MSI-X vectors changes during re-init, this 71110695417SScott Long * won't see it and adjust. 71210695417SScott Long */ 71310695417SScott Long if (attaching && (error = mps_pci_setup_interrupts(sc)) != 0) { 714757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Failed to setup " 715757ff642SScott Long "interrupts\n"); 7169b91b192SKenneth D. Merry mps_free(sc); 7179b91b192SKenneth D. Merry return (error); 7189b91b192SKenneth D. Merry } 7199b91b192SKenneth D. Merry 7209b91b192SKenneth D. Merry /* 7219b91b192SKenneth D. Merry * Set flag if this is a WD controller. This shouldn't ever change, but 7229b91b192SKenneth D. Merry * reset it after a Diag Reset, just in case. 7239b91b192SKenneth D. Merry */ 7249b91b192SKenneth D. Merry sc->WD_available = FALSE; 7259b91b192SKenneth D. Merry if (pci_get_device(sc->mps_dev) == MPI2_MFGPAGE_DEVID_SSS6200) 7269b91b192SKenneth D. Merry sc->WD_available = TRUE; 7279b91b192SKenneth D. Merry 7289b91b192SKenneth D. Merry return (error); 7299b91b192SKenneth D. Merry } 7309b91b192SKenneth D. Merry 7319b91b192SKenneth D. Merry /* 7329b91b192SKenneth D. Merry * This is called if memory is being free (during detach for example) and when 7339b91b192SKenneth D. Merry * buffers need to be reallocated due to a Diag Reset. 7349b91b192SKenneth D. Merry */ 7359b91b192SKenneth D. Merry static void 7369b91b192SKenneth D. Merry mps_iocfacts_free(struct mps_softc *sc) 7379b91b192SKenneth D. Merry { 7389b91b192SKenneth D. Merry struct mps_command *cm; 7399b91b192SKenneth D. Merry int i; 7409b91b192SKenneth D. Merry 7419b91b192SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 7429b91b192SKenneth D. Merry 743e248a3d1SSteven Hartland if (sc->free_busaddr != 0) 7449b91b192SKenneth D. Merry bus_dmamap_unload(sc->queues_dmat, sc->queues_map); 745ec3dd426SAlexander Motin if (sc->free_queue != NULL) 746ec3dd426SAlexander Motin bus_dmamem_free(sc->queues_dmat, sc->free_queue, 7479b91b192SKenneth D. Merry sc->queues_map); 7489b91b192SKenneth D. Merry if (sc->queues_dmat != NULL) 7499b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->queues_dmat); 7509b91b192SKenneth D. Merry 751731308d0SAlexander Motin if (sc->chain_frames != NULL) { 7529b91b192SKenneth D. Merry bus_dmamap_unload(sc->chain_dmat, sc->chain_map); 7539b91b192SKenneth D. Merry bus_dmamem_free(sc->chain_dmat, sc->chain_frames, 7549b91b192SKenneth D. Merry sc->chain_map); 755731308d0SAlexander Motin } 7569b91b192SKenneth D. Merry if (sc->chain_dmat != NULL) 7579b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->chain_dmat); 7589b91b192SKenneth D. Merry 7599b91b192SKenneth D. Merry if (sc->sense_busaddr != 0) 7609b91b192SKenneth D. Merry bus_dmamap_unload(sc->sense_dmat, sc->sense_map); 7619b91b192SKenneth D. Merry if (sc->sense_frames != NULL) 7629b91b192SKenneth D. Merry bus_dmamem_free(sc->sense_dmat, sc->sense_frames, 7639b91b192SKenneth D. Merry sc->sense_map); 7649b91b192SKenneth D. Merry if (sc->sense_dmat != NULL) 7659b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->sense_dmat); 7669b91b192SKenneth D. Merry 7679b91b192SKenneth D. Merry if (sc->reply_busaddr != 0) 7689b91b192SKenneth D. Merry bus_dmamap_unload(sc->reply_dmat, sc->reply_map); 7699b91b192SKenneth D. Merry if (sc->reply_frames != NULL) 7709b91b192SKenneth D. Merry bus_dmamem_free(sc->reply_dmat, sc->reply_frames, 7719b91b192SKenneth D. Merry sc->reply_map); 7729b91b192SKenneth D. Merry if (sc->reply_dmat != NULL) 7739b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->reply_dmat); 7749b91b192SKenneth D. Merry 7759b91b192SKenneth D. Merry if (sc->req_busaddr != 0) 7769b91b192SKenneth D. Merry bus_dmamap_unload(sc->req_dmat, sc->req_map); 7779b91b192SKenneth D. Merry if (sc->req_frames != NULL) 7789b91b192SKenneth D. Merry bus_dmamem_free(sc->req_dmat, sc->req_frames, sc->req_map); 7799b91b192SKenneth D. Merry if (sc->req_dmat != NULL) 7809b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->req_dmat); 7819b91b192SKenneth D. Merry 7829b91b192SKenneth D. Merry if (sc->chains != NULL) 7839b91b192SKenneth D. Merry free(sc->chains, M_MPT2); 7849b91b192SKenneth D. Merry if (sc->commands != NULL) { 7859b91b192SKenneth D. Merry for (i = 1; i < sc->num_reqs; i++) { 7869b91b192SKenneth D. Merry cm = &sc->commands[i]; 7879b91b192SKenneth D. Merry bus_dmamap_destroy(sc->buffer_dmat, cm->cm_dmamap); 7889b91b192SKenneth D. Merry } 7899b91b192SKenneth D. Merry free(sc->commands, M_MPT2); 7909b91b192SKenneth D. Merry } 7919b91b192SKenneth D. Merry if (sc->buffer_dmat != NULL) 7929b91b192SKenneth D. Merry bus_dma_tag_destroy(sc->buffer_dmat); 793bec09074SScott Long 794bec09074SScott Long mps_pci_free_interrupts(sc); 795bec09074SScott Long free(sc->queues, M_MPT2); 796bec09074SScott Long sc->queues = NULL; 7979b91b192SKenneth D. Merry } 7989b91b192SKenneth D. Merry 7999b91b192SKenneth D. Merry /* 800d043c564SKenneth D. Merry * The terms diag reset and hard reset are used interchangeably in the MPI 801d043c564SKenneth D. Merry * docs to mean resetting the controller chip. In this code diag reset 802d043c564SKenneth D. Merry * cleans everything up, and the hard reset function just sends the reset 803d043c564SKenneth D. Merry * sequence to the chip. This should probably be refactored so that every 804d043c564SKenneth D. Merry * subsystem gets a reset notification of some sort, and can clean up 805d043c564SKenneth D. Merry * appropriately. 806d043c564SKenneth D. Merry */ 807d043c564SKenneth D. Merry int 808d043c564SKenneth D. Merry mps_reinit(struct mps_softc *sc) 809d043c564SKenneth D. Merry { 810d043c564SKenneth D. Merry int error; 8117571e7f6SSteven Hartland struct mpssas_softc *sassc; 8127571e7f6SSteven Hartland 8137571e7f6SSteven Hartland sassc = sc->sassc; 814d043c564SKenneth D. Merry 8151610f95cSScott Long MPS_FUNCTRACE(sc); 816d043c564SKenneth D. Merry 817d043c564SKenneth D. Merry mtx_assert(&sc->mps_mtx, MA_OWNED); 818d043c564SKenneth D. Merry 819757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_INFO, "Reinitializing controller\n"); 820d043c564SKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_DIAGRESET) { 821757ff642SScott Long mps_dprint(sc, MPS_INIT, "Reset already in progress\n"); 822d043c564SKenneth D. Merry return 0; 823d043c564SKenneth D. Merry } 824d043c564SKenneth D. Merry 825d043c564SKenneth D. Merry /* make sure the completion callbacks can recognize they're getting 826d043c564SKenneth D. Merry * a NULL cm_reply due to a reset. 827d043c564SKenneth D. Merry */ 828d043c564SKenneth D. Merry sc->mps_flags |= MPS_FLAGS_DIAGRESET; 829d043c564SKenneth D. Merry 8309b91b192SKenneth D. Merry /* 8319b91b192SKenneth D. Merry * Mask interrupts here. 8329b91b192SKenneth D. Merry */ 833757ff642SScott Long mps_dprint(sc, MPS_INIT, "masking interrupts and resetting\n"); 834d043c564SKenneth D. Merry mps_mask_intr(sc); 835d043c564SKenneth D. Merry 836be4aa869SKenneth D. Merry error = mps_diag_reset(sc, CAN_SLEEP); 837d043c564SKenneth D. Merry if (error != 0) { 8381610f95cSScott Long /* XXXSL No need to panic here */ 839d043c564SKenneth D. Merry panic("%s hard reset failed with error %d\n", 840d043c564SKenneth D. Merry __func__, error); 841d043c564SKenneth D. Merry } 842d043c564SKenneth D. Merry 843d043c564SKenneth D. Merry /* Restore the PCI state, including the MSI-X registers */ 844d043c564SKenneth D. Merry mps_pci_restore(sc); 845d043c564SKenneth D. Merry 846d043c564SKenneth D. Merry /* Give the I/O subsystem special priority to get itself prepared */ 847d043c564SKenneth D. Merry mpssas_handle_reinit(sc); 848d043c564SKenneth D. Merry 8499b91b192SKenneth D. Merry /* 8509b91b192SKenneth D. Merry * Get IOC Facts and allocate all structures based on this information. 8519b91b192SKenneth D. Merry * The attach function will also call mps_iocfacts_allocate at startup. 8529b91b192SKenneth D. Merry * If relevant values have changed in IOC Facts, this function will free 8539b91b192SKenneth D. Merry * all of the memory based on IOC Facts and reallocate that memory. 854d043c564SKenneth D. Merry */ 8559b91b192SKenneth D. Merry if ((error = mps_iocfacts_allocate(sc, FALSE)) != 0) { 8569b91b192SKenneth D. Merry panic("%s IOC Facts based allocation failed with error %d\n", 8579b91b192SKenneth D. Merry __func__, error); 8589b91b192SKenneth D. Merry } 859d043c564SKenneth D. Merry 8609b91b192SKenneth D. Merry /* 8619b91b192SKenneth D. Merry * Mapping structures will be re-allocated after getting IOC Page8, so 8629b91b192SKenneth D. Merry * free these structures here. 8639b91b192SKenneth D. Merry */ 8649b91b192SKenneth D. Merry mps_mapping_exit(sc); 865d043c564SKenneth D. Merry 8669b91b192SKenneth D. Merry /* 8679b91b192SKenneth D. Merry * The static page function currently read is IOC Page8. Others can be 8689b91b192SKenneth D. Merry * added in future. It's possible that the values in IOC Page8 have 8699b91b192SKenneth D. Merry * changed after a Diag Reset due to user modification, so always read 8709b91b192SKenneth D. Merry * these. Interrupts are masked, so unmask them before getting config 8719b91b192SKenneth D. Merry * pages. 8729b91b192SKenneth D. Merry */ 873d043c564SKenneth D. Merry mps_unmask_intr(sc); 8749b91b192SKenneth D. Merry sc->mps_flags &= ~MPS_FLAGS_DIAGRESET; 8759b91b192SKenneth D. Merry mps_base_static_config_pages(sc); 876d043c564SKenneth D. Merry 8779b91b192SKenneth D. Merry /* 8789b91b192SKenneth D. Merry * Some mapping info is based in IOC Page8 data, so re-initialize the 8799b91b192SKenneth D. Merry * mapping tables. 8809b91b192SKenneth D. Merry */ 8819b91b192SKenneth D. Merry mps_mapping_initialize(sc); 882d043c564SKenneth D. Merry 8839b91b192SKenneth D. Merry /* 8849b91b192SKenneth D. Merry * Restart will reload the event masks clobbered by the reset, and 885d043c564SKenneth D. Merry * then enable the port. 886d043c564SKenneth D. Merry */ 887d043c564SKenneth D. Merry mps_reregister_events(sc); 888d043c564SKenneth D. Merry 889d043c564SKenneth D. Merry /* the end of discovery will release the simq, so we're done. */ 890757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_XINFO, "Finished sc %p post %u free %u\n", 891757ff642SScott Long sc, sc->replypostindex, sc->replyfreeindex); 892d043c564SKenneth D. Merry 8937571e7f6SSteven Hartland mpssas_release_simq_reinit(sassc); 894757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 8957571e7f6SSteven Hartland 896d043c564SKenneth D. Merry return 0; 897d043c564SKenneth D. Merry } 898d043c564SKenneth D. Merry 899be4aa869SKenneth D. Merry /* Wait for the chip to ACK a word that we've put into its FIFO 900be4aa869SKenneth D. Merry * Wait for <timeout> seconds. In single loop wait for busy loop 901be4aa869SKenneth D. Merry * for 500 microseconds. 902be4aa869SKenneth D. Merry * Total is [ 0.5 * (2000 * <timeout>) ] in miliseconds. 903be4aa869SKenneth D. Merry * */ 904d3c7b9a0SKenneth D. Merry static int 905be4aa869SKenneth D. Merry mps_wait_db_ack(struct mps_softc *sc, int timeout, int sleep_flag) 906d3c7b9a0SKenneth D. Merry { 907d3c7b9a0SKenneth D. Merry 908be4aa869SKenneth D. Merry u32 cntdn, count; 909be4aa869SKenneth D. Merry u32 int_status; 910be4aa869SKenneth D. Merry u32 doorbell; 911be4aa869SKenneth D. Merry 912be4aa869SKenneth D. Merry count = 0; 913be4aa869SKenneth D. Merry cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; 914be4aa869SKenneth D. Merry do { 915be4aa869SKenneth D. Merry int_status = mps_regread(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET); 916be4aa869SKenneth D. Merry if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { 917757ff642SScott Long mps_dprint(sc, MPS_TRACE, 918453130d9SPedro F. Giffuni "%s: successful count(%d), timeout(%d)\n", 919be4aa869SKenneth D. Merry __func__, count, timeout); 920be4aa869SKenneth D. Merry return 0; 921be4aa869SKenneth D. Merry } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { 922be4aa869SKenneth D. Merry doorbell = mps_regread(sc, MPI2_DOORBELL_OFFSET); 923be4aa869SKenneth D. Merry if ((doorbell & MPI2_IOC_STATE_MASK) == 924be4aa869SKenneth D. Merry MPI2_IOC_STATE_FAULT) { 925be4aa869SKenneth D. Merry mps_dprint(sc, MPS_FAULT, 926be4aa869SKenneth D. Merry "fault_state(0x%04x)!\n", doorbell); 927be4aa869SKenneth D. Merry return (EFAULT); 928d3c7b9a0SKenneth D. Merry } 929be4aa869SKenneth D. Merry } else if (int_status == 0xFFFFFFFF) 930be4aa869SKenneth D. Merry goto out; 931be4aa869SKenneth D. Merry 932be4aa869SKenneth D. Merry /* If it can sleep, sleep for 1 milisecond, else busy loop for 933be4aa869SKenneth D. Merry * 0.5 milisecond */ 934be4aa869SKenneth D. Merry if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP) 935be4aa869SKenneth D. Merry msleep(&sc->msleep_fake_chan, &sc->mps_mtx, 0, 936be4aa869SKenneth D. Merry "mpsdba", hz/1000); 937be4aa869SKenneth D. Merry else if (sleep_flag == CAN_SLEEP) 938be4aa869SKenneth D. Merry pause("mpsdba", hz/1000); 939be4aa869SKenneth D. Merry else 940be4aa869SKenneth D. Merry DELAY(500); 941be4aa869SKenneth D. Merry count++; 942be4aa869SKenneth D. Merry } while (--cntdn); 943be4aa869SKenneth D. Merry 944be4aa869SKenneth D. Merry out: 945be4aa869SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "%s: failed due to timeout count(%d), " 946be4aa869SKenneth D. Merry "int_status(%x)!\n", __func__, count, int_status); 947d3c7b9a0SKenneth D. Merry return (ETIMEDOUT); 948be4aa869SKenneth D. Merry 949d3c7b9a0SKenneth D. Merry } 950d3c7b9a0SKenneth D. Merry 951d3c7b9a0SKenneth D. Merry /* Wait for the chip to signal that the next word in its FIFO can be fetched */ 952d3c7b9a0SKenneth D. Merry static int 953d3c7b9a0SKenneth D. Merry mps_wait_db_int(struct mps_softc *sc) 954d3c7b9a0SKenneth D. Merry { 955d3c7b9a0SKenneth D. Merry int retry; 956d3c7b9a0SKenneth D. Merry 957d3c7b9a0SKenneth D. Merry for (retry = 0; retry < MPS_DB_MAX_WAIT; retry++) { 958d3c7b9a0SKenneth D. Merry if ((mps_regread(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET) & 959d3c7b9a0SKenneth D. Merry MPI2_HIS_IOC2SYS_DB_STATUS) != 0) 960d3c7b9a0SKenneth D. Merry return (0); 961d3c7b9a0SKenneth D. Merry DELAY(2000); 962d3c7b9a0SKenneth D. Merry } 963d3c7b9a0SKenneth D. Merry return (ETIMEDOUT); 964d3c7b9a0SKenneth D. Merry } 965d3c7b9a0SKenneth D. Merry 966d3c7b9a0SKenneth D. Merry /* Step through the synchronous command state machine, i.e. "Doorbell mode" */ 967d3c7b9a0SKenneth D. Merry static int 968d3c7b9a0SKenneth D. Merry mps_request_sync(struct mps_softc *sc, void *req, MPI2_DEFAULT_REPLY *reply, 969d3c7b9a0SKenneth D. Merry int req_sz, int reply_sz, int timeout) 970d3c7b9a0SKenneth D. Merry { 971d3c7b9a0SKenneth D. Merry uint32_t *data32; 972d3c7b9a0SKenneth D. Merry uint16_t *data16; 973d3c7b9a0SKenneth D. Merry int i, count, ioc_sz, residual; 974be4aa869SKenneth D. Merry int sleep_flags = CAN_SLEEP; 975be4aa869SKenneth D. Merry 976f9379dc4SJohn Baldwin if (curthread->td_no_sleeping != 0) 977be4aa869SKenneth D. Merry sleep_flags = NO_SLEEP; 978d3c7b9a0SKenneth D. Merry 979d3c7b9a0SKenneth D. Merry /* Step 1 */ 980d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 981d3c7b9a0SKenneth D. Merry 982d3c7b9a0SKenneth D. Merry /* Step 2 */ 983d3c7b9a0SKenneth D. Merry if (mps_regread(sc, MPI2_DOORBELL_OFFSET) & MPI2_DOORBELL_USED) 984d3c7b9a0SKenneth D. Merry return (EBUSY); 985d3c7b9a0SKenneth D. Merry 986d3c7b9a0SKenneth D. Merry /* Step 3 987d3c7b9a0SKenneth D. Merry * Announce that a message is coming through the doorbell. Messages 988d3c7b9a0SKenneth D. Merry * are pushed at 32bit words, so round up if needed. 989d3c7b9a0SKenneth D. Merry */ 990d3c7b9a0SKenneth D. Merry count = (req_sz + 3) / 4; 991d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_DOORBELL_OFFSET, 992d3c7b9a0SKenneth D. Merry (MPI2_FUNCTION_HANDSHAKE << MPI2_DOORBELL_FUNCTION_SHIFT) | 993d3c7b9a0SKenneth D. Merry (count << MPI2_DOORBELL_ADD_DWORDS_SHIFT)); 994d3c7b9a0SKenneth D. Merry 995d3c7b9a0SKenneth D. Merry /* Step 4 */ 996d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) || 997d3c7b9a0SKenneth D. Merry (mps_regread(sc, MPI2_DOORBELL_OFFSET) & MPI2_DOORBELL_USED) == 0) { 998d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Doorbell failed to activate\n"); 999d3c7b9a0SKenneth D. Merry return (ENXIO); 1000d3c7b9a0SKenneth D. Merry } 1001d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1002be4aa869SKenneth D. Merry if (mps_wait_db_ack(sc, 5, sleep_flags) != 0) { 1003d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Doorbell handshake failed\n"); 1004d3c7b9a0SKenneth D. Merry return (ENXIO); 1005d3c7b9a0SKenneth D. Merry } 1006d3c7b9a0SKenneth D. Merry 1007d3c7b9a0SKenneth D. Merry /* Step 5 */ 1008d3c7b9a0SKenneth D. Merry /* Clock out the message data synchronously in 32-bit dwords*/ 1009d3c7b9a0SKenneth D. Merry data32 = (uint32_t *)req; 1010d3c7b9a0SKenneth D. Merry for (i = 0; i < count; i++) { 1011be4aa869SKenneth D. Merry mps_regwrite(sc, MPI2_DOORBELL_OFFSET, htole32(data32[i])); 1012be4aa869SKenneth D. Merry if (mps_wait_db_ack(sc, 5, sleep_flags) != 0) { 1013d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, 1014d3c7b9a0SKenneth D. Merry "Timeout while writing doorbell\n"); 1015d3c7b9a0SKenneth D. Merry return (ENXIO); 1016d3c7b9a0SKenneth D. Merry } 1017d3c7b9a0SKenneth D. Merry } 1018d3c7b9a0SKenneth D. Merry 1019d3c7b9a0SKenneth D. Merry /* Step 6 */ 1020d3c7b9a0SKenneth D. Merry /* Clock in the reply in 16-bit words. The total length of the 1021d3c7b9a0SKenneth D. Merry * message is always in the 4th byte, so clock out the first 2 words 1022d3c7b9a0SKenneth D. Merry * manually, then loop the rest. 1023d3c7b9a0SKenneth D. Merry */ 1024d3c7b9a0SKenneth D. Merry data16 = (uint16_t *)reply; 1025d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) != 0) { 1026d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Timeout reading doorbell 0\n"); 1027d3c7b9a0SKenneth D. Merry return (ENXIO); 1028d3c7b9a0SKenneth D. Merry } 1029d3c7b9a0SKenneth D. Merry data16[0] = 1030d3c7b9a0SKenneth D. Merry mps_regread(sc, MPI2_DOORBELL_OFFSET) & MPI2_DOORBELL_DATA_MASK; 1031d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1032d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) != 0) { 1033d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Timeout reading doorbell 1\n"); 1034d3c7b9a0SKenneth D. Merry return (ENXIO); 1035d3c7b9a0SKenneth D. Merry } 1036d3c7b9a0SKenneth D. Merry data16[1] = 1037d3c7b9a0SKenneth D. Merry mps_regread(sc, MPI2_DOORBELL_OFFSET) & MPI2_DOORBELL_DATA_MASK; 1038d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1039d3c7b9a0SKenneth D. Merry 1040d3c7b9a0SKenneth D. Merry /* Number of 32bit words in the message */ 1041d3c7b9a0SKenneth D. Merry ioc_sz = reply->MsgLength; 1042d3c7b9a0SKenneth D. Merry 1043d3c7b9a0SKenneth D. Merry /* 1044d3c7b9a0SKenneth D. Merry * Figure out how many 16bit words to clock in without overrunning. 1045d3c7b9a0SKenneth D. Merry * The precision loss with dividing reply_sz can safely be 1046d3c7b9a0SKenneth D. Merry * ignored because the messages can only be multiples of 32bits. 1047d3c7b9a0SKenneth D. Merry */ 1048d3c7b9a0SKenneth D. Merry residual = 0; 1049d3c7b9a0SKenneth D. Merry count = MIN((reply_sz / 4), ioc_sz) * 2; 1050d3c7b9a0SKenneth D. Merry if (count < ioc_sz * 2) { 1051d3c7b9a0SKenneth D. Merry residual = ioc_sz * 2 - count; 10521610f95cSScott Long mps_dprint(sc, MPS_ERROR, "Driver error, throwing away %d " 1053d3c7b9a0SKenneth D. Merry "residual message words\n", residual); 1054d3c7b9a0SKenneth D. Merry } 1055d3c7b9a0SKenneth D. Merry 1056d3c7b9a0SKenneth D. Merry for (i = 2; i < count; i++) { 1057d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) != 0) { 1058d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, 1059d3c7b9a0SKenneth D. Merry "Timeout reading doorbell %d\n", i); 1060d3c7b9a0SKenneth D. Merry return (ENXIO); 1061d3c7b9a0SKenneth D. Merry } 1062d3c7b9a0SKenneth D. Merry data16[i] = mps_regread(sc, MPI2_DOORBELL_OFFSET) & 1063d3c7b9a0SKenneth D. Merry MPI2_DOORBELL_DATA_MASK; 1064d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1065d3c7b9a0SKenneth D. Merry } 1066d3c7b9a0SKenneth D. Merry 1067d3c7b9a0SKenneth D. Merry /* 1068d3c7b9a0SKenneth D. Merry * Pull out residual words that won't fit into the provided buffer. 1069d3c7b9a0SKenneth D. Merry * This keeps the chip from hanging due to a driver programming 1070d3c7b9a0SKenneth D. Merry * error. 1071d3c7b9a0SKenneth D. Merry */ 1072d3c7b9a0SKenneth D. Merry while (residual--) { 1073d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) != 0) { 1074d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, 1075d3c7b9a0SKenneth D. Merry "Timeout reading doorbell\n"); 1076d3c7b9a0SKenneth D. Merry return (ENXIO); 1077d3c7b9a0SKenneth D. Merry } 1078d3c7b9a0SKenneth D. Merry (void)mps_regread(sc, MPI2_DOORBELL_OFFSET); 1079d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1080d3c7b9a0SKenneth D. Merry } 1081d3c7b9a0SKenneth D. Merry 1082d3c7b9a0SKenneth D. Merry /* Step 7 */ 1083d3c7b9a0SKenneth D. Merry if (mps_wait_db_int(sc) != 0) { 1084d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Timeout waiting to exit doorbell\n"); 1085d3c7b9a0SKenneth D. Merry return (ENXIO); 1086d3c7b9a0SKenneth D. Merry } 1087d3c7b9a0SKenneth D. Merry if (mps_regread(sc, MPI2_DOORBELL_OFFSET) & MPI2_DOORBELL_USED) 1088d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Warning, doorbell still active\n"); 1089d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET, 0x0); 1090d3c7b9a0SKenneth D. Merry 1091d3c7b9a0SKenneth D. Merry return (0); 1092d3c7b9a0SKenneth D. Merry } 1093d3c7b9a0SKenneth D. Merry 1094d043c564SKenneth D. Merry static void 1095d3c7b9a0SKenneth D. Merry mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm) 1096d3c7b9a0SKenneth D. Merry { 10974bc604dcSScott Long request_descriptor_t rd; 10981610f95cSScott Long MPS_FUNCTRACE(sc); 10991610f95cSScott Long mps_dprint(sc, MPS_TRACE, "SMID %u cm %p ccb %p\n", 1100d043c564SKenneth D. Merry cm->cm_desc.Default.SMID, cm, cm->cm_ccb); 1101d3c7b9a0SKenneth D. Merry 1102653c521fSKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_ATTACH_DONE && !(sc->mps_flags & MPS_FLAGS_SHUTDOWN)) 1103550e2acdSKenneth D. Merry mtx_assert(&sc->mps_mtx, MA_OWNED); 1104550e2acdSKenneth D. Merry 1105d043c564SKenneth D. Merry if (++sc->io_cmds_active > sc->io_cmds_highwater) 1106d043c564SKenneth D. Merry sc->io_cmds_highwater++; 1107be4aa869SKenneth D. Merry rd.u.low = cm->cm_desc.Words.Low; 1108be4aa869SKenneth D. Merry rd.u.high = cm->cm_desc.Words.High; 1109be4aa869SKenneth D. Merry rd.word = htole64(rd.word); 1110f0779b04SScott Long 1111175ad3d0SKenneth D. Merry KASSERT(cm->cm_state == MPS_CM_STATE_BUSY, 1112175ad3d0SKenneth D. Merry ("command not busy, state = %u\n", cm->cm_state)); 1113f0779b04SScott Long cm->cm_state = MPS_CM_STATE_INQUEUE; 1114f0779b04SScott Long 1115be4aa869SKenneth D. Merry /* TODO-We may need to make below regwrite atomic */ 1116d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET, 1117be4aa869SKenneth D. Merry rd.u.low); 1118d3c7b9a0SKenneth D. Merry mps_regwrite(sc, MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET, 1119be4aa869SKenneth D. Merry rd.u.high); 1120d3c7b9a0SKenneth D. Merry } 1121d3c7b9a0SKenneth D. Merry 1122d3c7b9a0SKenneth D. Merry /* 1123d3c7b9a0SKenneth D. Merry * Just the FACTS, ma'am. 1124d3c7b9a0SKenneth D. Merry */ 1125d3c7b9a0SKenneth D. Merry static int 1126d3c7b9a0SKenneth D. Merry mps_get_iocfacts(struct mps_softc *sc, MPI2_IOC_FACTS_REPLY *facts) 1127d3c7b9a0SKenneth D. Merry { 1128d3c7b9a0SKenneth D. Merry MPI2_DEFAULT_REPLY *reply; 1129d3c7b9a0SKenneth D. Merry MPI2_IOC_FACTS_REQUEST request; 1130*c0e0e530Sprateek sethi int error, req_sz, reply_sz, retry = 0; 1131d3c7b9a0SKenneth D. Merry 11321610f95cSScott Long MPS_FUNCTRACE(sc); 1133757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 1134d3c7b9a0SKenneth D. Merry 1135d3c7b9a0SKenneth D. Merry req_sz = sizeof(MPI2_IOC_FACTS_REQUEST); 1136d3c7b9a0SKenneth D. Merry reply_sz = sizeof(MPI2_IOC_FACTS_REPLY); 1137d3c7b9a0SKenneth D. Merry reply = (MPI2_DEFAULT_REPLY *)facts; 1138d3c7b9a0SKenneth D. Merry 1139*c0e0e530Sprateek sethi /* 1140*c0e0e530Sprateek sethi * Retry sending the initialization sequence. Sometimes, especially with 1141*c0e0e530Sprateek sethi * older firmware, the initialization process fails. Retrying allows the 1142*c0e0e530Sprateek sethi * error to clear in the firmware. 1143*c0e0e530Sprateek sethi */ 1144d3c7b9a0SKenneth D. Merry bzero(&request, req_sz); 1145d3c7b9a0SKenneth D. Merry request.Function = MPI2_FUNCTION_IOC_FACTS; 1146*c0e0e530Sprateek sethi while (retry < 5) { 1147d3c7b9a0SKenneth D. Merry error = mps_request_sync(sc, &request, reply, req_sz, reply_sz, 5); 1148*c0e0e530Sprateek sethi if (error == 0) 1149*c0e0e530Sprateek sethi break; 1150*c0e0e530Sprateek sethi mps_dprint(sc, MPS_FAULT, "%s failed retry %d\n", __func__, retry); 1151*c0e0e530Sprateek sethi DELAY(1000); 1152*c0e0e530Sprateek sethi retry++; 1153*c0e0e530Sprateek sethi } 1154d3c7b9a0SKenneth D. Merry 1155d3c7b9a0SKenneth D. Merry return (error); 1156d3c7b9a0SKenneth D. Merry } 1157d3c7b9a0SKenneth D. Merry 1158d3c7b9a0SKenneth D. Merry static int 1159d3c7b9a0SKenneth D. Merry mps_send_iocinit(struct mps_softc *sc) 1160d3c7b9a0SKenneth D. Merry { 1161d3c7b9a0SKenneth D. Merry MPI2_IOC_INIT_REQUEST init; 1162d3c7b9a0SKenneth D. Merry MPI2_DEFAULT_REPLY reply; 1163*c0e0e530Sprateek sethi int req_sz, reply_sz, error, retry = 0; 11649b91b192SKenneth D. Merry struct timeval now; 11659b91b192SKenneth D. Merry uint64_t time_in_msec; 1166d3c7b9a0SKenneth D. Merry 11671610f95cSScott Long MPS_FUNCTRACE(sc); 1168757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 1169d3c7b9a0SKenneth D. Merry 117096410703SScott Long /* Do a quick sanity check on proper initialization */ 117196410703SScott Long if ((sc->pqdepth == 0) || (sc->fqdepth == 0) || (sc->reqframesz == 0) 117296410703SScott Long || (sc->replyframesz == 0)) { 117396410703SScott Long mps_dprint(sc, MPS_INIT|MPS_ERROR, 117496410703SScott Long "Driver not fully initialized for IOCInit\n"); 117596410703SScott Long return (EINVAL); 117696410703SScott Long } 117796410703SScott Long 1178d3c7b9a0SKenneth D. Merry req_sz = sizeof(MPI2_IOC_INIT_REQUEST); 1179d3c7b9a0SKenneth D. Merry reply_sz = sizeof(MPI2_IOC_INIT_REPLY); 1180d3c7b9a0SKenneth D. Merry bzero(&init, req_sz); 1181d3c7b9a0SKenneth D. Merry bzero(&reply, reply_sz); 1182d3c7b9a0SKenneth D. Merry 1183d3c7b9a0SKenneth D. Merry /* 1184d3c7b9a0SKenneth D. Merry * Fill in the init block. Note that most addresses are 1185d3c7b9a0SKenneth D. Merry * deliberately in the lower 32bits of memory. This is a micro- 1186d3c7b9a0SKenneth D. Merry * optimzation for PCI/PCIX, though it's not clear if it helps PCIe. 1187d3c7b9a0SKenneth D. Merry */ 1188d3c7b9a0SKenneth D. Merry init.Function = MPI2_FUNCTION_IOC_INIT; 1189d3c7b9a0SKenneth D. Merry init.WhoInit = MPI2_WHOINIT_HOST_DRIVER; 1190be4aa869SKenneth D. Merry init.MsgVersion = htole16(MPI2_VERSION); 1191be4aa869SKenneth D. Merry init.HeaderVersion = htole16(MPI2_HEADER_VERSION); 119296410703SScott Long init.SystemRequestFrameSize = htole16((uint16_t)(sc->reqframesz / 4)); 1193be4aa869SKenneth D. Merry init.ReplyDescriptorPostQueueDepth = htole16(sc->pqdepth); 1194be4aa869SKenneth D. Merry init.ReplyFreeQueueDepth = htole16(sc->fqdepth); 1195d3c7b9a0SKenneth D. Merry init.SenseBufferAddressHigh = 0; 1196d3c7b9a0SKenneth D. Merry init.SystemReplyAddressHigh = 0; 1197d3c7b9a0SKenneth D. Merry init.SystemRequestFrameBaseAddress.High = 0; 1198be4aa869SKenneth D. Merry init.SystemRequestFrameBaseAddress.Low = htole32((uint32_t)sc->req_busaddr); 1199d3c7b9a0SKenneth D. Merry init.ReplyDescriptorPostQueueAddress.High = 0; 1200be4aa869SKenneth D. Merry init.ReplyDescriptorPostQueueAddress.Low = htole32((uint32_t)sc->post_busaddr); 1201d3c7b9a0SKenneth D. Merry init.ReplyFreeQueueAddress.High = 0; 1202be4aa869SKenneth D. Merry init.ReplyFreeQueueAddress.Low = htole32((uint32_t)sc->free_busaddr); 12039b91b192SKenneth D. Merry getmicrotime(&now); 12049b91b192SKenneth D. Merry time_in_msec = (now.tv_sec * 1000 + now.tv_usec/1000); 12059b91b192SKenneth D. Merry init.TimeStamp.High = htole32((time_in_msec >> 32) & 0xFFFFFFFF); 12069b91b192SKenneth D. Merry init.TimeStamp.Low = htole32(time_in_msec & 0xFFFFFFFF); 1207*c0e0e530Sprateek sethi /* 1208*c0e0e530Sprateek sethi * Retry sending the initialization sequence. Sometimes, especially with 1209*c0e0e530Sprateek sethi * older firmware, the initialization process fails. Retrying allows the 1210*c0e0e530Sprateek sethi * error to clear in the firmware. 1211*c0e0e530Sprateek sethi */ 1212*c0e0e530Sprateek sethi while (retry < 5) { 1213d3c7b9a0SKenneth D. Merry error = mps_request_sync(sc, &init, &reply, req_sz, reply_sz, 5); 1214d3c7b9a0SKenneth D. Merry if ((reply.IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) 1215d3c7b9a0SKenneth D. Merry error = ENXIO; 1216*c0e0e530Sprateek sethi if (error == 0) 1217*c0e0e530Sprateek sethi break; 1218*c0e0e530Sprateek sethi mps_dprint(sc, MPS_FAULT, "%s failed retry %d\n", __func__, retry); 1219*c0e0e530Sprateek sethi DELAY(1000); 1220*c0e0e530Sprateek sethi retry++; 1221*c0e0e530Sprateek sethi } 1222d3c7b9a0SKenneth D. Merry 12231610f95cSScott Long mps_dprint(sc, MPS_INIT, "IOCInit status= 0x%x\n", reply.IOCStatus); 1224757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 1225d3c7b9a0SKenneth D. Merry return (error); 1226d3c7b9a0SKenneth D. Merry } 1227d3c7b9a0SKenneth D. Merry 1228d3c7b9a0SKenneth D. Merry void 1229d3c7b9a0SKenneth D. Merry mps_memaddr_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1230d3c7b9a0SKenneth D. Merry { 1231d3c7b9a0SKenneth D. Merry bus_addr_t *addr; 1232d3c7b9a0SKenneth D. Merry 1233d3c7b9a0SKenneth D. Merry addr = arg; 1234d3c7b9a0SKenneth D. Merry *addr = segs[0].ds_addr; 1235d3c7b9a0SKenneth D. Merry } 1236d3c7b9a0SKenneth D. Merry 1237e2997a03SKenneth D. Merry void 1238e2997a03SKenneth D. Merry mps_memaddr_wait_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1239e2997a03SKenneth D. Merry { 1240e2997a03SKenneth D. Merry struct mps_busdma_context *ctx; 1241e2997a03SKenneth D. Merry int need_unload, need_free; 1242e2997a03SKenneth D. Merry 1243e2997a03SKenneth D. Merry ctx = (struct mps_busdma_context *)arg; 1244e2997a03SKenneth D. Merry need_unload = 0; 1245e2997a03SKenneth D. Merry need_free = 0; 1246e2997a03SKenneth D. Merry 1247e2997a03SKenneth D. Merry mps_lock(ctx->softc); 1248e2997a03SKenneth D. Merry ctx->error = error; 1249e2997a03SKenneth D. Merry ctx->completed = 1; 1250e2997a03SKenneth D. Merry if ((error == 0) && (ctx->abandoned == 0)) { 1251e2997a03SKenneth D. Merry *ctx->addr = segs[0].ds_addr; 1252e2997a03SKenneth D. Merry } else { 1253e2997a03SKenneth D. Merry if (nsegs != 0) 1254e2997a03SKenneth D. Merry need_unload = 1; 1255e2997a03SKenneth D. Merry if (ctx->abandoned != 0) 1256e2997a03SKenneth D. Merry need_free = 1; 1257e2997a03SKenneth D. Merry } 1258e2997a03SKenneth D. Merry if (need_free == 0) 1259e2997a03SKenneth D. Merry wakeup(ctx); 1260e2997a03SKenneth D. Merry 1261e2997a03SKenneth D. Merry mps_unlock(ctx->softc); 1262e2997a03SKenneth D. Merry 1263e2997a03SKenneth D. Merry if (need_unload != 0) { 1264e2997a03SKenneth D. Merry bus_dmamap_unload(ctx->buffer_dmat, 1265e2997a03SKenneth D. Merry ctx->buffer_dmamap); 1266e2997a03SKenneth D. Merry *ctx->addr = 0; 1267e2997a03SKenneth D. Merry } 1268e2997a03SKenneth D. Merry 1269e2997a03SKenneth D. Merry if (need_free != 0) 1270e2997a03SKenneth D. Merry free(ctx, M_MPSUSER); 1271e2997a03SKenneth D. Merry } 1272e2997a03SKenneth D. Merry 1273d3c7b9a0SKenneth D. Merry static int 1274d3c7b9a0SKenneth D. Merry mps_alloc_queues(struct mps_softc *sc) 1275d3c7b9a0SKenneth D. Merry { 1276bec09074SScott Long struct mps_queue *q; 1277d821d364SPedro F. Giffuni u_int nq, i; 1278bec09074SScott Long 12793c5ac992SScott Long nq = sc->msi_msgs; 1280bec09074SScott Long mps_dprint(sc, MPS_INIT|MPS_XINFO, "Allocating %d I/O queues\n", nq); 1281bec09074SScott Long 1282ac2fffa4SPedro F. Giffuni sc->queues = malloc(sizeof(struct mps_queue) * nq, M_MPT2, 12833c5ac992SScott Long M_NOWAIT|M_ZERO); 1284bec09074SScott Long if (sc->queues == NULL) 1285bec09074SScott Long return (ENOMEM); 1286bec09074SScott Long 1287bec09074SScott Long for (i = 0; i < nq; i++) { 1288bec09074SScott Long q = &sc->queues[i]; 1289bec09074SScott Long mps_dprint(sc, MPS_INIT, "Configuring queue %d %p\n", i, q); 1290bec09074SScott Long q->sc = sc; 1291bec09074SScott Long q->qnum = i; 1292bec09074SScott Long } 1293d3c7b9a0SKenneth D. Merry 12941415db6cSScott Long return (0); 12951415db6cSScott Long } 12961415db6cSScott Long 12971415db6cSScott Long static int 12981415db6cSScott Long mps_alloc_hw_queues(struct mps_softc *sc) 12991415db6cSScott Long { 130074c781edSScott Long bus_dma_template_t t; 13011415db6cSScott Long bus_addr_t queues_busaddr; 13021415db6cSScott Long uint8_t *queues; 13031415db6cSScott Long int qsize, fqsize, pqsize; 13041415db6cSScott Long 1305d3c7b9a0SKenneth D. Merry /* 1306d3c7b9a0SKenneth D. Merry * The reply free queue contains 4 byte entries in multiples of 16 and 1307d3c7b9a0SKenneth D. Merry * aligned on a 16 byte boundary. There must always be an unused entry. 1308d3c7b9a0SKenneth D. Merry * This queue supplies fresh reply frames for the firmware to use. 1309d3c7b9a0SKenneth D. Merry * 1310d3c7b9a0SKenneth D. Merry * The reply descriptor post queue contains 8 byte entries in 1311d3c7b9a0SKenneth D. Merry * multiples of 16 and aligned on a 16 byte boundary. This queue 1312d3c7b9a0SKenneth D. Merry * contains filled-in reply frames sent from the firmware to the host. 1313d3c7b9a0SKenneth D. Merry * 1314d3c7b9a0SKenneth D. Merry * These two queues are allocated together for simplicity. 1315d3c7b9a0SKenneth D. Merry */ 1316d9c9c81cSPedro F. Giffuni sc->fqdepth = roundup2(sc->num_replies + 1, 16); 1317d9c9c81cSPedro F. Giffuni sc->pqdepth = roundup2(sc->num_replies + 1, 16); 1318d3c7b9a0SKenneth D. Merry fqsize= sc->fqdepth * 4; 1319d3c7b9a0SKenneth D. Merry pqsize = sc->pqdepth * 8; 1320d3c7b9a0SKenneth D. Merry qsize = fqsize + pqsize; 1321d3c7b9a0SKenneth D. Merry 13221002529eSScott Long bus_dma_template_init(&t, sc->mps_parent_dmat); 132374c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_ALIGNMENT(16), BD_MAXSIZE(qsize), 132474c781edSScott Long BD_MAXSEGSIZE(qsize), BD_NSEGMENTS(1), 132574c781edSScott Long BD_LOWADDR(BUS_SPACE_MAXADDR_32BIT)); 13261002529eSScott Long if (bus_dma_template_tag(&t, &sc->queues_dmat)) { 1327757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate queues DMA tag\n"); 1328d3c7b9a0SKenneth D. Merry return (ENOMEM); 1329d3c7b9a0SKenneth D. Merry } 1330d3c7b9a0SKenneth D. Merry if (bus_dmamem_alloc(sc->queues_dmat, (void **)&queues, BUS_DMA_NOWAIT, 1331d3c7b9a0SKenneth D. Merry &sc->queues_map)) { 1332757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate queues memory\n"); 1333d3c7b9a0SKenneth D. Merry return (ENOMEM); 1334d3c7b9a0SKenneth D. Merry } 1335d3c7b9a0SKenneth D. Merry bzero(queues, qsize); 1336d3c7b9a0SKenneth D. Merry bus_dmamap_load(sc->queues_dmat, sc->queues_map, queues, qsize, 1337d3c7b9a0SKenneth D. Merry mps_memaddr_cb, &queues_busaddr, 0); 1338d3c7b9a0SKenneth D. Merry 1339d3c7b9a0SKenneth D. Merry sc->free_queue = (uint32_t *)queues; 1340d3c7b9a0SKenneth D. Merry sc->free_busaddr = queues_busaddr; 1341d3c7b9a0SKenneth D. Merry sc->post_queue = (MPI2_REPLY_DESCRIPTORS_UNION *)(queues + fqsize); 1342d3c7b9a0SKenneth D. Merry sc->post_busaddr = queues_busaddr + fqsize; 134392ddc7b8SLi-Wen Hsu mps_dprint(sc, MPS_INIT, "free queue busaddr= %#016jx size= %d\n", 134492ddc7b8SLi-Wen Hsu (uintmax_t)sc->free_busaddr, fqsize); 134592ddc7b8SLi-Wen Hsu mps_dprint(sc, MPS_INIT, "reply queue busaddr= %#016jx size= %d\n", 134692ddc7b8SLi-Wen Hsu (uintmax_t)sc->post_busaddr, pqsize); 1347d3c7b9a0SKenneth D. Merry 1348d3c7b9a0SKenneth D. Merry return (0); 1349d3c7b9a0SKenneth D. Merry } 1350d3c7b9a0SKenneth D. Merry 1351d3c7b9a0SKenneth D. Merry static int 1352d3c7b9a0SKenneth D. Merry mps_alloc_replies(struct mps_softc *sc) 1353d3c7b9a0SKenneth D. Merry { 135474c781edSScott Long bus_dma_template_t t; 135549dfe4a2SKenneth D. Merry int rsize, num_replies; 1356d3c7b9a0SKenneth D. Merry 135796410703SScott Long /* Store the reply frame size in bytes rather than as 32bit words */ 135896410703SScott Long sc->replyframesz = sc->facts->ReplyFrameSize * 4; 135996410703SScott Long 136049dfe4a2SKenneth D. Merry /* 136149dfe4a2SKenneth D. Merry * sc->num_replies should be one less than sc->fqdepth. We need to 136249dfe4a2SKenneth D. Merry * allocate space for sc->fqdepth replies, but only sc->num_replies 136349dfe4a2SKenneth D. Merry * replies can be used at once. 136449dfe4a2SKenneth D. Merry */ 136549dfe4a2SKenneth D. Merry num_replies = max(sc->fqdepth, sc->num_replies); 136649dfe4a2SKenneth D. Merry 136796410703SScott Long rsize = sc->replyframesz * num_replies; 13681002529eSScott Long bus_dma_template_init(&t, sc->mps_parent_dmat); 136974c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_ALIGNMENT(4), BD_MAXSIZE(rsize), 137074c781edSScott Long BD_MAXSEGSIZE(rsize), BD_NSEGMENTS(1), 137174c781edSScott Long BD_LOWADDR(BUS_SPACE_MAXADDR_32BIT)); 13721002529eSScott Long if (bus_dma_template_tag(&t, &sc->reply_dmat)) { 1373757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate replies DMA tag\n"); 1374d3c7b9a0SKenneth D. Merry return (ENOMEM); 1375d3c7b9a0SKenneth D. Merry } 1376d3c7b9a0SKenneth D. Merry if (bus_dmamem_alloc(sc->reply_dmat, (void **)&sc->reply_frames, 1377d3c7b9a0SKenneth D. Merry BUS_DMA_NOWAIT, &sc->reply_map)) { 1378757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate replies memory\n"); 1379d3c7b9a0SKenneth D. Merry return (ENOMEM); 1380d3c7b9a0SKenneth D. Merry } 1381d3c7b9a0SKenneth D. Merry bzero(sc->reply_frames, rsize); 1382d3c7b9a0SKenneth D. Merry bus_dmamap_load(sc->reply_dmat, sc->reply_map, sc->reply_frames, rsize, 1383d3c7b9a0SKenneth D. Merry mps_memaddr_cb, &sc->reply_busaddr, 0); 1384d3c7b9a0SKenneth D. Merry 138592ddc7b8SLi-Wen Hsu mps_dprint(sc, MPS_INIT, "reply frames busaddr= %#016jx size= %d\n", 138692ddc7b8SLi-Wen Hsu (uintmax_t)sc->reply_busaddr, rsize); 138763b1a335SScott Long 1388d3c7b9a0SKenneth D. Merry return (0); 1389d3c7b9a0SKenneth D. Merry } 1390d3c7b9a0SKenneth D. Merry 1391731308d0SAlexander Motin static void 1392731308d0SAlexander Motin mps_load_chains_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1393731308d0SAlexander Motin { 1394731308d0SAlexander Motin struct mps_softc *sc = arg; 1395731308d0SAlexander Motin struct mps_chain *chain; 1396731308d0SAlexander Motin bus_size_t bo; 1397731308d0SAlexander Motin int i, o, s; 1398731308d0SAlexander Motin 1399731308d0SAlexander Motin if (error != 0) 1400731308d0SAlexander Motin return; 1401731308d0SAlexander Motin 1402731308d0SAlexander Motin for (i = 0, o = 0, s = 0; s < nsegs; s++) { 1403e30fceb8SWarner Losh KASSERT(segs[s].ds_addr + segs[s].ds_len - 1 <= BUS_SPACE_MAXADDR_32BIT, 1404e30fceb8SWarner Losh ("mps: Bad segment address %#jx len %#jx\n", (uintmax_t)segs[s].ds_addr, 1405e30fceb8SWarner Losh (uintmax_t)segs[s].ds_len)); 1406731308d0SAlexander Motin for (bo = 0; bo + sc->reqframesz <= segs[s].ds_len; 1407731308d0SAlexander Motin bo += sc->reqframesz) { 1408731308d0SAlexander Motin chain = &sc->chains[i++]; 1409731308d0SAlexander Motin chain->chain =(MPI2_SGE_IO_UNION *)(sc->chain_frames+o); 1410731308d0SAlexander Motin chain->chain_busaddr = segs[s].ds_addr + bo; 1411731308d0SAlexander Motin o += sc->reqframesz; 1412731308d0SAlexander Motin mps_free_chain(sc, chain); 1413731308d0SAlexander Motin } 1414731308d0SAlexander Motin if (bo != segs[s].ds_len) 1415731308d0SAlexander Motin o += segs[s].ds_len - bo; 1416731308d0SAlexander Motin } 1417731308d0SAlexander Motin sc->chain_free_lowwater = i; 1418731308d0SAlexander Motin } 1419731308d0SAlexander Motin 1420d3c7b9a0SKenneth D. Merry static int 1421d3c7b9a0SKenneth D. Merry mps_alloc_requests(struct mps_softc *sc) 1422d3c7b9a0SKenneth D. Merry { 142374c781edSScott Long bus_dma_template_t t; 1424d3c7b9a0SKenneth D. Merry struct mps_command *cm; 1425d3c7b9a0SKenneth D. Merry int i, rsize, nsegs; 1426d3c7b9a0SKenneth D. Merry 142796410703SScott Long rsize = sc->reqframesz * sc->num_reqs; 14281002529eSScott Long bus_dma_template_init(&t, sc->mps_parent_dmat); 142974c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_ALIGNMENT(16), BD_MAXSIZE(rsize), 143074c781edSScott Long BD_MAXSEGSIZE(rsize), BD_NSEGMENTS(1), 143174c781edSScott Long BD_LOWADDR(BUS_SPACE_MAXADDR_32BIT)); 14321002529eSScott Long if (bus_dma_template_tag(&t, &sc->req_dmat)) { 1433757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate request DMA tag\n"); 1434d3c7b9a0SKenneth D. Merry return (ENOMEM); 1435d3c7b9a0SKenneth D. Merry } 1436d3c7b9a0SKenneth D. Merry if (bus_dmamem_alloc(sc->req_dmat, (void **)&sc->req_frames, 1437d3c7b9a0SKenneth D. Merry BUS_DMA_NOWAIT, &sc->req_map)) { 1438757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate request memory\n"); 1439d3c7b9a0SKenneth D. Merry return (ENOMEM); 1440d3c7b9a0SKenneth D. Merry } 1441d3c7b9a0SKenneth D. Merry bzero(sc->req_frames, rsize); 1442d3c7b9a0SKenneth D. Merry bus_dmamap_load(sc->req_dmat, sc->req_map, sc->req_frames, rsize, 1443d3c7b9a0SKenneth D. Merry mps_memaddr_cb, &sc->req_busaddr, 0); 144492ddc7b8SLi-Wen Hsu mps_dprint(sc, MPS_INIT, "request frames busaddr= %#016jx size= %d\n", 144592ddc7b8SLi-Wen Hsu (uintmax_t)sc->req_busaddr, rsize); 1446d3c7b9a0SKenneth D. Merry 1447731308d0SAlexander Motin sc->chains = malloc(sizeof(struct mps_chain) * sc->num_chains, M_MPT2, 1448731308d0SAlexander Motin M_NOWAIT | M_ZERO); 1449731308d0SAlexander Motin if (!sc->chains) { 1450731308d0SAlexander Motin mps_dprint(sc, MPS_ERROR, "Cannot allocate chain memory\n"); 1451731308d0SAlexander Motin return (ENOMEM); 1452731308d0SAlexander Motin } 14534f5d6573SAlexander Motin rsize = sc->reqframesz * sc->num_chains; 14541002529eSScott Long bus_dma_template_clone(&t, sc->req_dmat); 145574c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_MAXSIZE(rsize), BD_MAXSEGSIZE(rsize), 1456264610a8SKenneth D. Merry BD_NSEGMENTS(howmany(rsize, PAGE_SIZE)), 1457264610a8SKenneth D. Merry BD_BOUNDARY(BUS_SPACE_MAXSIZE_32BIT+1)); 14581002529eSScott Long if (bus_dma_template_tag(&t, &sc->chain_dmat)) { 1459757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate chain DMA tag\n"); 1460d3c7b9a0SKenneth D. Merry return (ENOMEM); 1461d3c7b9a0SKenneth D. Merry } 1462d3c7b9a0SKenneth D. Merry if (bus_dmamem_alloc(sc->chain_dmat, (void **)&sc->chain_frames, 1463731308d0SAlexander Motin BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->chain_map)) { 1464757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate chain memory\n"); 1465d3c7b9a0SKenneth D. Merry return (ENOMEM); 1466d3c7b9a0SKenneth D. Merry } 1467731308d0SAlexander Motin if (bus_dmamap_load(sc->chain_dmat, sc->chain_map, sc->chain_frames, 1468731308d0SAlexander Motin rsize, mps_load_chains_cb, sc, BUS_DMA_NOWAIT)) { 1469731308d0SAlexander Motin mps_dprint(sc, MPS_ERROR, "Cannot load chain memory\n"); 1470731308d0SAlexander Motin bus_dmamem_free(sc->chain_dmat, sc->chain_frames, 1471731308d0SAlexander Motin sc->chain_map); 1472731308d0SAlexander Motin return (ENOMEM); 1473731308d0SAlexander Motin } 1474d3c7b9a0SKenneth D. Merry 1475d3c7b9a0SKenneth D. Merry rsize = MPS_SENSE_LEN * sc->num_reqs; 14761002529eSScott Long bus_dma_template_clone(&t, sc->req_dmat); 147774c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_ALIGNMENT(1), BD_MAXSIZE(rsize), 147874c781edSScott Long BD_MAXSEGSIZE(rsize)); 14791002529eSScott Long if (bus_dma_template_tag(&t, &sc->sense_dmat)) { 1480757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate sense DMA tag\n"); 1481d3c7b9a0SKenneth D. Merry return (ENOMEM); 1482d3c7b9a0SKenneth D. Merry } 1483d3c7b9a0SKenneth D. Merry if (bus_dmamem_alloc(sc->sense_dmat, (void **)&sc->sense_frames, 1484d3c7b9a0SKenneth D. Merry BUS_DMA_NOWAIT, &sc->sense_map)) { 1485757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate sense memory\n"); 1486d3c7b9a0SKenneth D. Merry return (ENOMEM); 1487d3c7b9a0SKenneth D. Merry } 1488d3c7b9a0SKenneth D. Merry bzero(sc->sense_frames, rsize); 1489d3c7b9a0SKenneth D. Merry bus_dmamap_load(sc->sense_dmat, sc->sense_map, sc->sense_frames, rsize, 1490d3c7b9a0SKenneth D. Merry mps_memaddr_cb, &sc->sense_busaddr, 0); 149192ddc7b8SLi-Wen Hsu mps_dprint(sc, MPS_INIT, "sense frames busaddr= %#016jx size= %d\n", 149292ddc7b8SLi-Wen Hsu (uintmax_t)sc->sense_busaddr, rsize); 1493d3c7b9a0SKenneth D. Merry 14944f5d6573SAlexander Motin nsegs = (sc->maxio / PAGE_SIZE) + 1; 14951002529eSScott Long bus_dma_template_init(&t, sc->mps_parent_dmat); 149674c781edSScott Long BUS_DMA_TEMPLATE_FILL(&t, BD_MAXSIZE(BUS_SPACE_MAXSIZE_32BIT), 149774c781edSScott Long BD_NSEGMENTS(nsegs), BD_MAXSEGSIZE(BUS_SPACE_MAXSIZE_24BIT), 149874c781edSScott Long BD_FLAGS(BUS_DMA_ALLOCNOW), BD_LOCKFUNC(busdma_lock_mutex), 1499264610a8SKenneth D. Merry BD_LOCKFUNCARG(&sc->mps_mtx), 1500264610a8SKenneth D. Merry BD_BOUNDARY(BUS_SPACE_MAXSIZE_32BIT+1)); 15011002529eSScott Long if (bus_dma_template_tag(&t, &sc->buffer_dmat)) { 1502757ff642SScott Long mps_dprint(sc, MPS_ERROR, "Cannot allocate buffer DMA tag\n"); 1503d3c7b9a0SKenneth D. Merry return (ENOMEM); 1504d3c7b9a0SKenneth D. Merry } 1505d3c7b9a0SKenneth D. Merry 1506d3c7b9a0SKenneth D. Merry /* 1507d3c7b9a0SKenneth D. Merry * SMID 0 cannot be used as a free command per the firmware spec. 1508d3c7b9a0SKenneth D. Merry * Just drop that command instead of risking accounting bugs. 1509d3c7b9a0SKenneth D. Merry */ 1510d3c7b9a0SKenneth D. Merry sc->commands = malloc(sizeof(struct mps_command) * sc->num_reqs, 1511d3c7b9a0SKenneth D. Merry M_MPT2, M_WAITOK | M_ZERO); 1512d3c7b9a0SKenneth D. Merry for (i = 1; i < sc->num_reqs; i++) { 1513d3c7b9a0SKenneth D. Merry cm = &sc->commands[i]; 151496410703SScott Long cm->cm_req = sc->req_frames + i * sc->reqframesz; 151596410703SScott Long cm->cm_req_busaddr = sc->req_busaddr + i * sc->reqframesz; 1516d3c7b9a0SKenneth D. Merry cm->cm_sense = &sc->sense_frames[i]; 1517d3c7b9a0SKenneth D. Merry cm->cm_sense_busaddr = sc->sense_busaddr + i * MPS_SENSE_LEN; 1518d3c7b9a0SKenneth D. Merry cm->cm_desc.Default.SMID = i; 1519d3c7b9a0SKenneth D. Merry cm->cm_sc = sc; 1520f0779b04SScott Long cm->cm_state = MPS_CM_STATE_BUSY; 1521d3c7b9a0SKenneth D. Merry TAILQ_INIT(&cm->cm_chain_list); 1522d043c564SKenneth D. Merry callout_init_mtx(&cm->cm_callout, &sc->mps_mtx, 0); 1523d3c7b9a0SKenneth D. Merry 1524d3c7b9a0SKenneth D. Merry /* XXX Is a failure here a critical problem? */ 1525d3c7b9a0SKenneth D. Merry if (bus_dmamap_create(sc->buffer_dmat, 0, &cm->cm_dmamap) == 0) 152662a09ee9SAlexander Motin if (i <= sc->num_prireqs) 1527d043c564SKenneth D. Merry mps_free_high_priority_command(sc, cm); 1528d043c564SKenneth D. Merry else 1529d3c7b9a0SKenneth D. Merry mps_free_command(sc, cm); 1530d3c7b9a0SKenneth D. Merry else { 1531d043c564SKenneth D. Merry panic("failed to allocate command %d\n", i); 1532d3c7b9a0SKenneth D. Merry sc->num_reqs = i; 1533d3c7b9a0SKenneth D. Merry break; 1534d3c7b9a0SKenneth D. Merry } 1535d3c7b9a0SKenneth D. Merry } 1536d3c7b9a0SKenneth D. Merry 1537d3c7b9a0SKenneth D. Merry return (0); 1538d3c7b9a0SKenneth D. Merry } 1539d3c7b9a0SKenneth D. Merry 1540d3c7b9a0SKenneth D. Merry static int 1541d3c7b9a0SKenneth D. Merry mps_init_queues(struct mps_softc *sc) 1542d3c7b9a0SKenneth D. Merry { 1543d3c7b9a0SKenneth D. Merry int i; 1544d3c7b9a0SKenneth D. Merry 1545d3c7b9a0SKenneth D. Merry memset((uint8_t *)sc->post_queue, 0xff, sc->pqdepth * 8); 1546d3c7b9a0SKenneth D. Merry 154749dfe4a2SKenneth D. Merry /* 154849dfe4a2SKenneth D. Merry * According to the spec, we need to use one less reply than we 154949dfe4a2SKenneth D. Merry * have space for on the queue. So sc->num_replies (the number we 155049dfe4a2SKenneth D. Merry * use) should be less than sc->fqdepth (allocated size). 155149dfe4a2SKenneth D. Merry */ 1552d3c7b9a0SKenneth D. Merry if (sc->num_replies >= sc->fqdepth) 1553d3c7b9a0SKenneth D. Merry return (EINVAL); 1554d3c7b9a0SKenneth D. Merry 155549dfe4a2SKenneth D. Merry /* 155649dfe4a2SKenneth D. Merry * Initialize all of the free queue entries. 155749dfe4a2SKenneth D. Merry */ 155849dfe4a2SKenneth D. Merry for (i = 0; i < sc->fqdepth; i++) 155996410703SScott Long sc->free_queue[i] = sc->reply_busaddr + (i * sc->replyframesz); 1560d3c7b9a0SKenneth D. Merry sc->replyfreeindex = sc->num_replies; 1561d3c7b9a0SKenneth D. Merry 1562d3c7b9a0SKenneth D. Merry return (0); 1563d3c7b9a0SKenneth D. Merry } 1564d3c7b9a0SKenneth D. Merry 1565d043c564SKenneth D. Merry /* Get the driver parameter tunables. Lowest priority are the driver defaults. 1566d043c564SKenneth D. Merry * Next are the global settings, if they exist. Highest are the per-unit 1567d043c564SKenneth D. Merry * settings, if they exist. 1568d043c564SKenneth D. Merry */ 1569252b2b4fSScott Long void 1570d043c564SKenneth D. Merry mps_get_tunables(struct mps_softc *sc) 1571d3c7b9a0SKenneth D. Merry { 1572867aa8cdSScott Long char tmpstr[80], mps_debug[80]; 1573d043c564SKenneth D. Merry 1574d043c564SKenneth D. Merry /* XXX default to some debugging for now */ 15751610f95cSScott Long sc->mps_debug = MPS_INFO|MPS_FAULT; 1576d043c564SKenneth D. Merry sc->disable_msix = 0; 1577d043c564SKenneth D. Merry sc->disable_msi = 0; 15783c5ac992SScott Long sc->max_msix = MPS_MSIX_MAX; 1579d043c564SKenneth D. Merry sc->max_chains = MPS_CHAIN_FRAMES; 158032b0a21eSStephen McConnell sc->max_io_pages = MPS_MAXIO_PAGES; 1581ef065d89SStephen McConnell sc->enable_ssu = MPS_SSU_ENABLE_SSD_DISABLE_HDD; 1582ef065d89SStephen McConnell sc->spinup_wait_time = DEFAULT_SPINUP_WAIT; 15834ab1cdc5SScott Long sc->use_phynum = 1; 15843c5ac992SScott Long sc->max_reqframes = MPS_REQ_FRAMES; 15853c5ac992SScott Long sc->max_prireqframes = MPS_PRI_REQ_FRAMES; 15863c5ac992SScott Long sc->max_replyframes = MPS_REPLY_FRAMES; 15873c5ac992SScott Long sc->max_evtframes = MPS_EVT_REPLY_FRAMES; 1588d3c7b9a0SKenneth D. Merry 1589d3c7b9a0SKenneth D. Merry /* 1590d043c564SKenneth D. Merry * Grab the global variables. 1591d3c7b9a0SKenneth D. Merry */ 1592867aa8cdSScott Long bzero(mps_debug, 80); 1593867aa8cdSScott Long if (TUNABLE_STR_FETCH("hw.mps.debug_level", mps_debug, 80) != 0) 1594867aa8cdSScott Long mps_parse_debug(sc, mps_debug); 1595d043c564SKenneth D. Merry TUNABLE_INT_FETCH("hw.mps.disable_msix", &sc->disable_msix); 1596d043c564SKenneth D. Merry TUNABLE_INT_FETCH("hw.mps.disable_msi", &sc->disable_msi); 15973c5ac992SScott Long TUNABLE_INT_FETCH("hw.mps.max_msix", &sc->max_msix); 1598d043c564SKenneth D. Merry TUNABLE_INT_FETCH("hw.mps.max_chains", &sc->max_chains); 159932b0a21eSStephen McConnell TUNABLE_INT_FETCH("hw.mps.max_io_pages", &sc->max_io_pages); 1600ef065d89SStephen McConnell TUNABLE_INT_FETCH("hw.mps.enable_ssu", &sc->enable_ssu); 1601ef065d89SStephen McConnell TUNABLE_INT_FETCH("hw.mps.spinup_wait_time", &sc->spinup_wait_time); 16024ab1cdc5SScott Long TUNABLE_INT_FETCH("hw.mps.use_phy_num", &sc->use_phynum); 16033c5ac992SScott Long TUNABLE_INT_FETCH("hw.mps.max_reqframes", &sc->max_reqframes); 16043c5ac992SScott Long TUNABLE_INT_FETCH("hw.mps.max_prireqframes", &sc->max_prireqframes); 16053c5ac992SScott Long TUNABLE_INT_FETCH("hw.mps.max_replyframes", &sc->max_replyframes); 16063c5ac992SScott Long TUNABLE_INT_FETCH("hw.mps.max_evtframes", &sc->max_evtframes); 1607d043c564SKenneth D. Merry 1608d043c564SKenneth D. Merry /* Grab the unit-instance variables */ 1609d043c564SKenneth D. Merry snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.debug_level", 1610d3c7b9a0SKenneth D. Merry device_get_unit(sc->mps_dev)); 1611867aa8cdSScott Long bzero(mps_debug, 80); 1612867aa8cdSScott Long if (TUNABLE_STR_FETCH(tmpstr, mps_debug, 80) != 0) 1613867aa8cdSScott Long mps_parse_debug(sc, mps_debug); 1614d043c564SKenneth D. Merry 1615d043c564SKenneth D. Merry snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msix", 16161476ba40SKenneth D. Merry device_get_unit(sc->mps_dev)); 1617d043c564SKenneth D. Merry TUNABLE_INT_FETCH(tmpstr, &sc->disable_msix); 1618d3c7b9a0SKenneth D. Merry 1619d043c564SKenneth D. Merry snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.disable_msi", 1620d043c564SKenneth D. Merry device_get_unit(sc->mps_dev)); 1621d043c564SKenneth D. Merry TUNABLE_INT_FETCH(tmpstr, &sc->disable_msi); 1622d3c7b9a0SKenneth D. Merry 16233c5ac992SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_msix", 16243c5ac992SScott Long device_get_unit(sc->mps_dev)); 16253c5ac992SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->max_msix); 16263c5ac992SScott Long 1627d043c564SKenneth D. Merry snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_chains", 1628d043c564SKenneth D. Merry device_get_unit(sc->mps_dev)); 1629d043c564SKenneth D. Merry TUNABLE_INT_FETCH(tmpstr, &sc->max_chains); 1630d9802debSScott Long 163132b0a21eSStephen McConnell snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_io_pages", 163232b0a21eSStephen McConnell device_get_unit(sc->mps_dev)); 163332b0a21eSStephen McConnell TUNABLE_INT_FETCH(tmpstr, &sc->max_io_pages); 163432b0a21eSStephen McConnell 1635d9802debSScott Long bzero(sc->exclude_ids, sizeof(sc->exclude_ids)); 1636d9802debSScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.exclude_ids", 1637d9802debSScott Long device_get_unit(sc->mps_dev)); 1638d9802debSScott Long TUNABLE_STR_FETCH(tmpstr, sc->exclude_ids, sizeof(sc->exclude_ids)); 1639ef065d89SStephen McConnell 1640ef065d89SStephen McConnell snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.enable_ssu", 1641ef065d89SStephen McConnell device_get_unit(sc->mps_dev)); 1642ef065d89SStephen McConnell TUNABLE_INT_FETCH(tmpstr, &sc->enable_ssu); 1643ef065d89SStephen McConnell 1644ef065d89SStephen McConnell snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.spinup_wait_time", 1645ef065d89SStephen McConnell device_get_unit(sc->mps_dev)); 1646ef065d89SStephen McConnell TUNABLE_INT_FETCH(tmpstr, &sc->spinup_wait_time); 16474ab1cdc5SScott Long 16484ab1cdc5SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.use_phy_num", 16494ab1cdc5SScott Long device_get_unit(sc->mps_dev)); 16504ab1cdc5SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->use_phynum); 16513c5ac992SScott Long 16523c5ac992SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_reqframes", 16533c5ac992SScott Long device_get_unit(sc->mps_dev)); 16543c5ac992SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->max_reqframes); 16553c5ac992SScott Long 16563c5ac992SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_prireqframes", 16573c5ac992SScott Long device_get_unit(sc->mps_dev)); 16583c5ac992SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->max_prireqframes); 16593c5ac992SScott Long 16603c5ac992SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_replyframes", 16613c5ac992SScott Long device_get_unit(sc->mps_dev)); 16623c5ac992SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->max_replyframes); 16633c5ac992SScott Long 16643c5ac992SScott Long snprintf(tmpstr, sizeof(tmpstr), "dev.mps.%d.max_evtframes", 16653c5ac992SScott Long device_get_unit(sc->mps_dev)); 16663c5ac992SScott Long TUNABLE_INT_FETCH(tmpstr, &sc->max_evtframes); 16673c5ac992SScott Long 1668d043c564SKenneth D. Merry } 1669d043c564SKenneth D. Merry 1670d043c564SKenneth D. Merry static void 1671d043c564SKenneth D. Merry mps_setup_sysctl(struct mps_softc *sc) 1672d043c564SKenneth D. Merry { 1673d043c564SKenneth D. Merry struct sysctl_ctx_list *sysctl_ctx = NULL; 1674d043c564SKenneth D. Merry struct sysctl_oid *sysctl_tree = NULL; 1675d043c564SKenneth D. Merry char tmpstr[80], tmpstr2[80]; 1676d3c7b9a0SKenneth D. Merry 1677d3c7b9a0SKenneth D. Merry /* 1678d3c7b9a0SKenneth D. Merry * Setup the sysctl variable so the user can change the debug level 1679d3c7b9a0SKenneth D. Merry * on the fly. 1680d3c7b9a0SKenneth D. Merry */ 1681d3c7b9a0SKenneth D. Merry snprintf(tmpstr, sizeof(tmpstr), "MPS controller %d", 1682d3c7b9a0SKenneth D. Merry device_get_unit(sc->mps_dev)); 1683d3c7b9a0SKenneth D. Merry snprintf(tmpstr2, sizeof(tmpstr2), "%d", device_get_unit(sc->mps_dev)); 1684d3c7b9a0SKenneth D. Merry 1685d043c564SKenneth D. Merry sysctl_ctx = device_get_sysctl_ctx(sc->mps_dev); 1686d043c564SKenneth D. Merry if (sysctl_ctx != NULL) 1687d043c564SKenneth D. Merry sysctl_tree = device_get_sysctl_tree(sc->mps_dev); 1688d043c564SKenneth D. Merry 1689d043c564SKenneth D. Merry if (sysctl_tree == NULL) { 1690d3c7b9a0SKenneth D. Merry sysctl_ctx_init(&sc->sysctl_ctx); 1691d3c7b9a0SKenneth D. Merry sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 1692d043c564SKenneth D. Merry SYSCTL_STATIC_CHILDREN(_hw_mps), OID_AUTO, tmpstr2, 16937029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, tmpstr); 1694d3c7b9a0SKenneth D. Merry if (sc->sysctl_tree == NULL) 1695d043c564SKenneth D. Merry return; 1696d043c564SKenneth D. Merry sysctl_ctx = &sc->sysctl_ctx; 1697d043c564SKenneth D. Merry sysctl_tree = sc->sysctl_tree; 1698d043c564SKenneth D. Merry } 1699d3c7b9a0SKenneth D. Merry 1700867aa8cdSScott Long SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1701cb242d7cSScott Long OID_AUTO, "debug_level", CTLTYPE_STRING | CTLFLAG_RW |CTLFLAG_MPSAFE, 1702cb242d7cSScott Long sc, 0, mps_debug_sysctl, "A", "mps debug level"); 1703d3c7b9a0SKenneth D. Merry 1704d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1705d043c564SKenneth D. Merry OID_AUTO, "disable_msix", CTLFLAG_RD, &sc->disable_msix, 0, 1706d043c564SKenneth D. Merry "Disable the use of MSI-X interrupts"); 17071476ba40SKenneth D. Merry 1708d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1709d043c564SKenneth D. Merry OID_AUTO, "disable_msi", CTLFLAG_RD, &sc->disable_msi, 0, 1710d043c564SKenneth D. Merry "Disable the use of MSI interrupts"); 1711d043c564SKenneth D. Merry 17123c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17133c5ac992SScott Long OID_AUTO, "max_msix", CTLFLAG_RD, &sc->max_msix, 0, 17143c5ac992SScott Long "User-defined maximum number of MSIX queues"); 17153c5ac992SScott Long 17163c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17173c5ac992SScott Long OID_AUTO, "msix_msgs", CTLFLAG_RD, &sc->msi_msgs, 0, 17183c5ac992SScott Long "Negotiated number of MSIX queues"); 17193c5ac992SScott Long 17203c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17213c5ac992SScott Long OID_AUTO, "max_reqframes", CTLFLAG_RD, &sc->max_reqframes, 0, 17223c5ac992SScott Long "Total number of allocated request frames"); 17233c5ac992SScott Long 17243c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17253c5ac992SScott Long OID_AUTO, "max_prireqframes", CTLFLAG_RD, &sc->max_prireqframes, 0, 17263c5ac992SScott Long "Total number of allocated high priority request frames"); 17273c5ac992SScott Long 17283c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17293c5ac992SScott Long OID_AUTO, "max_replyframes", CTLFLAG_RD, &sc->max_replyframes, 0, 17303c5ac992SScott Long "Total number of allocated reply frames"); 17313c5ac992SScott Long 17323c5ac992SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17333c5ac992SScott Long OID_AUTO, "max_evtframes", CTLFLAG_RD, &sc->max_evtframes, 0, 17343c5ac992SScott Long "Total number of event frames allocated"); 17353c5ac992SScott Long 1736d043c564SKenneth D. Merry SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 173769e85eb8SScott Long OID_AUTO, "firmware_version", CTLFLAG_RD, sc->fw_version, 1738d043c564SKenneth D. Merry strlen(sc->fw_version), "firmware version"); 1739d043c564SKenneth D. Merry 1740d043c564SKenneth D. Merry SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 174169e85eb8SScott Long OID_AUTO, "driver_version", CTLFLAG_RD, MPS_DRIVER_VERSION, 1742d043c564SKenneth D. Merry strlen(MPS_DRIVER_VERSION), "driver version"); 1743d043c564SKenneth D. Merry 174469e85eb8SScott Long SYSCTL_ADD_STRING(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 174569e85eb8SScott Long OID_AUTO, "msg_version", CTLFLAG_RD, sc->msg_version, 17467d154c4dSAlan Somers strlen(sc->msg_version), "message interface version (deprecated)"); 174769e85eb8SScott Long 1748d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1749550e2acdSKenneth D. Merry OID_AUTO, "io_cmds_active", CTLFLAG_RD, 1750550e2acdSKenneth D. Merry &sc->io_cmds_active, 0, "number of currently active commands"); 1751550e2acdSKenneth D. Merry 1752d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1753550e2acdSKenneth D. Merry OID_AUTO, "io_cmds_highwater", CTLFLAG_RD, 1754550e2acdSKenneth D. Merry &sc->io_cmds_highwater, 0, "maximum active commands seen"); 1755550e2acdSKenneth D. Merry 1756d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1757550e2acdSKenneth D. Merry OID_AUTO, "chain_free", CTLFLAG_RD, 1758550e2acdSKenneth D. Merry &sc->chain_free, 0, "number of free chain elements"); 1759550e2acdSKenneth D. Merry 1760d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1761550e2acdSKenneth D. Merry OID_AUTO, "chain_free_lowwater", CTLFLAG_RD, 1762550e2acdSKenneth D. Merry &sc->chain_free_lowwater, 0,"lowest number of free chain elements"); 1763550e2acdSKenneth D. Merry 1764d043c564SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1765d043c564SKenneth D. Merry OID_AUTO, "max_chains", CTLFLAG_RD, 1766d043c564SKenneth D. Merry &sc->max_chains, 0,"maximum chain frames that will be allocated"); 1767d043c564SKenneth D. Merry 1768ef065d89SStephen McConnell SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 176932b0a21eSStephen McConnell OID_AUTO, "max_io_pages", CTLFLAG_RD, 177032b0a21eSStephen McConnell &sc->max_io_pages, 0,"maximum pages to allow per I/O (if <1 use " 177132b0a21eSStephen McConnell "IOCFacts)"); 177232b0a21eSStephen McConnell 177332b0a21eSStephen McConnell SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1774ef065d89SStephen McConnell OID_AUTO, "enable_ssu", CTLFLAG_RW, &sc->enable_ssu, 0, 1775ef065d89SStephen McConnell "enable SSU to SATA SSD/HDD at shutdown"); 1776ef065d89SStephen McConnell 1777d043c564SKenneth D. Merry SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1778550e2acdSKenneth D. Merry OID_AUTO, "chain_alloc_fail", CTLFLAG_RD, 1779550e2acdSKenneth D. Merry &sc->chain_alloc_fail, "chain allocation failures"); 1780ef065d89SStephen McConnell 1781ef065d89SStephen McConnell SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1782ef065d89SStephen McConnell OID_AUTO, "spinup_wait_time", CTLFLAG_RD, 1783ef065d89SStephen McConnell &sc->spinup_wait_time, DEFAULT_SPINUP_WAIT, "seconds to wait for " 1784ef065d89SStephen McConnell "spinup after SATA ID error"); 1785ee5c196bSScott Long 1786ee5c196bSScott Long SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17877029da5cSPawel Biernacki OID_AUTO, "mapping_table_dump", 1788b776de67SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 1789ee5c196bSScott Long mps_mapping_dump, "A", "Mapping Table Dump"); 1790ee5c196bSScott Long 1791ee5c196bSScott Long SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17927029da5cSPawel Biernacki OID_AUTO, "encl_table_dump", 1793b776de67SAlexander Motin CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 1794ee5c196bSScott Long mps_mapping_encl_dump, "A", "Enclosure Table Dump"); 17954ab1cdc5SScott Long 1796cf6ea6f2SScott Long SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 17977029da5cSPawel Biernacki OID_AUTO, "dump_reqs", 1798b776de67SAlexander Motin CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, 17997029da5cSPawel Biernacki sc, 0, mps_dump_reqs, "I", "Dump Active Requests"); 1800cf6ea6f2SScott Long 18014ab1cdc5SScott Long SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 1802175ad3d0SKenneth D. Merry OID_AUTO, "dump_reqs_alltypes", CTLFLAG_RW, 1803175ad3d0SKenneth D. Merry &sc->dump_reqs_alltypes, 0, 1804175ad3d0SKenneth D. Merry "dump all request types not just inqueue"); 1805175ad3d0SKenneth D. Merry 1806175ad3d0SKenneth D. Merry SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), 18074ab1cdc5SScott Long OID_AUTO, "use_phy_num", CTLFLAG_RD, &sc->use_phynum, 0, 18084ab1cdc5SScott Long "Use the phy number for enumeration"); 1809d043c564SKenneth D. Merry } 1810550e2acdSKenneth D. Merry 1811cb242d7cSScott Long static struct mps_debug_string { 1812867aa8cdSScott Long char *name; 1813867aa8cdSScott Long int flag; 1814867aa8cdSScott Long } mps_debug_strings[] = { 1815867aa8cdSScott Long {"info", MPS_INFO}, 1816867aa8cdSScott Long {"fault", MPS_FAULT}, 1817867aa8cdSScott Long {"event", MPS_EVENT}, 1818867aa8cdSScott Long {"log", MPS_LOG}, 1819867aa8cdSScott Long {"recovery", MPS_RECOVERY}, 1820867aa8cdSScott Long {"error", MPS_ERROR}, 1821867aa8cdSScott Long {"init", MPS_INIT}, 1822867aa8cdSScott Long {"xinfo", MPS_XINFO}, 1823867aa8cdSScott Long {"user", MPS_USER}, 1824867aa8cdSScott Long {"mapping", MPS_MAPPING}, 1825867aa8cdSScott Long {"trace", MPS_TRACE} 1826867aa8cdSScott Long }; 1827867aa8cdSScott Long 1828cfd6fd5aSScott Long enum mps_debug_level_combiner { 1829cfd6fd5aSScott Long COMB_NONE, 1830cfd6fd5aSScott Long COMB_ADD, 1831cfd6fd5aSScott Long COMB_SUB 1832cfd6fd5aSScott Long }; 1833cfd6fd5aSScott Long 1834867aa8cdSScott Long static int 1835867aa8cdSScott Long mps_debug_sysctl(SYSCTL_HANDLER_ARGS) 1836867aa8cdSScott Long { 1837867aa8cdSScott Long struct mps_softc *sc; 1838867aa8cdSScott Long struct mps_debug_string *string; 1839cb242d7cSScott Long struct sbuf *sbuf; 1840867aa8cdSScott Long char *buffer; 1841867aa8cdSScott Long size_t sz; 1842867aa8cdSScott Long int i, len, debug, error; 1843867aa8cdSScott Long 1844867aa8cdSScott Long sc = (struct mps_softc *)arg1; 1845867aa8cdSScott Long 1846867aa8cdSScott Long error = sysctl_wire_old_buffer(req, 0); 1847867aa8cdSScott Long if (error != 0) 1848867aa8cdSScott Long return (error); 1849867aa8cdSScott Long 1850cb242d7cSScott Long sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); 1851867aa8cdSScott Long debug = sc->mps_debug; 1852867aa8cdSScott Long 1853cb242d7cSScott Long sbuf_printf(sbuf, "%#x", debug); 1854867aa8cdSScott Long 1855867aa8cdSScott Long sz = sizeof(mps_debug_strings) / sizeof(mps_debug_strings[0]); 1856867aa8cdSScott Long for (i = 0; i < sz; i++) { 1857867aa8cdSScott Long string = &mps_debug_strings[i]; 1858867aa8cdSScott Long if (debug & string->flag) 1859cb242d7cSScott Long sbuf_printf(sbuf, ",%s", string->name); 1860867aa8cdSScott Long } 1861867aa8cdSScott Long 1862cb242d7cSScott Long error = sbuf_finish(sbuf); 1863cb242d7cSScott Long sbuf_delete(sbuf); 1864867aa8cdSScott Long 1865867aa8cdSScott Long if (error || req->newptr == NULL) 1866867aa8cdSScott Long return (error); 1867867aa8cdSScott Long 1868867aa8cdSScott Long len = req->newlen - req->newidx; 1869867aa8cdSScott Long if (len == 0) 1870867aa8cdSScott Long return (0); 1871867aa8cdSScott Long 1872867aa8cdSScott Long buffer = malloc(len, M_MPT2, M_ZERO|M_WAITOK); 1873867aa8cdSScott Long error = SYSCTL_IN(req, buffer, len); 1874867aa8cdSScott Long 1875867aa8cdSScott Long mps_parse_debug(sc, buffer); 1876867aa8cdSScott Long 1877867aa8cdSScott Long free(buffer, M_MPT2); 1878867aa8cdSScott Long return (error); 1879867aa8cdSScott Long } 1880867aa8cdSScott Long 1881867aa8cdSScott Long static void 1882867aa8cdSScott Long mps_parse_debug(struct mps_softc *sc, char *list) 1883867aa8cdSScott Long { 1884867aa8cdSScott Long struct mps_debug_string *string; 1885cfd6fd5aSScott Long enum mps_debug_level_combiner op; 1886867aa8cdSScott Long char *token, *endtoken; 1887867aa8cdSScott Long size_t sz; 1888867aa8cdSScott Long int flags, i; 1889867aa8cdSScott Long 1890867aa8cdSScott Long if (list == NULL || *list == '\0') 1891867aa8cdSScott Long return; 1892867aa8cdSScott Long 1893cfd6fd5aSScott Long if (*list == '+') { 1894cfd6fd5aSScott Long op = COMB_ADD; 1895cfd6fd5aSScott Long list++; 1896cfd6fd5aSScott Long } else if (*list == '-') { 1897cfd6fd5aSScott Long op = COMB_SUB; 1898cfd6fd5aSScott Long list++; 1899cfd6fd5aSScott Long } else 1900cfd6fd5aSScott Long op = COMB_NONE; 1901cfd6fd5aSScott Long if (*list == '\0') 1902cfd6fd5aSScott Long return; 1903cfd6fd5aSScott Long 1904867aa8cdSScott Long flags = 0; 1905867aa8cdSScott Long sz = sizeof(mps_debug_strings) / sizeof(mps_debug_strings[0]); 1906867aa8cdSScott Long while ((token = strsep(&list, ":,")) != NULL) { 1907867aa8cdSScott Long /* Handle integer flags */ 1908867aa8cdSScott Long flags |= strtol(token, &endtoken, 0); 1909867aa8cdSScott Long if (token != endtoken) 1910867aa8cdSScott Long continue; 1911867aa8cdSScott Long 1912867aa8cdSScott Long /* Handle text flags */ 1913867aa8cdSScott Long for (i = 0; i < sz; i++) { 1914867aa8cdSScott Long string = &mps_debug_strings[i]; 1915867aa8cdSScott Long if (strcasecmp(token, string->name) == 0) { 1916867aa8cdSScott Long flags |= string->flag; 1917867aa8cdSScott Long break; 1918867aa8cdSScott Long } 1919867aa8cdSScott Long } 1920867aa8cdSScott Long } 1921867aa8cdSScott Long 1922cfd6fd5aSScott Long switch (op) { 1923cfd6fd5aSScott Long case COMB_NONE: 1924867aa8cdSScott Long sc->mps_debug = flags; 1925cfd6fd5aSScott Long break; 1926cfd6fd5aSScott Long case COMB_ADD: 1927cfd6fd5aSScott Long sc->mps_debug |= flags; 1928cfd6fd5aSScott Long break; 1929cfd6fd5aSScott Long case COMB_SUB: 1930cfd6fd5aSScott Long sc->mps_debug &= (~flags); 1931cfd6fd5aSScott Long break; 1932cfd6fd5aSScott Long } 1933cfd6fd5aSScott Long 1934867aa8cdSScott Long return; 1935867aa8cdSScott Long } 1936867aa8cdSScott Long 1937cf6ea6f2SScott Long struct mps_dumpreq_hdr { 1938cf6ea6f2SScott Long uint32_t smid; 1939cf6ea6f2SScott Long uint32_t state; 1940cf6ea6f2SScott Long uint32_t numframes; 1941cf6ea6f2SScott Long uint32_t deschi; 1942cf6ea6f2SScott Long uint32_t desclo; 1943cf6ea6f2SScott Long }; 1944cf6ea6f2SScott Long 1945cf6ea6f2SScott Long static int 1946cf6ea6f2SScott Long mps_dump_reqs(SYSCTL_HANDLER_ARGS) 1947cf6ea6f2SScott Long { 1948cf6ea6f2SScott Long struct mps_softc *sc; 1949cf6ea6f2SScott Long struct mps_chain *chain, *chain1; 1950cf6ea6f2SScott Long struct mps_command *cm; 1951cf6ea6f2SScott Long struct mps_dumpreq_hdr hdr; 1952cf6ea6f2SScott Long struct sbuf *sb; 1953cf6ea6f2SScott Long uint32_t smid, state; 1954cf6ea6f2SScott Long int i, numreqs, error = 0; 1955cf6ea6f2SScott Long 1956cf6ea6f2SScott Long sc = (struct mps_softc *)arg1; 1957cf6ea6f2SScott Long 1958cf6ea6f2SScott Long if ((error = priv_check(curthread, PRIV_DRIVER)) != 0) { 1959cf6ea6f2SScott Long printf("priv check error %d\n", error); 1960cf6ea6f2SScott Long return (error); 1961cf6ea6f2SScott Long } 1962cf6ea6f2SScott Long 1963cf6ea6f2SScott Long state = MPS_CM_STATE_INQUEUE; 1964cf6ea6f2SScott Long smid = 1; 1965cf6ea6f2SScott Long numreqs = sc->num_reqs; 1966cf6ea6f2SScott Long 1967cf6ea6f2SScott Long if (req->newptr != NULL) 1968cf6ea6f2SScott Long return (EINVAL); 1969cf6ea6f2SScott Long 1970cf6ea6f2SScott Long if (smid == 0 || smid > sc->num_reqs) 1971cf6ea6f2SScott Long return (EINVAL); 1972cf6ea6f2SScott Long if (numreqs <= 0 || (numreqs + smid > sc->num_reqs)) 1973cf6ea6f2SScott Long numreqs = sc->num_reqs; 1974cf6ea6f2SScott Long sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req); 1975cf6ea6f2SScott Long 1976cf6ea6f2SScott Long /* Best effort, no locking */ 1977cf6ea6f2SScott Long for (i = smid; i < numreqs; i++) { 1978cf6ea6f2SScott Long cm = &sc->commands[i]; 1979175ad3d0SKenneth D. Merry if ((sc->dump_reqs_alltypes == 0) && (cm->cm_state != state)) 1980cf6ea6f2SScott Long continue; 1981cf6ea6f2SScott Long hdr.smid = i; 1982cf6ea6f2SScott Long hdr.state = cm->cm_state; 1983cf6ea6f2SScott Long hdr.numframes = 1; 1984cf6ea6f2SScott Long hdr.deschi = cm->cm_desc.Words.High; 1985cf6ea6f2SScott Long hdr.desclo = cm->cm_desc.Words.Low; 1986cf6ea6f2SScott Long TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, 1987cf6ea6f2SScott Long chain1) 1988cf6ea6f2SScott Long hdr.numframes++; 1989cf6ea6f2SScott Long sbuf_bcat(sb, &hdr, sizeof(hdr)); 1990cf6ea6f2SScott Long sbuf_bcat(sb, cm->cm_req, 128); 1991cf6ea6f2SScott Long TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, 1992cf6ea6f2SScott Long chain1) 1993cf6ea6f2SScott Long sbuf_bcat(sb, chain->chain, 128); 1994cf6ea6f2SScott Long } 1995cf6ea6f2SScott Long 1996cf6ea6f2SScott Long error = sbuf_finish(sb); 1997cf6ea6f2SScott Long sbuf_delete(sb); 1998cf6ea6f2SScott Long return (error); 1999cf6ea6f2SScott Long } 2000cf6ea6f2SScott Long 2001d043c564SKenneth D. Merry int 2002d043c564SKenneth D. Merry mps_attach(struct mps_softc *sc) 2003d043c564SKenneth D. Merry { 20049b91b192SKenneth D. Merry int error; 2005d043c564SKenneth D. Merry 20061610f95cSScott Long MPS_FUNCTRACE(sc); 2007757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 2008d043c564SKenneth D. Merry 2009d043c564SKenneth D. Merry mtx_init(&sc->mps_mtx, "MPT2SAS lock", NULL, MTX_DEF); 2010d043c564SKenneth D. Merry callout_init_mtx(&sc->periodic, &sc->mps_mtx, 0); 2011635e58c7SStephen McConnell callout_init_mtx(&sc->device_check_callout, &sc->mps_mtx, 0); 2012d043c564SKenneth D. Merry TAILQ_INIT(&sc->event_list); 2013fe839103SScott Long timevalclear(&sc->lastfail); 2014d043c564SKenneth D. Merry 2015d043c564SKenneth D. Merry if ((error = mps_transition_ready(sc)) != 0) { 2016757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "failed to transition " 2017757ff642SScott Long "ready\n"); 2018d3c7b9a0SKenneth D. Merry return (error); 2019d043c564SKenneth D. Merry } 2020d3c7b9a0SKenneth D. Merry 2021d3c7b9a0SKenneth D. Merry sc->facts = malloc(sizeof(MPI2_IOC_FACTS_REPLY), M_MPT2, 2022d3c7b9a0SKenneth D. Merry M_ZERO|M_NOWAIT); 2023be4aa869SKenneth D. Merry if(!sc->facts) { 2024757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "Cannot allocate memory, " 2025757ff642SScott Long "exit\n"); 2026be4aa869SKenneth D. Merry return (ENOMEM); 2027be4aa869SKenneth D. Merry } 2028d3c7b9a0SKenneth D. Merry 2029d3c7b9a0SKenneth D. Merry /* 20309b91b192SKenneth D. Merry * Get IOC Facts and allocate all structures based on this information. 20319b91b192SKenneth D. Merry * A Diag Reset will also call mps_iocfacts_allocate and re-read the IOC 20329b91b192SKenneth D. Merry * Facts. If relevant values have changed in IOC Facts, this function 20339b91b192SKenneth D. Merry * will free all of the memory based on IOC Facts and reallocate that 20349b91b192SKenneth D. Merry * memory. If this fails, any allocated memory should already be freed. 2035d3c7b9a0SKenneth D. Merry */ 20369b91b192SKenneth D. Merry if ((error = mps_iocfacts_allocate(sc, TRUE)) != 0) { 2037757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "IOC Facts based allocation " 2038757ff642SScott Long "failed with error %d, exit\n", error); 2039d3c7b9a0SKenneth D. Merry return (error); 2040d3c7b9a0SKenneth D. Merry } 2041d3c7b9a0SKenneth D. Merry 2042d3c7b9a0SKenneth D. Merry /* Start the periodic watchdog check on the IOC Doorbell */ 2043d3c7b9a0SKenneth D. Merry mps_periodic(sc); 2044d3c7b9a0SKenneth D. Merry 2045d3c7b9a0SKenneth D. Merry /* 2046d3c7b9a0SKenneth D. Merry * The portenable will kick off discovery events that will drive the 2047d3c7b9a0SKenneth D. Merry * rest of the initialization process. The CAM/SAS module will 2048d3c7b9a0SKenneth D. Merry * hold up the boot sequence until discovery is complete. 2049d3c7b9a0SKenneth D. Merry */ 2050d3c7b9a0SKenneth D. Merry sc->mps_ich.ich_func = mps_startup; 2051d3c7b9a0SKenneth D. Merry sc->mps_ich.ich_arg = sc; 2052d3c7b9a0SKenneth D. Merry if (config_intrhook_establish(&sc->mps_ich) != 0) { 2053757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_ERROR, 2054757ff642SScott Long "Cannot establish MPS config hook\n"); 2055d3c7b9a0SKenneth D. Merry error = EINVAL; 2056d3c7b9a0SKenneth D. Merry } 2057d3c7b9a0SKenneth D. Merry 2058d043c564SKenneth D. Merry /* 2059d043c564SKenneth D. Merry * Allow IR to shutdown gracefully when shutdown occurs. 2060d043c564SKenneth D. Merry */ 2061d043c564SKenneth D. Merry sc->shutdown_eh = EVENTHANDLER_REGISTER(shutdown_final, 2062d043c564SKenneth D. Merry mpssas_ir_shutdown, sc, SHUTDOWN_PRI_DEFAULT); 2063d043c564SKenneth D. Merry 2064d043c564SKenneth D. Merry if (sc->shutdown_eh == NULL) 2065757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_ERROR, 2066757ff642SScott Long "shutdown event registration failed\n"); 2067d043c564SKenneth D. Merry 2068d043c564SKenneth D. Merry mps_setup_sysctl(sc); 2069d043c564SKenneth D. Merry 2070550e2acdSKenneth D. Merry sc->mps_flags |= MPS_FLAGS_ATTACH_DONE; 2071757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit error= %d\n", __func__, error); 2072550e2acdSKenneth D. Merry 2073d3c7b9a0SKenneth D. Merry return (error); 2074d3c7b9a0SKenneth D. Merry } 2075d3c7b9a0SKenneth D. Merry 2076d043c564SKenneth D. Merry /* Run through any late-start handlers. */ 2077d3c7b9a0SKenneth D. Merry static void 2078d3c7b9a0SKenneth D. Merry mps_startup(void *arg) 2079d3c7b9a0SKenneth D. Merry { 2080d3c7b9a0SKenneth D. Merry struct mps_softc *sc; 2081d3c7b9a0SKenneth D. Merry 2082d3c7b9a0SKenneth D. Merry sc = (struct mps_softc *)arg; 2083757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 2084d3c7b9a0SKenneth D. Merry 2085d3c7b9a0SKenneth D. Merry mps_lock(sc); 2086d3c7b9a0SKenneth D. Merry mps_unmask_intr(sc); 20879b91b192SKenneth D. Merry 2088d043c564SKenneth D. Merry /* initialize device mapping tables */ 20899b91b192SKenneth D. Merry mps_base_static_config_pages(sc); 2090d043c564SKenneth D. Merry mps_mapping_initialize(sc); 2091d043c564SKenneth D. Merry mpssas_startup(sc); 2092d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2093a4bb51a4SScott Long 2094a4bb51a4SScott Long mps_dprint(sc, MPS_INIT, "disestablish config intrhook\n"); 2095a4bb51a4SScott Long config_intrhook_disestablish(&sc->mps_ich); 2096a4bb51a4SScott Long sc->mps_ich.ich_arg = NULL; 2097a4bb51a4SScott Long 2098757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 2099d3c7b9a0SKenneth D. Merry } 2100d3c7b9a0SKenneth D. Merry 2101d3c7b9a0SKenneth D. Merry /* Periodic watchdog. Is called with the driver lock already held. */ 2102d3c7b9a0SKenneth D. Merry static void 2103d3c7b9a0SKenneth D. Merry mps_periodic(void *arg) 2104d3c7b9a0SKenneth D. Merry { 2105d3c7b9a0SKenneth D. Merry struct mps_softc *sc; 2106d3c7b9a0SKenneth D. Merry uint32_t db; 2107d3c7b9a0SKenneth D. Merry 2108d3c7b9a0SKenneth D. Merry sc = (struct mps_softc *)arg; 2109d3c7b9a0SKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_SHUTDOWN) 2110d3c7b9a0SKenneth D. Merry return; 2111d3c7b9a0SKenneth D. Merry 2112d3c7b9a0SKenneth D. Merry db = mps_regread(sc, MPI2_DOORBELL_OFFSET); 2113d3c7b9a0SKenneth D. Merry if ((db & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { 21141610f95cSScott Long mps_dprint(sc, MPS_FAULT, "IOC Fault 0x%08x, Resetting\n", db); 2115d043c564SKenneth D. Merry mps_reinit(sc); 2116d3c7b9a0SKenneth D. Merry } 2117d3c7b9a0SKenneth D. Merry 21181849bc5fSAlexander Motin callout_reset_sbt(&sc->periodic, MPS_PERIODIC_DELAY * SBT_1S, 0, 21191849bc5fSAlexander Motin mps_periodic, sc, C_PREL(1)); 2120d3c7b9a0SKenneth D. Merry } 2121d3c7b9a0SKenneth D. Merry 2122d3c7b9a0SKenneth D. Merry static void 2123d3c7b9a0SKenneth D. Merry mps_log_evt_handler(struct mps_softc *sc, uintptr_t data, 2124d3c7b9a0SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REPLY *event) 2125d3c7b9a0SKenneth D. Merry { 2126d3c7b9a0SKenneth D. Merry MPI2_EVENT_DATA_LOG_ENTRY_ADDED *entry; 2127d3c7b9a0SKenneth D. Merry 2128055e2653SScott Long MPS_DPRINT_EVENT(sc, generic, event); 2129d3c7b9a0SKenneth D. Merry 2130d3c7b9a0SKenneth D. Merry switch (event->Event) { 2131d3c7b9a0SKenneth D. Merry case MPI2_EVENT_LOG_DATA: 21321610f95cSScott Long mps_dprint(sc, MPS_EVENT, "MPI2_EVENT_LOG_DATA:\n"); 21331610f95cSScott Long if (sc->mps_debug & MPS_EVENT) 2134d3c7b9a0SKenneth D. Merry hexdump(event->EventData, event->EventDataLength, NULL, 0); 2135d3c7b9a0SKenneth D. Merry break; 2136d3c7b9a0SKenneth D. Merry case MPI2_EVENT_LOG_ENTRY_ADDED: 2137d3c7b9a0SKenneth D. Merry entry = (MPI2_EVENT_DATA_LOG_ENTRY_ADDED *)event->EventData; 21381610f95cSScott Long mps_dprint(sc, MPS_EVENT, "MPI2_EVENT_LOG_ENTRY_ADDED event " 2139d3c7b9a0SKenneth D. Merry "0x%x Sequence %d:\n", entry->LogEntryQualifier, 2140d3c7b9a0SKenneth D. Merry entry->LogSequence); 2141d3c7b9a0SKenneth D. Merry break; 2142d3c7b9a0SKenneth D. Merry default: 2143d3c7b9a0SKenneth D. Merry break; 2144d3c7b9a0SKenneth D. Merry } 2145d3c7b9a0SKenneth D. Merry return; 2146d3c7b9a0SKenneth D. Merry } 2147d3c7b9a0SKenneth D. Merry 2148d3c7b9a0SKenneth D. Merry static int 2149d3c7b9a0SKenneth D. Merry mps_attach_log(struct mps_softc *sc) 2150d3c7b9a0SKenneth D. Merry { 2151be4aa869SKenneth D. Merry u32 events[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; 2152d3c7b9a0SKenneth D. Merry 2153d3c7b9a0SKenneth D. Merry bzero(events, 16); 2154d3c7b9a0SKenneth D. Merry setbit(events, MPI2_EVENT_LOG_DATA); 2155d3c7b9a0SKenneth D. Merry setbit(events, MPI2_EVENT_LOG_ENTRY_ADDED); 2156d3c7b9a0SKenneth D. Merry 2157d3c7b9a0SKenneth D. Merry mps_register_events(sc, events, mps_log_evt_handler, NULL, 2158d3c7b9a0SKenneth D. Merry &sc->mps_log_eh); 2159d3c7b9a0SKenneth D. Merry 2160d3c7b9a0SKenneth D. Merry return (0); 2161d3c7b9a0SKenneth D. Merry } 2162d3c7b9a0SKenneth D. Merry 2163d3c7b9a0SKenneth D. Merry static int 2164d3c7b9a0SKenneth D. Merry mps_detach_log(struct mps_softc *sc) 2165d3c7b9a0SKenneth D. Merry { 2166d3c7b9a0SKenneth D. Merry 2167d3c7b9a0SKenneth D. Merry if (sc->mps_log_eh != NULL) 2168d3c7b9a0SKenneth D. Merry mps_deregister_events(sc, sc->mps_log_eh); 2169d3c7b9a0SKenneth D. Merry return (0); 2170d3c7b9a0SKenneth D. Merry } 2171d3c7b9a0SKenneth D. Merry 2172d3c7b9a0SKenneth D. Merry /* 2173d3c7b9a0SKenneth D. Merry * Free all of the driver resources and detach submodules. Should be called 2174d3c7b9a0SKenneth D. Merry * without the lock held. 2175d3c7b9a0SKenneth D. Merry */ 2176d3c7b9a0SKenneth D. Merry int 2177d3c7b9a0SKenneth D. Merry mps_free(struct mps_softc *sc) 2178d3c7b9a0SKenneth D. Merry { 21799b91b192SKenneth D. Merry int error; 2180d3c7b9a0SKenneth D. Merry 2181757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s entered\n", __func__); 2182d3c7b9a0SKenneth D. Merry /* Turn off the watchdog */ 2183d3c7b9a0SKenneth D. Merry mps_lock(sc); 2184d3c7b9a0SKenneth D. Merry sc->mps_flags |= MPS_FLAGS_SHUTDOWN; 2185d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2186d3c7b9a0SKenneth D. Merry /* Lock must not be held for this */ 2187d3c7b9a0SKenneth D. Merry callout_drain(&sc->periodic); 2188635e58c7SStephen McConnell callout_drain(&sc->device_check_callout); 2189d3c7b9a0SKenneth D. Merry 2190d3c7b9a0SKenneth D. Merry if (((error = mps_detach_log(sc)) != 0) || 2191757ff642SScott Long ((error = mps_detach_sas(sc)) != 0)) { 2192757ff642SScott Long mps_dprint(sc, MPS_INIT|MPS_FAULT, "failed to detach " 2193757ff642SScott Long "subsystems, exit\n"); 2194d3c7b9a0SKenneth D. Merry return (error); 2195757ff642SScott Long } 2196d3c7b9a0SKenneth D. Merry 2197653c521fSKenneth D. Merry mps_detach_user(sc); 2198653c521fSKenneth D. Merry 2199d3c7b9a0SKenneth D. Merry /* Put the IOC back in the READY state. */ 2200d3c7b9a0SKenneth D. Merry mps_lock(sc); 2201d043c564SKenneth D. Merry if ((error = mps_transition_ready(sc)) != 0) { 2202d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2203d3c7b9a0SKenneth D. Merry return (error); 2204d3c7b9a0SKenneth D. Merry } 2205d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2206d3c7b9a0SKenneth D. Merry 2207d3c7b9a0SKenneth D. Merry if (sc->facts != NULL) 2208d3c7b9a0SKenneth D. Merry free(sc->facts, M_MPT2); 2209d3c7b9a0SKenneth D. Merry 22109b91b192SKenneth D. Merry /* 22119b91b192SKenneth D. Merry * Free all buffers that are based on IOC Facts. A Diag Reset may need 22129b91b192SKenneth D. Merry * to free these buffers too. 22139b91b192SKenneth D. Merry */ 22149b91b192SKenneth D. Merry mps_iocfacts_free(sc); 2215d3c7b9a0SKenneth D. Merry 2216d3c7b9a0SKenneth D. Merry if (sc->sysctl_tree != NULL) 2217d3c7b9a0SKenneth D. Merry sysctl_ctx_free(&sc->sysctl_ctx); 2218d3c7b9a0SKenneth D. Merry 2219d043c564SKenneth D. Merry /* Deregister the shutdown function */ 2220d043c564SKenneth D. Merry if (sc->shutdown_eh != NULL) 2221d043c564SKenneth D. Merry EVENTHANDLER_DEREGISTER(shutdown_final, sc->shutdown_eh); 2222d043c564SKenneth D. Merry 2223d3c7b9a0SKenneth D. Merry mtx_destroy(&sc->mps_mtx); 2224757ff642SScott Long mps_dprint(sc, MPS_INIT, "%s exit\n", __func__); 2225d3c7b9a0SKenneth D. Merry 2226d3c7b9a0SKenneth D. Merry return (0); 2227d3c7b9a0SKenneth D. Merry } 2228d3c7b9a0SKenneth D. Merry 2229550e2acdSKenneth D. Merry static __inline void 22301610f95cSScott Long mps_complete_command(struct mps_softc *sc, struct mps_command *cm) 2231550e2acdSKenneth D. Merry { 22321610f95cSScott Long MPS_FUNCTRACE(sc); 22331610f95cSScott Long 22341610f95cSScott Long if (cm == NULL) { 22351610f95cSScott Long mps_dprint(sc, MPS_ERROR, "Completing NULL command\n"); 22361610f95cSScott Long return; 22371610f95cSScott Long } 22381610f95cSScott Long 2239175ad3d0SKenneth D. Merry KASSERT(cm->cm_state == MPS_CM_STATE_INQUEUE, 2240175ad3d0SKenneth D. Merry ("command not inqueue, state = %u\n", cm->cm_state)); 2241175ad3d0SKenneth D. Merry cm->cm_state = MPS_CM_STATE_BUSY; 2242550e2acdSKenneth D. Merry if (cm->cm_flags & MPS_CM_FLAGS_POLLED) 2243550e2acdSKenneth D. Merry cm->cm_flags |= MPS_CM_FLAGS_COMPLETE; 2244550e2acdSKenneth D. Merry 2245d043c564SKenneth D. Merry if (cm->cm_complete != NULL) { 22461610f95cSScott Long mps_dprint(sc, MPS_TRACE, 2247d043c564SKenneth D. Merry "%s cm %p calling cm_complete %p data %p reply %p\n", 2248d043c564SKenneth D. Merry __func__, cm, cm->cm_complete, cm->cm_complete_data, 2249d043c564SKenneth D. Merry cm->cm_reply); 22501610f95cSScott Long cm->cm_complete(sc, cm); 2251d043c564SKenneth D. Merry } 2252550e2acdSKenneth D. Merry 2253550e2acdSKenneth D. Merry if (cm->cm_flags & MPS_CM_FLAGS_WAKEUP) { 22541610f95cSScott Long mps_dprint(sc, MPS_TRACE, "waking up %p\n", cm); 2255550e2acdSKenneth D. Merry wakeup(cm); 2256550e2acdSKenneth D. Merry } 2257d043c564SKenneth D. Merry 2258d043c564SKenneth D. Merry if (cm->cm_sc->io_cmds_active != 0) { 2259d043c564SKenneth D. Merry cm->cm_sc->io_cmds_active--; 2260d043c564SKenneth D. Merry } else { 22611610f95cSScott Long mps_dprint(sc, MPS_ERROR, "Warning: io_cmds_active is " 2262d043c564SKenneth D. Merry "out of sync - resynching to 0\n"); 2263d043c564SKenneth D. Merry } 2264550e2acdSKenneth D. Merry } 2265550e2acdSKenneth D. Merry 2266be4aa869SKenneth D. Merry static void 2267be4aa869SKenneth D. Merry mps_sas_log_info(struct mps_softc *sc , u32 log_info) 2268be4aa869SKenneth D. Merry { 2269be4aa869SKenneth D. Merry union loginfo_type { 2270be4aa869SKenneth D. Merry u32 loginfo; 2271be4aa869SKenneth D. Merry struct { 2272be4aa869SKenneth D. Merry u32 subcode:16; 2273be4aa869SKenneth D. Merry u32 code:8; 2274be4aa869SKenneth D. Merry u32 originator:4; 2275be4aa869SKenneth D. Merry u32 bus_type:4; 2276be4aa869SKenneth D. Merry } dw; 2277be4aa869SKenneth D. Merry }; 2278be4aa869SKenneth D. Merry union loginfo_type sas_loginfo; 2279be4aa869SKenneth D. Merry char *originator_str = NULL; 2280be4aa869SKenneth D. Merry 2281be4aa869SKenneth D. Merry sas_loginfo.loginfo = log_info; 2282be4aa869SKenneth D. Merry if (sas_loginfo.dw.bus_type != 3 /*SAS*/) 2283be4aa869SKenneth D. Merry return; 2284be4aa869SKenneth D. Merry 2285be4aa869SKenneth D. Merry /* each nexus loss loginfo */ 2286be4aa869SKenneth D. Merry if (log_info == 0x31170000) 2287be4aa869SKenneth D. Merry return; 2288be4aa869SKenneth D. Merry 2289be4aa869SKenneth D. Merry /* eat the loginfos associated with task aborts */ 2290be4aa869SKenneth D. Merry if ((log_info == 30050000 || log_info == 2291be4aa869SKenneth D. Merry 0x31140000 || log_info == 0x31130000)) 2292be4aa869SKenneth D. Merry return; 2293be4aa869SKenneth D. Merry 2294be4aa869SKenneth D. Merry switch (sas_loginfo.dw.originator) { 2295be4aa869SKenneth D. Merry case 0: 2296be4aa869SKenneth D. Merry originator_str = "IOP"; 2297be4aa869SKenneth D. Merry break; 2298be4aa869SKenneth D. Merry case 1: 2299be4aa869SKenneth D. Merry originator_str = "PL"; 2300be4aa869SKenneth D. Merry break; 2301be4aa869SKenneth D. Merry case 2: 2302be4aa869SKenneth D. Merry originator_str = "IR"; 2303be4aa869SKenneth D. Merry break; 2304be4aa869SKenneth D. Merry } 2305be4aa869SKenneth D. Merry 23061610f95cSScott Long mps_dprint(sc, MPS_LOG, "log_info(0x%08x): originator(%s), " 2307be4aa869SKenneth D. Merry "code(0x%02x), sub_code(0x%04x)\n", log_info, 2308be4aa869SKenneth D. Merry originator_str, sas_loginfo.dw.code, 2309be4aa869SKenneth D. Merry sas_loginfo.dw.subcode); 2310be4aa869SKenneth D. Merry } 2311be4aa869SKenneth D. Merry 2312be4aa869SKenneth D. Merry static void 2313be4aa869SKenneth D. Merry mps_display_reply_info(struct mps_softc *sc, uint8_t *reply) 2314be4aa869SKenneth D. Merry { 2315be4aa869SKenneth D. Merry MPI2DefaultReply_t *mpi_reply; 2316be4aa869SKenneth D. Merry u16 sc_status; 2317be4aa869SKenneth D. Merry 2318be4aa869SKenneth D. Merry mpi_reply = (MPI2DefaultReply_t*)reply; 2319be4aa869SKenneth D. Merry sc_status = le16toh(mpi_reply->IOCStatus); 2320be4aa869SKenneth D. Merry if (sc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) 2321be4aa869SKenneth D. Merry mps_sas_log_info(sc, le32toh(mpi_reply->IOCLogInfo)); 2322be4aa869SKenneth D. Merry } 2323d3c7b9a0SKenneth D. Merry void 2324d3c7b9a0SKenneth D. Merry mps_intr(void *data) 2325d3c7b9a0SKenneth D. Merry { 2326d3c7b9a0SKenneth D. Merry struct mps_softc *sc; 2327d3c7b9a0SKenneth D. Merry uint32_t status; 2328d3c7b9a0SKenneth D. Merry 2329d3c7b9a0SKenneth D. Merry sc = (struct mps_softc *)data; 2330d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 2331d3c7b9a0SKenneth D. Merry 2332d3c7b9a0SKenneth D. Merry /* 2333d3c7b9a0SKenneth D. Merry * Check interrupt status register to flush the bus. This is 2334d3c7b9a0SKenneth D. Merry * needed for both INTx interrupts and driver-driven polling 2335d3c7b9a0SKenneth D. Merry */ 2336d3c7b9a0SKenneth D. Merry status = mps_regread(sc, MPI2_HOST_INTERRUPT_STATUS_OFFSET); 2337d3c7b9a0SKenneth D. Merry if ((status & MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT) == 0) 2338d3c7b9a0SKenneth D. Merry return; 2339d3c7b9a0SKenneth D. Merry 2340d3c7b9a0SKenneth D. Merry mps_lock(sc); 2341d3c7b9a0SKenneth D. Merry mps_intr_locked(data); 2342d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2343d3c7b9a0SKenneth D. Merry return; 2344d3c7b9a0SKenneth D. Merry } 2345d3c7b9a0SKenneth D. Merry 2346d3c7b9a0SKenneth D. Merry /* 2347d3c7b9a0SKenneth D. Merry * In theory, MSI/MSIX interrupts shouldn't need to read any registers on the 2348d3c7b9a0SKenneth D. Merry * chip. Hopefully this theory is correct. 2349d3c7b9a0SKenneth D. Merry */ 2350d3c7b9a0SKenneth D. Merry void 2351d3c7b9a0SKenneth D. Merry mps_intr_msi(void *data) 2352d3c7b9a0SKenneth D. Merry { 2353d3c7b9a0SKenneth D. Merry struct mps_softc *sc; 2354d3c7b9a0SKenneth D. Merry 2355d3c7b9a0SKenneth D. Merry sc = (struct mps_softc *)data; 2356d043c564SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 2357d3c7b9a0SKenneth D. Merry mps_lock(sc); 2358d3c7b9a0SKenneth D. Merry mps_intr_locked(data); 2359d3c7b9a0SKenneth D. Merry mps_unlock(sc); 2360d3c7b9a0SKenneth D. Merry return; 2361d3c7b9a0SKenneth D. Merry } 2362d3c7b9a0SKenneth D. Merry 2363d3c7b9a0SKenneth D. Merry /* 2364d3c7b9a0SKenneth D. Merry * The locking is overly broad and simplistic, but easy to deal with for now. 2365d3c7b9a0SKenneth D. Merry */ 2366d3c7b9a0SKenneth D. Merry void 2367d3c7b9a0SKenneth D. Merry mps_intr_locked(void *data) 2368d3c7b9a0SKenneth D. Merry { 2369d3c7b9a0SKenneth D. Merry MPI2_REPLY_DESCRIPTORS_UNION *desc; 2370d043c564SKenneth D. Merry MPI2_DIAG_RELEASE_REPLY *rel_rep; 2371d043c564SKenneth D. Merry mps_fw_diagnostic_buffer_t *pBuffer; 2372617e85f3SScott Long struct mps_softc *sc; 2373617e85f3SScott Long struct mps_command *cm = NULL; 2374617e85f3SScott Long uint64_t tdesc; 2375617e85f3SScott Long uint8_t flags; 2376617e85f3SScott Long u_int pq; 2377d3c7b9a0SKenneth D. Merry 2378d3c7b9a0SKenneth D. Merry sc = (struct mps_softc *)data; 2379d3c7b9a0SKenneth D. Merry 2380d3c7b9a0SKenneth D. Merry pq = sc->replypostindex; 2381d043c564SKenneth D. Merry mps_dprint(sc, MPS_TRACE, 2382d043c564SKenneth D. Merry "%s sc %p starting with replypostindex %u\n", 2383d043c564SKenneth D. Merry __func__, sc, sc->replypostindex); 2384d3c7b9a0SKenneth D. Merry 2385d3c7b9a0SKenneth D. Merry for ( ;; ) { 2386d3c7b9a0SKenneth D. Merry cm = NULL; 2387d043c564SKenneth D. Merry desc = &sc->post_queue[sc->replypostindex]; 2388617e85f3SScott Long 2389617e85f3SScott Long /* 2390617e85f3SScott Long * Copy and clear out the descriptor so that any reentry will 2391617e85f3SScott Long * immediately know that this descriptor has already been 2392617e85f3SScott Long * looked at. There is unfortunate casting magic because the 2393617e85f3SScott Long * MPI API doesn't have a cardinal 64bit type. 2394617e85f3SScott Long */ 2395617e85f3SScott Long tdesc = 0xffffffffffffffff; 2396617e85f3SScott Long tdesc = atomic_swap_64((uint64_t *)desc, tdesc); 2397617e85f3SScott Long desc = (MPI2_REPLY_DESCRIPTORS_UNION *)&tdesc; 2398617e85f3SScott Long 2399d3c7b9a0SKenneth D. Merry flags = desc->Default.ReplyFlags & 2400d3c7b9a0SKenneth D. Merry MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; 240149dfe4a2SKenneth D. Merry if ((flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) 2402be4aa869SKenneth D. Merry || (le32toh(desc->Words.High) == 0xffffffff)) 2403d3c7b9a0SKenneth D. Merry break; 2404d3c7b9a0SKenneth D. Merry 2405d043c564SKenneth D. Merry /* increment the replypostindex now, so that event handlers 2406d043c564SKenneth D. Merry * and cm completion handlers which decide to do a diag 2407d043c564SKenneth D. Merry * reset can zero it without it getting incremented again 2408d043c564SKenneth D. Merry * afterwards, and we break out of this loop on the next 2409d043c564SKenneth D. Merry * iteration since the reply post queue has been cleared to 2410d043c564SKenneth D. Merry * 0xFF and all descriptors look unused (which they are). 2411d043c564SKenneth D. Merry */ 2412d043c564SKenneth D. Merry if (++sc->replypostindex >= sc->pqdepth) 2413d043c564SKenneth D. Merry sc->replypostindex = 0; 2414d043c564SKenneth D. Merry 2415d3c7b9a0SKenneth D. Merry switch (flags) { 2416d3c7b9a0SKenneth D. Merry case MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS: 2417be4aa869SKenneth D. Merry cm = &sc->commands[le16toh(desc->SCSIIOSuccess.SMID)]; 2418d3c7b9a0SKenneth D. Merry cm->cm_reply = NULL; 2419d3c7b9a0SKenneth D. Merry break; 2420d3c7b9a0SKenneth D. Merry case MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY: 2421d3c7b9a0SKenneth D. Merry { 2422d3c7b9a0SKenneth D. Merry uint32_t baddr; 2423d3c7b9a0SKenneth D. Merry uint8_t *reply; 2424d3c7b9a0SKenneth D. Merry 242549dfe4a2SKenneth D. Merry /* 242649dfe4a2SKenneth D. Merry * Re-compose the reply address from the address 242749dfe4a2SKenneth D. Merry * sent back from the chip. The ReplyFrameAddress 242849dfe4a2SKenneth D. Merry * is the lower 32 bits of the physical address of 242949dfe4a2SKenneth D. Merry * particular reply frame. Convert that address to 243049dfe4a2SKenneth D. Merry * host format, and then use that to provide the 243149dfe4a2SKenneth D. Merry * offset against the virtual address base 243249dfe4a2SKenneth D. Merry * (sc->reply_frames). 243349dfe4a2SKenneth D. Merry */ 243449dfe4a2SKenneth D. Merry baddr = le32toh(desc->AddressReply.ReplyFrameAddress); 2435d3c7b9a0SKenneth D. Merry reply = sc->reply_frames + 243649dfe4a2SKenneth D. Merry (baddr - ((uint32_t)sc->reply_busaddr)); 243749dfe4a2SKenneth D. Merry /* 243849dfe4a2SKenneth D. Merry * Make sure the reply we got back is in a valid 243949dfe4a2SKenneth D. Merry * range. If not, go ahead and panic here, since 244049dfe4a2SKenneth D. Merry * we'll probably panic as soon as we deference the 244149dfe4a2SKenneth D. Merry * reply pointer anyway. 244249dfe4a2SKenneth D. Merry */ 244349dfe4a2SKenneth D. Merry if ((reply < sc->reply_frames) 244449dfe4a2SKenneth D. Merry || (reply > (sc->reply_frames + 244596410703SScott Long (sc->fqdepth * sc->replyframesz)))) { 244649dfe4a2SKenneth D. Merry printf("%s: WARNING: reply %p out of range!\n", 244749dfe4a2SKenneth D. Merry __func__, reply); 244849dfe4a2SKenneth D. Merry printf("%s: reply_frames %p, fqdepth %d, " 244949dfe4a2SKenneth D. Merry "frame size %d\n", __func__, 245049dfe4a2SKenneth D. Merry sc->reply_frames, sc->fqdepth, 245196410703SScott Long sc->replyframesz); 245249dfe4a2SKenneth D. Merry printf("%s: baddr %#x,\n", __func__, baddr); 2453f0779b04SScott Long /* LSI-TODO. See Linux Code for Graceful exit */ 245449dfe4a2SKenneth D. Merry panic("Reply address out of range"); 245549dfe4a2SKenneth D. Merry } 2456be4aa869SKenneth D. Merry if (le16toh(desc->AddressReply.SMID) == 0) { 2457d043c564SKenneth D. Merry if (((MPI2_DEFAULT_REPLY *)reply)->Function == 2458d043c564SKenneth D. Merry MPI2_FUNCTION_DIAG_BUFFER_POST) { 2459d043c564SKenneth D. Merry /* 2460d043c564SKenneth D. Merry * If SMID is 0 for Diag Buffer Post, 2461d043c564SKenneth D. Merry * this implies that the reply is due to 2462d043c564SKenneth D. Merry * a release function with a status that 2463d043c564SKenneth D. Merry * the buffer has been released. Set 2464d043c564SKenneth D. Merry * the buffer flags accordingly. 2465d043c564SKenneth D. Merry */ 2466d043c564SKenneth D. Merry rel_rep = 2467d043c564SKenneth D. Merry (MPI2_DIAG_RELEASE_REPLY *)reply; 2468f4e69c98SStephen McConnell if ((le16toh(rel_rep->IOCStatus) & 2469f4e69c98SStephen McConnell MPI2_IOCSTATUS_MASK) == 2470d043c564SKenneth D. Merry MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED) 2471d043c564SKenneth D. Merry { 2472d043c564SKenneth D. Merry pBuffer = 2473d043c564SKenneth D. Merry &sc->fw_diag_buffer_list[ 2474d043c564SKenneth D. Merry rel_rep->BufferType]; 2475d043c564SKenneth D. Merry pBuffer->valid_data = TRUE; 2476d043c564SKenneth D. Merry pBuffer->owned_by_firmware = 2477d043c564SKenneth D. Merry FALSE; 2478d043c564SKenneth D. Merry pBuffer->immediate = FALSE; 2479d043c564SKenneth D. Merry } 2480d043c564SKenneth D. Merry } else 2481d3c7b9a0SKenneth D. Merry mps_dispatch_event(sc, baddr, 2482d043c564SKenneth D. Merry (MPI2_EVENT_NOTIFICATION_REPLY *) 2483d043c564SKenneth D. Merry reply); 2484d3c7b9a0SKenneth D. Merry } else { 24858fe7bf06SWarner Losh /* 24868fe7bf06SWarner Losh * Ignore commands not in INQUEUE state 24878fe7bf06SWarner Losh * since they've already been completed 24888fe7bf06SWarner Losh * via another path. 24898fe7bf06SWarner Losh */ 2490f0779b04SScott Long cm = &sc->commands[ 2491f0779b04SScott Long le16toh(desc->AddressReply.SMID)]; 24928fe7bf06SWarner Losh if (cm->cm_state == MPS_CM_STATE_INQUEUE) { 2493d3c7b9a0SKenneth D. Merry cm->cm_reply = reply; 2494f0779b04SScott Long cm->cm_reply_data = le32toh( 2495f0779b04SScott Long desc->AddressReply.ReplyFrameAddress); 24968fe7bf06SWarner Losh } else { 24978fe7bf06SWarner Losh mps_dprint(sc, MPS_RECOVERY, 24988fe7bf06SWarner Losh "Bad state for ADDRESS_REPLY status," 24998fe7bf06SWarner Losh " ignoring state %d cm %p\n", 25008fe7bf06SWarner Losh cm->cm_state, cm); 25018fe7bf06SWarner Losh } 2502d3c7b9a0SKenneth D. Merry } 2503d3c7b9a0SKenneth D. Merry break; 2504d3c7b9a0SKenneth D. Merry } 2505d3c7b9a0SKenneth D. Merry case MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS: 2506d3c7b9a0SKenneth D. Merry case MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER: 2507d3c7b9a0SKenneth D. Merry case MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS: 2508d3c7b9a0SKenneth D. Merry default: 2509d3c7b9a0SKenneth D. Merry /* Unhandled */ 25101610f95cSScott Long mps_dprint(sc, MPS_ERROR, "Unhandled reply 0x%x\n", 2511d3c7b9a0SKenneth D. Merry desc->Default.ReplyFlags); 2512d3c7b9a0SKenneth D. Merry cm = NULL; 2513d3c7b9a0SKenneth D. Merry break; 2514d3c7b9a0SKenneth D. Merry } 2515d3c7b9a0SKenneth D. Merry 2516be4aa869SKenneth D. Merry 2517be4aa869SKenneth D. Merry if (cm != NULL) { 2518be4aa869SKenneth D. Merry // Print Error reply frame 2519be4aa869SKenneth D. Merry if (cm->cm_reply) 2520be4aa869SKenneth D. Merry mps_display_reply_info(sc,cm->cm_reply); 25211610f95cSScott Long mps_complete_command(sc, cm); 2522be4aa869SKenneth D. Merry } 2523d3c7b9a0SKenneth D. Merry } 2524d3c7b9a0SKenneth D. Merry 2525d3c7b9a0SKenneth D. Merry if (pq != sc->replypostindex) { 2526f0779b04SScott Long mps_dprint(sc, MPS_TRACE, "%s sc %p writing postindex %d\n", 2527d043c564SKenneth D. Merry __func__, sc, sc->replypostindex); 2528f0779b04SScott Long mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, 2529f0779b04SScott Long sc->replypostindex); 2530d3c7b9a0SKenneth D. Merry } 2531d3c7b9a0SKenneth D. Merry 2532d3c7b9a0SKenneth D. Merry return; 2533d3c7b9a0SKenneth D. Merry } 2534d3c7b9a0SKenneth D. Merry 2535d3c7b9a0SKenneth D. Merry static void 2536d3c7b9a0SKenneth D. Merry mps_dispatch_event(struct mps_softc *sc, uintptr_t data, 2537d3c7b9a0SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REPLY *reply) 2538d3c7b9a0SKenneth D. Merry { 2539d3c7b9a0SKenneth D. Merry struct mps_event_handle *eh; 25407df9d5acSKevin Lo int event, handled = 0; 2541d3c7b9a0SKenneth D. Merry 2542be4aa869SKenneth D. Merry event = le16toh(reply->Event); 2543d3c7b9a0SKenneth D. Merry TAILQ_FOREACH(eh, &sc->event_list, eh_list) { 2544d3c7b9a0SKenneth D. Merry if (isset(eh->mask, event)) { 2545d3c7b9a0SKenneth D. Merry eh->callback(sc, data, reply); 2546d3c7b9a0SKenneth D. Merry handled++; 2547d3c7b9a0SKenneth D. Merry } 2548d3c7b9a0SKenneth D. Merry } 2549d3c7b9a0SKenneth D. Merry 2550d3c7b9a0SKenneth D. Merry if (handled == 0) 25511610f95cSScott Long mps_dprint(sc, MPS_EVENT, "Unhandled event 0x%x\n", le16toh(event)); 2552d043c564SKenneth D. Merry 2553d043c564SKenneth D. Merry /* 2554d043c564SKenneth D. Merry * This is the only place that the event/reply should be freed. 2555d043c564SKenneth D. Merry * Anything wanting to hold onto the event data should have 2556d043c564SKenneth D. Merry * already copied it into their own storage. 2557d043c564SKenneth D. Merry */ 2558d043c564SKenneth D. Merry mps_free_reply(sc, data); 2559d043c564SKenneth D. Merry } 2560d043c564SKenneth D. Merry 2561d043c564SKenneth D. Merry static void 2562d043c564SKenneth D. Merry mps_reregister_events_complete(struct mps_softc *sc, struct mps_command *cm) 2563d043c564SKenneth D. Merry { 2564d043c564SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 2565d043c564SKenneth D. Merry 2566d043c564SKenneth D. Merry if (cm->cm_reply) 2567055e2653SScott Long MPS_DPRINT_EVENT(sc, generic, 2568d043c564SKenneth D. Merry (MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply); 2569d043c564SKenneth D. Merry 2570d043c564SKenneth D. Merry mps_free_command(sc, cm); 2571d043c564SKenneth D. Merry 2572d043c564SKenneth D. Merry /* next, send a port enable */ 2573d043c564SKenneth D. Merry mpssas_startup(sc); 2574d3c7b9a0SKenneth D. Merry } 2575d3c7b9a0SKenneth D. Merry 2576d3c7b9a0SKenneth D. Merry /* 2577d3c7b9a0SKenneth D. Merry * For both register_events and update_events, the caller supplies a bitmap 2578d3c7b9a0SKenneth D. Merry * of events that it _wants_. These functions then turn that into a bitmask 2579d3c7b9a0SKenneth D. Merry * suitable for the controller. 2580d3c7b9a0SKenneth D. Merry */ 2581d3c7b9a0SKenneth D. Merry int 2582be4aa869SKenneth D. Merry mps_register_events(struct mps_softc *sc, u32 *mask, 2583d3c7b9a0SKenneth D. Merry mps_evt_callback_t *cb, void *data, struct mps_event_handle **handle) 2584d3c7b9a0SKenneth D. Merry { 2585d3c7b9a0SKenneth D. Merry struct mps_event_handle *eh; 2586d3c7b9a0SKenneth D. Merry int error = 0; 2587d3c7b9a0SKenneth D. Merry 2588d3c7b9a0SKenneth D. Merry eh = malloc(sizeof(struct mps_event_handle), M_MPT2, M_WAITOK|M_ZERO); 2589d3c7b9a0SKenneth D. Merry eh->callback = cb; 2590d3c7b9a0SKenneth D. Merry eh->data = data; 2591d3c7b9a0SKenneth D. Merry TAILQ_INSERT_TAIL(&sc->event_list, eh, eh_list); 2592d3c7b9a0SKenneth D. Merry if (mask != NULL) 2593d3c7b9a0SKenneth D. Merry error = mps_update_events(sc, eh, mask); 2594d3c7b9a0SKenneth D. Merry *handle = eh; 2595d3c7b9a0SKenneth D. Merry 2596d3c7b9a0SKenneth D. Merry return (error); 2597d3c7b9a0SKenneth D. Merry } 2598d3c7b9a0SKenneth D. Merry 2599d3c7b9a0SKenneth D. Merry int 2600d3c7b9a0SKenneth D. Merry mps_update_events(struct mps_softc *sc, struct mps_event_handle *handle, 2601be4aa869SKenneth D. Merry u32 *mask) 2602d3c7b9a0SKenneth D. Merry { 2603d3c7b9a0SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REQUEST *evtreq; 26046d4ffcb4SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REPLY *reply = NULL; 2605d3c7b9a0SKenneth D. Merry struct mps_command *cm; 2606d3c7b9a0SKenneth D. Merry int error, i; 2607d3c7b9a0SKenneth D. Merry 2608d3c7b9a0SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 2609d3c7b9a0SKenneth D. Merry 2610d3c7b9a0SKenneth D. Merry if ((mask != NULL) && (handle != NULL)) 2611be4aa869SKenneth D. Merry bcopy(mask, &handle->mask[0], sizeof(u32) * 2612be4aa869SKenneth D. Merry MPI2_EVENT_NOTIFY_EVENTMASK_WORDS); 2613d3c7b9a0SKenneth D. Merry 2614be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2615be4aa869SKenneth D. Merry sc->event_mask[i] = -1; 2616be4aa869SKenneth D. Merry 2617be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2618be4aa869SKenneth D. Merry sc->event_mask[i] &= ~handle->mask[i]; 2619be4aa869SKenneth D. Merry 2620d3c7b9a0SKenneth D. Merry if ((cm = mps_alloc_command(sc)) == NULL) 2621d3c7b9a0SKenneth D. Merry return (EBUSY); 2622d3c7b9a0SKenneth D. Merry evtreq = (MPI2_EVENT_NOTIFICATION_REQUEST *)cm->cm_req; 2623d3c7b9a0SKenneth D. Merry evtreq->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; 2624d3c7b9a0SKenneth D. Merry evtreq->MsgFlags = 0; 2625d3c7b9a0SKenneth D. Merry evtreq->SASBroadcastPrimitiveMasks = 0; 2626d3c7b9a0SKenneth D. Merry #ifdef MPS_DEBUG_ALL_EVENTS 2627d3c7b9a0SKenneth D. Merry { 2628d3c7b9a0SKenneth D. Merry u_char fullmask[16]; 2629d3c7b9a0SKenneth D. Merry memset(fullmask, 0x00, 16); 2630be4aa869SKenneth D. Merry bcopy(fullmask, &evtreq->EventMasks[0], sizeof(u32) * 2631be4aa869SKenneth D. Merry MPI2_EVENT_NOTIFY_EVENTMASK_WORDS); 2632d3c7b9a0SKenneth D. Merry } 2633d3c7b9a0SKenneth D. Merry #else 2634be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2635be4aa869SKenneth D. Merry evtreq->EventMasks[i] = 2636be4aa869SKenneth D. Merry htole32(sc->event_mask[i]); 2637d3c7b9a0SKenneth D. Merry #endif 2638d3c7b9a0SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 2639d3c7b9a0SKenneth D. Merry cm->cm_data = NULL; 2640d3c7b9a0SKenneth D. Merry 26416d4ffcb4SKenneth D. Merry error = mps_wait_command(sc, &cm, 60, 0); 26426d4ffcb4SKenneth D. Merry if (cm != NULL) 2643d3c7b9a0SKenneth D. Merry reply = (MPI2_EVENT_NOTIFICATION_REPLY *)cm->cm_reply; 2644d043c564SKenneth D. Merry if ((reply == NULL) || 2645d043c564SKenneth D. Merry (reply->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) 2646d3c7b9a0SKenneth D. Merry error = ENXIO; 2647055e2653SScott Long 2648055e2653SScott Long if (reply) 2649055e2653SScott Long MPS_DPRINT_EVENT(sc, generic, reply); 2650055e2653SScott Long 2651d043c564SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s finished error %d\n", __func__, error); 2652d3c7b9a0SKenneth D. Merry 26536d4ffcb4SKenneth D. Merry if (cm != NULL) 2654d3c7b9a0SKenneth D. Merry mps_free_command(sc, cm); 2655d3c7b9a0SKenneth D. Merry return (error); 2656d3c7b9a0SKenneth D. Merry } 2657d3c7b9a0SKenneth D. Merry 2658d043c564SKenneth D. Merry static int 2659d043c564SKenneth D. Merry mps_reregister_events(struct mps_softc *sc) 2660d043c564SKenneth D. Merry { 2661d043c564SKenneth D. Merry MPI2_EVENT_NOTIFICATION_REQUEST *evtreq; 2662d043c564SKenneth D. Merry struct mps_command *cm; 2663d043c564SKenneth D. Merry struct mps_event_handle *eh; 2664d043c564SKenneth D. Merry int error, i; 2665d043c564SKenneth D. Merry 2666d043c564SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 2667d043c564SKenneth D. Merry 2668d043c564SKenneth D. Merry /* first, reregister events */ 2669d043c564SKenneth D. Merry 2670be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2671be4aa869SKenneth D. Merry sc->event_mask[i] = -1; 2672d043c564SKenneth D. Merry 2673d043c564SKenneth D. Merry TAILQ_FOREACH(eh, &sc->event_list, eh_list) { 2674be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2675d043c564SKenneth D. Merry sc->event_mask[i] &= ~eh->mask[i]; 2676d043c564SKenneth D. Merry } 2677d043c564SKenneth D. Merry 2678d043c564SKenneth D. Merry if ((cm = mps_alloc_command(sc)) == NULL) 2679d043c564SKenneth D. Merry return (EBUSY); 2680d043c564SKenneth D. Merry evtreq = (MPI2_EVENT_NOTIFICATION_REQUEST *)cm->cm_req; 2681d043c564SKenneth D. Merry evtreq->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; 2682d043c564SKenneth D. Merry evtreq->MsgFlags = 0; 2683d043c564SKenneth D. Merry evtreq->SASBroadcastPrimitiveMasks = 0; 2684d043c564SKenneth D. Merry #ifdef MPS_DEBUG_ALL_EVENTS 2685d043c564SKenneth D. Merry { 2686d043c564SKenneth D. Merry u_char fullmask[16]; 2687d043c564SKenneth D. Merry memset(fullmask, 0x00, 16); 2688be4aa869SKenneth D. Merry bcopy(fullmask, &evtreq->EventMasks[0], sizeof(u32) * 2689be4aa869SKenneth D. Merry MPI2_EVENT_NOTIFY_EVENTMASK_WORDS); 2690d043c564SKenneth D. Merry } 2691d043c564SKenneth D. Merry #else 2692be4aa869SKenneth D. Merry for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) 2693be4aa869SKenneth D. Merry evtreq->EventMasks[i] = 2694be4aa869SKenneth D. Merry htole32(sc->event_mask[i]); 2695d043c564SKenneth D. Merry #endif 2696d043c564SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 2697d043c564SKenneth D. Merry cm->cm_data = NULL; 2698d043c564SKenneth D. Merry cm->cm_complete = mps_reregister_events_complete; 2699d043c564SKenneth D. Merry 2700d043c564SKenneth D. Merry error = mps_map_command(sc, cm); 2701d043c564SKenneth D. Merry 27029b91b192SKenneth D. Merry mps_dprint(sc, MPS_TRACE, "%s finished with error %d\n", __func__, 27039b91b192SKenneth D. Merry error); 2704d043c564SKenneth D. Merry return (error); 2705d043c564SKenneth D. Merry } 2706d043c564SKenneth D. Merry 2707be4aa869SKenneth D. Merry void 2708d3c7b9a0SKenneth D. Merry mps_deregister_events(struct mps_softc *sc, struct mps_event_handle *handle) 2709d3c7b9a0SKenneth D. Merry { 2710d3c7b9a0SKenneth D. Merry 2711d3c7b9a0SKenneth D. Merry TAILQ_REMOVE(&sc->event_list, handle, eh_list); 2712d3c7b9a0SKenneth D. Merry free(handle, M_MPT2); 2713d3c7b9a0SKenneth D. Merry } 2714d3c7b9a0SKenneth D. Merry 271513ac67a7SMatthew D Fleming /* 271613ac67a7SMatthew D Fleming * Add a chain element as the next SGE for the specified command. 271713ac67a7SMatthew D Fleming * Reset cm_sge and cm_sgesize to indicate all the available space. 271813ac67a7SMatthew D Fleming */ 271913ac67a7SMatthew D Fleming static int 272013ac67a7SMatthew D Fleming mps_add_chain(struct mps_command *cm) 2721d3c7b9a0SKenneth D. Merry { 2722e30fceb8SWarner Losh MPI2_SGE_CHAIN64 *sgc; 2723d3c7b9a0SKenneth D. Merry struct mps_chain *chain; 272496410703SScott Long u_int space; 2725d3c7b9a0SKenneth D. Merry 272613ac67a7SMatthew D Fleming if (cm->cm_sglsize < MPS_SGC_SIZE) 272713ac67a7SMatthew D Fleming panic("MPS: Need SGE Error Code\n"); 2728d3c7b9a0SKenneth D. Merry 272913ac67a7SMatthew D Fleming chain = mps_alloc_chain(cm->cm_sc); 273013ac67a7SMatthew D Fleming if (chain == NULL) 273113ac67a7SMatthew D Fleming return (ENOBUFS); 273213ac67a7SMatthew D Fleming 273396410703SScott Long space = cm->cm_sc->reqframesz; 2734d3c7b9a0SKenneth D. Merry 2735d3c7b9a0SKenneth D. Merry /* 273613ac67a7SMatthew D Fleming * Note: a double-linked list is used to make it easier to 273713ac67a7SMatthew D Fleming * walk for debugging. 2738d3c7b9a0SKenneth D. Merry */ 273913ac67a7SMatthew D Fleming TAILQ_INSERT_TAIL(&cm->cm_chain_list, chain, chain_link); 274013ac67a7SMatthew D Fleming 2741e30fceb8SWarner Losh sgc = (MPI2_SGE_CHAIN64 *)&cm->cm_sge->MpiChain; 2742be4aa869SKenneth D. Merry sgc->Length = htole16(space); 274313ac67a7SMatthew D Fleming sgc->NextChainOffset = 0; 2744be4aa869SKenneth D. Merry /* TODO Looks like bug in Setting sgc->Flags. 2745be4aa869SKenneth D. Merry * sgc->Flags = ( MPI2_SGE_FLAGS_CHAIN_ELEMENT | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | 2746be4aa869SKenneth D. Merry * MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT 2747be4aa869SKenneth D. Merry * This is fine.. because we are not using simple element. In case of 27485bcbdb0bSZhenlei Huang * MPI2_SGE_CHAIN64, we have separate Length and Flags field. 2749be4aa869SKenneth D. Merry */ 2750e30fceb8SWarner Losh sgc->Flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT | MPI2_SGE_FLAGS_64_BIT_ADDRESSING; 2751e30fceb8SWarner Losh sgc->Address.High = htole32(chain->chain_busaddr >> 32); 2752e30fceb8SWarner Losh sgc->Address.Low = htole32(chain->chain_busaddr); 275313ac67a7SMatthew D Fleming 275413ac67a7SMatthew D Fleming cm->cm_sge = (MPI2_SGE_IO_UNION *)&chain->chain->MpiSimple; 275513ac67a7SMatthew D Fleming cm->cm_sglsize = space; 275613ac67a7SMatthew D Fleming return (0); 275713ac67a7SMatthew D Fleming } 275813ac67a7SMatthew D Fleming 275913ac67a7SMatthew D Fleming /* 276013ac67a7SMatthew D Fleming * Add one scatter-gather element (chain, simple, transaction context) 276113ac67a7SMatthew D Fleming * to the scatter-gather list for a command. Maintain cm_sglsize and 276213ac67a7SMatthew D Fleming * cm_sge as the remaining size and pointer to the next SGE to fill 276313ac67a7SMatthew D Fleming * in, respectively. 276413ac67a7SMatthew D Fleming */ 276513ac67a7SMatthew D Fleming int 276613ac67a7SMatthew D Fleming mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft) 276713ac67a7SMatthew D Fleming { 276813ac67a7SMatthew D Fleming MPI2_SGE_TRANSACTION_UNION *tc = sgep; 276913ac67a7SMatthew D Fleming MPI2_SGE_SIMPLE64 *sge = sgep; 277013ac67a7SMatthew D Fleming int error, type; 2771d043c564SKenneth D. Merry uint32_t saved_buf_len, saved_address_low, saved_address_high; 277213ac67a7SMatthew D Fleming 277313ac67a7SMatthew D Fleming type = (tc->Flags & MPI2_SGE_FLAGS_ELEMENT_MASK); 277413ac67a7SMatthew D Fleming 277513ac67a7SMatthew D Fleming #ifdef INVARIANTS 277613ac67a7SMatthew D Fleming switch (type) { 277713ac67a7SMatthew D Fleming case MPI2_SGE_FLAGS_TRANSACTION_ELEMENT: { 277813ac67a7SMatthew D Fleming if (len != tc->DetailsLength + 4) 277913ac67a7SMatthew D Fleming panic("TC %p length %u or %zu?", tc, 278013ac67a7SMatthew D Fleming tc->DetailsLength + 4, len); 278113ac67a7SMatthew D Fleming } 278213ac67a7SMatthew D Fleming break; 278313ac67a7SMatthew D Fleming case MPI2_SGE_FLAGS_CHAIN_ELEMENT: 2784e30fceb8SWarner Losh /* Driver only uses 64-bit chain elements */ 278513ac67a7SMatthew D Fleming if (len != MPS_SGC_SIZE) 278613ac67a7SMatthew D Fleming panic("CHAIN %p length %u or %zu?", sgep, 278713ac67a7SMatthew D Fleming MPS_SGC_SIZE, len); 278813ac67a7SMatthew D Fleming break; 278913ac67a7SMatthew D Fleming case MPI2_SGE_FLAGS_SIMPLE_ELEMENT: 279013ac67a7SMatthew D Fleming /* Driver only uses 64-bit SGE simple elements */ 279113ac67a7SMatthew D Fleming if (len != MPS_SGE64_SIZE) 279213ac67a7SMatthew D Fleming panic("SGE simple %p length %u or %zu?", sge, 279313ac67a7SMatthew D Fleming MPS_SGE64_SIZE, len); 279461c49b4dSAlexander Motin if (((le32toh(sge->FlagsLength) >> MPI2_SGE_FLAGS_SHIFT) & 279513ac67a7SMatthew D Fleming MPI2_SGE_FLAGS_ADDRESS_SIZE) == 0) 279661c49b4dSAlexander Motin panic("SGE simple %p not marked 64-bit?", sge); 279713ac67a7SMatthew D Fleming 279813ac67a7SMatthew D Fleming break; 279913ac67a7SMatthew D Fleming default: 280013ac67a7SMatthew D Fleming panic("Unexpected SGE %p, flags %02x", tc, tc->Flags); 280113ac67a7SMatthew D Fleming } 280213ac67a7SMatthew D Fleming #endif 2803d3c7b9a0SKenneth D. Merry 2804d3c7b9a0SKenneth D. Merry /* 2805d3c7b9a0SKenneth D. Merry * case 1: 1 more segment, enough room for it 2806d3c7b9a0SKenneth D. Merry * case 2: 2 more segments, enough room for both 2807d3c7b9a0SKenneth D. Merry * case 3: >=2 more segments, only enough room for 1 and a chain 2808d3c7b9a0SKenneth D. Merry * case 4: >=1 more segment, enough room for only a chain 2809d3c7b9a0SKenneth D. Merry * case 5: >=1 more segment, no room for anything (error) 2810d3c7b9a0SKenneth D. Merry */ 2811d3c7b9a0SKenneth D. Merry 281213ac67a7SMatthew D Fleming /* 281313ac67a7SMatthew D Fleming * There should be room for at least a chain element, or this 281413ac67a7SMatthew D Fleming * code is buggy. Case (5). 281513ac67a7SMatthew D Fleming */ 281613ac67a7SMatthew D Fleming if (cm->cm_sglsize < MPS_SGC_SIZE) 2817d3c7b9a0SKenneth D. Merry panic("MPS: Need SGE Error Code\n"); 281813ac67a7SMatthew D Fleming 28194b07a560SScott Long if (segsleft >= 1 && cm->cm_sglsize < len + MPS_SGC_SIZE) { 28204b07a560SScott Long /* 28214b07a560SScott Long * 1 or more segment, enough room for only a chain. 28224b07a560SScott Long * Hope the previous element wasn't a Simple entry 28234b07a560SScott Long * that needed to be marked with 28244b07a560SScott Long * MPI2_SGE_FLAGS_LAST_ELEMENT. Case (4). 28254b07a560SScott Long */ 28264b07a560SScott Long if ((error = mps_add_chain(cm)) != 0) 28274b07a560SScott Long return (error); 28284b07a560SScott Long } 28294b07a560SScott Long 283013ac67a7SMatthew D Fleming if (segsleft >= 2 && 283113ac67a7SMatthew D Fleming cm->cm_sglsize < len + MPS_SGC_SIZE + MPS_SGE64_SIZE) { 283213ac67a7SMatthew D Fleming /* 283313ac67a7SMatthew D Fleming * There are 2 or more segments left to add, and only 283413ac67a7SMatthew D Fleming * enough room for 1 and a chain. Case (3). 283513ac67a7SMatthew D Fleming * 283613ac67a7SMatthew D Fleming * Mark as last element in this chain if necessary. 283713ac67a7SMatthew D Fleming */ 283813ac67a7SMatthew D Fleming if (type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) { 283961c49b4dSAlexander Motin sge->FlagsLength |= htole32( 284061c49b4dSAlexander Motin MPI2_SGE_FLAGS_LAST_ELEMENT << MPI2_SGE_FLAGS_SHIFT); 2841d3c7b9a0SKenneth D. Merry } 2842d3c7b9a0SKenneth D. Merry 2843d3c7b9a0SKenneth D. Merry /* 284413ac67a7SMatthew D Fleming * Add the item then a chain. Do the chain now, 284513ac67a7SMatthew D Fleming * rather than on the next iteration, to simplify 284613ac67a7SMatthew D Fleming * understanding the code. 2847d3c7b9a0SKenneth D. Merry */ 284813ac67a7SMatthew D Fleming cm->cm_sglsize -= len; 284913ac67a7SMatthew D Fleming bcopy(sgep, cm->cm_sge, len); 285013ac67a7SMatthew D Fleming cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len); 285113ac67a7SMatthew D Fleming return (mps_add_chain(cm)); 285213ac67a7SMatthew D Fleming } 285313ac67a7SMatthew D Fleming 285413ac67a7SMatthew D Fleming #ifdef INVARIANTS 285513ac67a7SMatthew D Fleming /* Case 1: 1 more segment, enough room for it. */ 285613ac67a7SMatthew D Fleming if (segsleft == 1 && cm->cm_sglsize < len) 285713ac67a7SMatthew D Fleming panic("1 seg left and no room? %u versus %zu", 285813ac67a7SMatthew D Fleming cm->cm_sglsize, len); 285913ac67a7SMatthew D Fleming 286013ac67a7SMatthew D Fleming /* Case 2: 2 more segments, enough room for both */ 286113ac67a7SMatthew D Fleming if (segsleft == 2 && cm->cm_sglsize < len + MPS_SGE64_SIZE) 286213ac67a7SMatthew D Fleming panic("2 segs left and no room? %u versus %zu", 286313ac67a7SMatthew D Fleming cm->cm_sglsize, len); 286413ac67a7SMatthew D Fleming #endif 286513ac67a7SMatthew D Fleming 286613ac67a7SMatthew D Fleming if (segsleft == 1 && type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) { 286713ac67a7SMatthew D Fleming /* 2868d043c564SKenneth D. Merry * If this is a bi-directional request, need to account for that 2869d043c564SKenneth D. Merry * here. Save the pre-filled sge values. These will be used 2870d043c564SKenneth D. Merry * either for the 2nd SGL or for a single direction SGL. If 2871d043c564SKenneth D. Merry * cm_out_len is non-zero, this is a bi-directional request, so 2872d043c564SKenneth D. Merry * fill in the OUT SGL first, then the IN SGL, otherwise just 2873d043c564SKenneth D. Merry * fill in the IN SGL. Note that at this time, when filling in 2874d043c564SKenneth D. Merry * 2 SGL's for a bi-directional request, they both use the same 2875d043c564SKenneth D. Merry * DMA buffer (same cm command). 287613ac67a7SMatthew D Fleming */ 287761c49b4dSAlexander Motin saved_buf_len = le32toh(sge->FlagsLength) & 0x00FFFFFF; 2878d043c564SKenneth D. Merry saved_address_low = sge->Address.Low; 2879d043c564SKenneth D. Merry saved_address_high = sge->Address.High; 2880d043c564SKenneth D. Merry if (cm->cm_out_len) { 288161c49b4dSAlexander Motin sge->FlagsLength = htole32(cm->cm_out_len | 2882d043c564SKenneth D. Merry ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT | 288313ac67a7SMatthew D Fleming MPI2_SGE_FLAGS_END_OF_BUFFER | 2884d043c564SKenneth D. Merry MPI2_SGE_FLAGS_HOST_TO_IOC | 2885d043c564SKenneth D. Merry MPI2_SGE_FLAGS_64_BIT_ADDRESSING) << 288661c49b4dSAlexander Motin MPI2_SGE_FLAGS_SHIFT)); 2887d043c564SKenneth D. Merry cm->cm_sglsize -= len; 2888d043c564SKenneth D. Merry bcopy(sgep, cm->cm_sge, len); 2889d043c564SKenneth D. Merry cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge 2890d043c564SKenneth D. Merry + len); 2891d043c564SKenneth D. Merry } 289261c49b4dSAlexander Motin saved_buf_len |= 2893d043c564SKenneth D. Merry ((uint32_t)(MPI2_SGE_FLAGS_SIMPLE_ELEMENT | 2894d043c564SKenneth D. Merry MPI2_SGE_FLAGS_END_OF_BUFFER | 2895d043c564SKenneth D. Merry MPI2_SGE_FLAGS_LAST_ELEMENT | 2896d043c564SKenneth D. Merry MPI2_SGE_FLAGS_END_OF_LIST | 2897d043c564SKenneth D. Merry MPI2_SGE_FLAGS_64_BIT_ADDRESSING) << 2898d043c564SKenneth D. Merry MPI2_SGE_FLAGS_SHIFT); 2899d043c564SKenneth D. Merry if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) { 290061c49b4dSAlexander Motin saved_buf_len |= 2901d043c564SKenneth D. Merry ((uint32_t)(MPI2_SGE_FLAGS_IOC_TO_HOST) << 2902d043c564SKenneth D. Merry MPI2_SGE_FLAGS_SHIFT); 2903d043c564SKenneth D. Merry } else { 290461c49b4dSAlexander Motin saved_buf_len |= 2905d043c564SKenneth D. Merry ((uint32_t)(MPI2_SGE_FLAGS_HOST_TO_IOC) << 2906d043c564SKenneth D. Merry MPI2_SGE_FLAGS_SHIFT); 2907d043c564SKenneth D. Merry } 290861c49b4dSAlexander Motin sge->FlagsLength = htole32(saved_buf_len); 2909d043c564SKenneth D. Merry sge->Address.Low = saved_address_low; 2910d043c564SKenneth D. Merry sge->Address.High = saved_address_high; 291113ac67a7SMatthew D Fleming } 291213ac67a7SMatthew D Fleming 291313ac67a7SMatthew D Fleming cm->cm_sglsize -= len; 291413ac67a7SMatthew D Fleming bcopy(sgep, cm->cm_sge, len); 291513ac67a7SMatthew D Fleming cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len); 291613ac67a7SMatthew D Fleming return (0); 291713ac67a7SMatthew D Fleming } 291813ac67a7SMatthew D Fleming 291913ac67a7SMatthew D Fleming /* 292013ac67a7SMatthew D Fleming * Add one dma segment to the scatter-gather list for a command. 292113ac67a7SMatthew D Fleming */ 292213ac67a7SMatthew D Fleming int 292313ac67a7SMatthew D Fleming mps_add_dmaseg(struct mps_command *cm, vm_paddr_t pa, size_t len, u_int flags, 292413ac67a7SMatthew D Fleming int segsleft) 292513ac67a7SMatthew D Fleming { 292613ac67a7SMatthew D Fleming MPI2_SGE_SIMPLE64 sge; 292713ac67a7SMatthew D Fleming 292813ac67a7SMatthew D Fleming /* 2929d043c564SKenneth D. Merry * This driver always uses 64-bit address elements for simplicity. 293013ac67a7SMatthew D Fleming */ 293161c49b4dSAlexander Motin bzero(&sge, sizeof(sge)); 2932d043c564SKenneth D. Merry flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT | 2933d043c564SKenneth D. Merry MPI2_SGE_FLAGS_64_BIT_ADDRESSING; 293461c49b4dSAlexander Motin sge.FlagsLength = htole32(len | (flags << MPI2_SGE_FLAGS_SHIFT)); 293513ac67a7SMatthew D Fleming mps_from_u64(pa, &sge.Address); 293613ac67a7SMatthew D Fleming 293713ac67a7SMatthew D Fleming return (mps_push_sge(cm, &sge, sizeof sge, segsleft)); 293813ac67a7SMatthew D Fleming } 293913ac67a7SMatthew D Fleming 294013ac67a7SMatthew D Fleming static void 294113ac67a7SMatthew D Fleming mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 294213ac67a7SMatthew D Fleming { 294313ac67a7SMatthew D Fleming struct mps_softc *sc; 294413ac67a7SMatthew D Fleming struct mps_command *cm; 294513ac67a7SMatthew D Fleming u_int i, dir, sflags; 294613ac67a7SMatthew D Fleming 294713ac67a7SMatthew D Fleming cm = (struct mps_command *)arg; 294813ac67a7SMatthew D Fleming sc = cm->cm_sc; 294913ac67a7SMatthew D Fleming 295013ac67a7SMatthew D Fleming /* 295106e79492SKenneth D. Merry * In this case, just print out a warning and let the chip tell the 295206e79492SKenneth D. Merry * user they did the wrong thing. 295306e79492SKenneth D. Merry */ 295406e79492SKenneth D. Merry if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) { 29551610f95cSScott Long mps_dprint(sc, MPS_ERROR, 29561610f95cSScott Long "%s: warning: busdma returned %d segments, " 295706e79492SKenneth D. Merry "more than the %d allowed\n", __func__, nsegs, 295806e79492SKenneth D. Merry cm->cm_max_segs); 295906e79492SKenneth D. Merry } 296006e79492SKenneth D. Merry 296106e79492SKenneth D. Merry /* 2962d043c564SKenneth D. Merry * Set up DMA direction flags. Bi-directional requests are also handled 2963d043c564SKenneth D. Merry * here. In that case, both direction flags will be set. 296413ac67a7SMatthew D Fleming */ 296513ac67a7SMatthew D Fleming sflags = 0; 296606e79492SKenneth D. Merry if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) { 296706e79492SKenneth D. Merry /* 296806e79492SKenneth D. Merry * We have to add a special case for SMP passthrough, there 296906e79492SKenneth D. Merry * is no easy way to generically handle it. The first 297006e79492SKenneth D. Merry * S/G element is used for the command (therefore the 297106e79492SKenneth D. Merry * direction bit needs to be set). The second one is used 297206e79492SKenneth D. Merry * for the reply. We'll leave it to the caller to make 297306e79492SKenneth D. Merry * sure we only have two buffers. 297406e79492SKenneth D. Merry */ 297506e79492SKenneth D. Merry /* 297606e79492SKenneth D. Merry * Even though the busdma man page says it doesn't make 297706e79492SKenneth D. Merry * sense to have both direction flags, it does in this case. 297806e79492SKenneth D. Merry * We have one s/g element being accessed in each direction. 297906e79492SKenneth D. Merry */ 298006e79492SKenneth D. Merry dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD; 298106e79492SKenneth D. Merry 298206e79492SKenneth D. Merry /* 298306e79492SKenneth D. Merry * Set the direction flag on the first buffer in the SMP 298406e79492SKenneth D. Merry * passthrough request. We'll clear it for the second one. 298506e79492SKenneth D. Merry */ 298606e79492SKenneth D. Merry sflags |= MPI2_SGE_FLAGS_DIRECTION | 298706e79492SKenneth D. Merry MPI2_SGE_FLAGS_END_OF_BUFFER; 298806e79492SKenneth D. Merry } else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) { 2989d043c564SKenneth D. Merry sflags |= MPI2_SGE_FLAGS_HOST_TO_IOC; 299013ac67a7SMatthew D Fleming dir = BUS_DMASYNC_PREWRITE; 299113ac67a7SMatthew D Fleming } else 299213ac67a7SMatthew D Fleming dir = BUS_DMASYNC_PREREAD; 299313ac67a7SMatthew D Fleming 299413ac67a7SMatthew D Fleming for (i = 0; i < nsegs; i++) { 2995d043c564SKenneth D. Merry if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) && (i != 0)) { 299606e79492SKenneth D. Merry sflags &= ~MPI2_SGE_FLAGS_DIRECTION; 299706e79492SKenneth D. Merry } 299813ac67a7SMatthew D Fleming error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len, 299913ac67a7SMatthew D Fleming sflags, nsegs - i); 300013ac67a7SMatthew D Fleming if (error != 0) { 3001d3c7b9a0SKenneth D. Merry /* Resource shortage, roll back! */ 3002fe839103SScott Long if (ratecheck(&sc->lastfail, &mps_chainfail_interval)) 30031610f95cSScott Long mps_dprint(sc, MPS_INFO, "Out of chain frames, " 30041610f95cSScott Long "consider increasing hw.mps.max_chains.\n"); 3005550e2acdSKenneth D. Merry cm->cm_flags |= MPS_CM_FLAGS_CHAIN_FAILED; 3006c5041b4eSWarner Losh /* 3007c5041b4eSWarner Losh * mpr_complete_command can only be called on commands 3008c5041b4eSWarner Losh * that are in the queue. Since this is an error path 3009c5041b4eSWarner Losh * which gets called before we enqueue, update the state 3010c5041b4eSWarner Losh * to meet this requirement before we complete it. 3011c5041b4eSWarner Losh */ 301233755dbbSWarner Losh cm->cm_state = MPS_CM_STATE_INQUEUE; 30131610f95cSScott Long mps_complete_command(sc, cm); 3014d3c7b9a0SKenneth D. Merry return; 3015d3c7b9a0SKenneth D. Merry } 3016d3c7b9a0SKenneth D. Merry } 3017d3c7b9a0SKenneth D. Merry 3018d3c7b9a0SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); 3019d3c7b9a0SKenneth D. Merry mps_enqueue_request(sc, cm); 3020d3c7b9a0SKenneth D. Merry 3021d3c7b9a0SKenneth D. Merry return; 3022d3c7b9a0SKenneth D. Merry } 3023d3c7b9a0SKenneth D. Merry 302406e79492SKenneth D. Merry static void 302506e79492SKenneth D. Merry mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize, 302606e79492SKenneth D. Merry int error) 302706e79492SKenneth D. Merry { 302806e79492SKenneth D. Merry mps_data_cb(arg, segs, nsegs, error); 302906e79492SKenneth D. Merry } 303006e79492SKenneth D. Merry 30311476ba40SKenneth D. Merry /* 3032d043c564SKenneth D. Merry * This is the routine to enqueue commands ansynchronously. 30331476ba40SKenneth D. Merry * Note that the only error path here is from bus_dmamap_load(), which can 3034d043c564SKenneth D. Merry * return EINPROGRESS if it is waiting for resources. Other than this, it's 3035d043c564SKenneth D. Merry * assumed that if you have a command in-hand, then you have enough credits 3036d043c564SKenneth D. Merry * to use it. 30371476ba40SKenneth D. Merry */ 3038d3c7b9a0SKenneth D. Merry int 3039d3c7b9a0SKenneth D. Merry mps_map_command(struct mps_softc *sc, struct mps_command *cm) 3040d3c7b9a0SKenneth D. Merry { 3041d3c7b9a0SKenneth D. Merry int error = 0; 3042d3c7b9a0SKenneth D. Merry 304306e79492SKenneth D. Merry if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) { 304406e79492SKenneth D. Merry error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap, 304506e79492SKenneth D. Merry &cm->cm_uio, mps_data_cb2, cm, 0); 3046dd0b4fb6SKonstantin Belousov } else if (cm->cm_flags & MPS_CM_FLAGS_USE_CCB) { 3047dd0b4fb6SKonstantin Belousov error = bus_dmamap_load_ccb(sc->buffer_dmat, cm->cm_dmamap, 3048dd0b4fb6SKonstantin Belousov cm->cm_data, mps_data_cb, cm, 0); 304906e79492SKenneth D. Merry } else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) { 3050d3c7b9a0SKenneth D. Merry error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap, 3051d3c7b9a0SKenneth D. Merry cm->cm_data, cm->cm_length, mps_data_cb, cm, 0); 3052d3c7b9a0SKenneth D. Merry } else { 3053d3c7b9a0SKenneth D. Merry /* Add a zero-length element as needed */ 305461c49b4dSAlexander Motin if (cm->cm_sge != NULL) 305561c49b4dSAlexander Motin mps_add_dmaseg(cm, 0, 0, 0, 1); 3056d3c7b9a0SKenneth D. Merry mps_enqueue_request(sc, cm); 3057d3c7b9a0SKenneth D. Merry } 3058d3c7b9a0SKenneth D. Merry 3059d3c7b9a0SKenneth D. Merry return (error); 3060d3c7b9a0SKenneth D. Merry } 3061d3c7b9a0SKenneth D. Merry 3062d3c7b9a0SKenneth D. Merry /* 3063d043c564SKenneth D. Merry * This is the routine to enqueue commands synchronously. An error of 3064d043c564SKenneth D. Merry * EINPROGRESS from mps_map_command() is ignored since the command will 3065d043c564SKenneth D. Merry * be executed and enqueued automatically. Other errors come from msleep(). 3066d043c564SKenneth D. Merry */ 3067d043c564SKenneth D. Merry int 30686d4ffcb4SKenneth D. Merry mps_wait_command(struct mps_softc *sc, struct mps_command **cmp, int timeout, 30699b91b192SKenneth D. Merry int sleep_flag) 3070d043c564SKenneth D. Merry { 3071be4aa869SKenneth D. Merry int error, rc; 30729b91b192SKenneth D. Merry struct timeval cur_time, start_time; 30736d4ffcb4SKenneth D. Merry struct mps_command *cm = *cmp; 3074d043c564SKenneth D. Merry 3075be4aa869SKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_DIAGRESET) 3076be4aa869SKenneth D. Merry return EBUSY; 3077be4aa869SKenneth D. Merry 3078d043c564SKenneth D. Merry cm->cm_complete = NULL; 3079ac7d1ed2SScott Long cm->cm_flags |= MPS_CM_FLAGS_POLLED; 3080d043c564SKenneth D. Merry error = mps_map_command(sc, cm); 3081d043c564SKenneth D. Merry if ((error != 0) && (error != EINPROGRESS)) 3082d043c564SKenneth D. Merry return (error); 30839b91b192SKenneth D. Merry 3084ac7d1ed2SScott Long /* 3085ac7d1ed2SScott Long * Check for context and wait for 50 mSec at a time until time has 3086ac7d1ed2SScott Long * expired or the command has finished. If msleep can't be used, need 3087ac7d1ed2SScott Long * to poll. 3088ac7d1ed2SScott Long */ 30899b91b192SKenneth D. Merry if (curthread->td_no_sleeping != 0) 30909b91b192SKenneth D. Merry sleep_flag = NO_SLEEP; 3091417aa6b8SKenneth D. Merry getmicrouptime(&start_time); 30929b91b192SKenneth D. Merry if (mtx_owned(&sc->mps_mtx) && sleep_flag == CAN_SLEEP) { 3093ac7d1ed2SScott Long cm->cm_flags |= MPS_CM_FLAGS_WAKEUP; 3094653c521fSKenneth D. Merry error = msleep(cm, &sc->mps_mtx, 0, "mpswait", timeout*hz); 3095417aa6b8SKenneth D. Merry if (error == EWOULDBLOCK) { 3096417aa6b8SKenneth D. Merry /* 3097417aa6b8SKenneth D. Merry * Record the actual elapsed time in the case of a 3098417aa6b8SKenneth D. Merry * timeout for the message below. 3099417aa6b8SKenneth D. Merry */ 3100417aa6b8SKenneth D. Merry getmicrouptime(&cur_time); 3101417aa6b8SKenneth D. Merry timevalsub(&cur_time, &start_time); 3102417aa6b8SKenneth D. Merry } 31039b91b192SKenneth D. Merry } else { 31049b91b192SKenneth D. Merry while ((cm->cm_flags & MPS_CM_FLAGS_COMPLETE) == 0) { 31059b91b192SKenneth D. Merry mps_intr_locked(sc); 31069b91b192SKenneth D. Merry if (sleep_flag == CAN_SLEEP) 31079b91b192SKenneth D. Merry pause("mpswait", hz/20); 31089b91b192SKenneth D. Merry else 31099b91b192SKenneth D. Merry DELAY(50000); 31109b91b192SKenneth D. Merry 3111417aa6b8SKenneth D. Merry getmicrouptime(&cur_time); 3112417aa6b8SKenneth D. Merry timevalsub(&cur_time, &start_time); 3113417aa6b8SKenneth D. Merry if (cur_time.tv_sec > timeout) { 31149b91b192SKenneth D. Merry error = EWOULDBLOCK; 31159b91b192SKenneth D. Merry break; 31169b91b192SKenneth D. Merry } 31179b91b192SKenneth D. Merry } 31189b91b192SKenneth D. Merry } 31199b91b192SKenneth D. Merry 3120be4aa869SKenneth D. Merry if (error == EWOULDBLOCK) { 312186312e46SConrad Meyer if (cm->cm_timeout_handler == NULL) { 3122417aa6b8SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Calling Reinit from %s, timeout=%d," 3123417aa6b8SKenneth D. Merry " elapsed=%jd\n", __func__, timeout, 3124417aa6b8SKenneth D. Merry (intmax_t)cur_time.tv_sec); 3125be4aa869SKenneth D. Merry rc = mps_reinit(sc); 31269b91b192SKenneth D. Merry mps_dprint(sc, MPS_FAULT, "Reinit %s\n", (rc == 0) ? "success" : 31279b91b192SKenneth D. Merry "failed"); 312886312e46SConrad Meyer } else 312986312e46SConrad Meyer cm->cm_timeout_handler(sc, cm); 31306d4ffcb4SKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_REALLOCATED) { 31316d4ffcb4SKenneth D. Merry /* 31326d4ffcb4SKenneth D. Merry * Tell the caller that we freed the command in a 31336d4ffcb4SKenneth D. Merry * reinit. 31346d4ffcb4SKenneth D. Merry */ 31356d4ffcb4SKenneth D. Merry *cmp = NULL; 31366d4ffcb4SKenneth D. Merry } 3137d043c564SKenneth D. Merry error = ETIMEDOUT; 3138be4aa869SKenneth D. Merry } 3139d043c564SKenneth D. Merry return (error); 3140d043c564SKenneth D. Merry } 3141d043c564SKenneth D. Merry 3142d043c564SKenneth D. Merry /* 3143d3c7b9a0SKenneth D. Merry * The MPT driver had a verbose interface for config pages. In this driver, 3144453130d9SPedro F. Giffuni * reduce it to much simpler terms, similar to the Linux driver. 3145d3c7b9a0SKenneth D. Merry */ 3146d3c7b9a0SKenneth D. Merry int 3147d3c7b9a0SKenneth D. Merry mps_read_config_page(struct mps_softc *sc, struct mps_config_params *params) 3148d3c7b9a0SKenneth D. Merry { 3149d3c7b9a0SKenneth D. Merry MPI2_CONFIG_REQUEST *req; 3150d3c7b9a0SKenneth D. Merry struct mps_command *cm; 3151d3c7b9a0SKenneth D. Merry int error; 3152d3c7b9a0SKenneth D. Merry 3153d3c7b9a0SKenneth D. Merry if (sc->mps_flags & MPS_FLAGS_BUSY) { 3154d3c7b9a0SKenneth D. Merry return (EBUSY); 3155d3c7b9a0SKenneth D. Merry } 3156d3c7b9a0SKenneth D. Merry 3157d3c7b9a0SKenneth D. Merry cm = mps_alloc_command(sc); 3158d3c7b9a0SKenneth D. Merry if (cm == NULL) { 3159d3c7b9a0SKenneth D. Merry return (EBUSY); 3160d3c7b9a0SKenneth D. Merry } 3161d3c7b9a0SKenneth D. Merry 3162d3c7b9a0SKenneth D. Merry req = (MPI2_CONFIG_REQUEST *)cm->cm_req; 3163d3c7b9a0SKenneth D. Merry req->Function = MPI2_FUNCTION_CONFIG; 3164d3c7b9a0SKenneth D. Merry req->Action = params->action; 3165d3c7b9a0SKenneth D. Merry req->SGLFlags = 0; 3166d3c7b9a0SKenneth D. Merry req->ChainOffset = 0; 3167d3c7b9a0SKenneth D. Merry req->PageAddress = params->page_address; 31682ce303e8SAlan Somers if (params->hdr.Struct.PageType == MPI2_CONFIG_PAGETYPE_EXTENDED) { 3169d3c7b9a0SKenneth D. Merry MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; 3170d3c7b9a0SKenneth D. Merry 3171d3c7b9a0SKenneth D. Merry hdr = ¶ms->hdr.Ext; 3172d3c7b9a0SKenneth D. Merry req->ExtPageType = hdr->ExtPageType; 3173d3c7b9a0SKenneth D. Merry req->ExtPageLength = hdr->ExtPageLength; 3174d3c7b9a0SKenneth D. Merry req->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; 3175d3c7b9a0SKenneth D. Merry req->Header.PageLength = 0; /* Must be set to zero */ 3176d3c7b9a0SKenneth D. Merry req->Header.PageNumber = hdr->PageNumber; 3177d3c7b9a0SKenneth D. Merry req->Header.PageVersion = hdr->PageVersion; 3178d3c7b9a0SKenneth D. Merry } else { 3179d3c7b9a0SKenneth D. Merry MPI2_CONFIG_PAGE_HEADER *hdr; 3180d3c7b9a0SKenneth D. Merry 3181d3c7b9a0SKenneth D. Merry hdr = ¶ms->hdr.Struct; 3182d3c7b9a0SKenneth D. Merry req->Header.PageType = hdr->PageType; 3183d3c7b9a0SKenneth D. Merry req->Header.PageNumber = hdr->PageNumber; 3184d3c7b9a0SKenneth D. Merry req->Header.PageLength = hdr->PageLength; 3185d3c7b9a0SKenneth D. Merry req->Header.PageVersion = hdr->PageVersion; 3186d3c7b9a0SKenneth D. Merry } 3187d3c7b9a0SKenneth D. Merry 3188d3c7b9a0SKenneth D. Merry cm->cm_data = params->buffer; 3189d3c7b9a0SKenneth D. Merry cm->cm_length = params->length; 3190fcafcbcbSScott Long if (cm->cm_data != NULL) { 3191d3c7b9a0SKenneth D. Merry cm->cm_sge = &req->PageBufferSGE; 3192d3c7b9a0SKenneth D. Merry cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION); 3193d3c7b9a0SKenneth D. Merry cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN; 3194fcafcbcbSScott Long } else 3195fcafcbcbSScott Long cm->cm_sge = NULL; 3196d3c7b9a0SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 3197d3c7b9a0SKenneth D. Merry 319845b40ad0SMatthew D Fleming cm->cm_complete_data = params; 3199d3c7b9a0SKenneth D. Merry if (params->callback != NULL) { 3200d3c7b9a0SKenneth D. Merry cm->cm_complete = mps_config_complete; 3201d3c7b9a0SKenneth D. Merry return (mps_map_command(sc, cm)); 3202d3c7b9a0SKenneth D. Merry } else { 32036d4ffcb4SKenneth D. Merry error = mps_wait_command(sc, &cm, 0, CAN_SLEEP); 3204d043c564SKenneth D. Merry if (error) { 3205d043c564SKenneth D. Merry mps_dprint(sc, MPS_FAULT, 3206d043c564SKenneth D. Merry "Error %d reading config page\n", error); 32076d4ffcb4SKenneth D. Merry if (cm != NULL) 3208d043c564SKenneth D. Merry mps_free_command(sc, cm); 3209d3c7b9a0SKenneth D. Merry return (error); 3210d043c564SKenneth D. Merry } 3211d3c7b9a0SKenneth D. Merry mps_config_complete(sc, cm); 3212d3c7b9a0SKenneth D. Merry } 3213d3c7b9a0SKenneth D. Merry 3214d3c7b9a0SKenneth D. Merry return (0); 3215d3c7b9a0SKenneth D. Merry } 3216d3c7b9a0SKenneth D. Merry 3217d3c7b9a0SKenneth D. Merry int 3218d3c7b9a0SKenneth D. Merry mps_write_config_page(struct mps_softc *sc, struct mps_config_params *params) 3219d3c7b9a0SKenneth D. Merry { 3220d3c7b9a0SKenneth D. Merry return (EINVAL); 3221d3c7b9a0SKenneth D. Merry } 3222d3c7b9a0SKenneth D. Merry 3223d3c7b9a0SKenneth D. Merry static void 3224d3c7b9a0SKenneth D. Merry mps_config_complete(struct mps_softc *sc, struct mps_command *cm) 3225d3c7b9a0SKenneth D. Merry { 3226d3c7b9a0SKenneth D. Merry MPI2_CONFIG_REPLY *reply; 3227d3c7b9a0SKenneth D. Merry struct mps_config_params *params; 3228d3c7b9a0SKenneth D. Merry 32291610f95cSScott Long MPS_FUNCTRACE(sc); 3230d3c7b9a0SKenneth D. Merry params = cm->cm_complete_data; 3231d3c7b9a0SKenneth D. Merry 3232d3c7b9a0SKenneth D. Merry if (cm->cm_data != NULL) { 3233d3c7b9a0SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, 3234d3c7b9a0SKenneth D. Merry BUS_DMASYNC_POSTREAD); 3235d3c7b9a0SKenneth D. Merry bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); 3236d3c7b9a0SKenneth D. Merry } 3237d3c7b9a0SKenneth D. Merry 3238550e2acdSKenneth D. Merry /* 3239550e2acdSKenneth D. Merry * XXX KDM need to do more error recovery? This results in the 3240550e2acdSKenneth D. Merry * device in question not getting probed. 3241550e2acdSKenneth D. Merry */ 3242550e2acdSKenneth D. Merry if ((cm->cm_flags & MPS_CM_FLAGS_ERROR_MASK) != 0) { 3243550e2acdSKenneth D. Merry params->status = MPI2_IOCSTATUS_BUSY; 3244d043c564SKenneth D. Merry goto done; 3245550e2acdSKenneth D. Merry } 3246550e2acdSKenneth D. Merry 3247d3c7b9a0SKenneth D. Merry reply = (MPI2_CONFIG_REPLY *)cm->cm_reply; 3248d043c564SKenneth D. Merry if (reply == NULL) { 3249d043c564SKenneth D. Merry params->status = MPI2_IOCSTATUS_BUSY; 3250d043c564SKenneth D. Merry goto done; 3251d043c564SKenneth D. Merry } 3252d3c7b9a0SKenneth D. Merry params->status = reply->IOCStatus; 3253fcafcbcbSScott Long if (params->hdr.Struct.PageType == MPI2_CONFIG_PAGETYPE_EXTENDED) { 3254d3c7b9a0SKenneth D. Merry params->hdr.Ext.ExtPageType = reply->ExtPageType; 3255d3c7b9a0SKenneth D. Merry params->hdr.Ext.ExtPageLength = reply->ExtPageLength; 3256fcafcbcbSScott Long params->hdr.Ext.PageType = reply->Header.PageType; 3257fcafcbcbSScott Long params->hdr.Ext.PageNumber = reply->Header.PageNumber; 3258fcafcbcbSScott Long params->hdr.Ext.PageVersion = reply->Header.PageVersion; 3259d3c7b9a0SKenneth D. Merry } else { 3260d3c7b9a0SKenneth D. Merry params->hdr.Struct.PageType = reply->Header.PageType; 3261d3c7b9a0SKenneth D. Merry params->hdr.Struct.PageNumber = reply->Header.PageNumber; 3262d3c7b9a0SKenneth D. Merry params->hdr.Struct.PageLength = reply->Header.PageLength; 3263d3c7b9a0SKenneth D. Merry params->hdr.Struct.PageVersion = reply->Header.PageVersion; 3264d3c7b9a0SKenneth D. Merry } 3265d3c7b9a0SKenneth D. Merry 3266d043c564SKenneth D. Merry done: 3267d3c7b9a0SKenneth D. Merry mps_free_command(sc, cm); 3268d3c7b9a0SKenneth D. Merry if (params->callback != NULL) 3269d3c7b9a0SKenneth D. Merry params->callback(sc, params); 3270d3c7b9a0SKenneth D. Merry 3271d3c7b9a0SKenneth D. Merry return; 3272d3c7b9a0SKenneth D. Merry } 3273