twe.c revision 91790
160894Smsmith/*- 260894Smsmith * Copyright (c) 2000 Michael Smith 360894Smsmith * Copyright (c) 2000 BSDi 460894Smsmith * All rights reserved. 560894Smsmith * 660894Smsmith * Redistribution and use in source and binary forms, with or without 760894Smsmith * modification, are permitted provided that the following conditions 860894Smsmith * are met: 960894Smsmith * 1. Redistributions of source code must retain the above copyright 1060894Smsmith * notice, this list of conditions and the following disclaimer. 1160894Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1260894Smsmith * notice, this list of conditions and the following disclaimer in the 1360894Smsmith * documentation and/or other materials provided with the distribution. 1460894Smsmith * 1560894Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1660894Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1760894Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1860894Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1960894Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2060894Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2160894Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2260894Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2360894Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2460894Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2560894Smsmith * SUCH DAMAGE. 2660894Smsmith * 2760894Smsmith * $FreeBSD: head/sys/dev/twe/twe.c 91790 2002-03-07 09:55:41Z msmith $ 2860894Smsmith */ 2960894Smsmith 3060894Smsmith/* 3160894Smsmith * Driver for the 3ware Escalade family of IDE RAID controllers. 3260894Smsmith */ 3360894Smsmith 3467555Smsmith#include <dev/twe/twe_compat.h> 3560894Smsmith#include <dev/twe/twereg.h> 3667555Smsmith#include <dev/twe/tweio.h> 3760894Smsmith#include <dev/twe/twevar.h> 3867555Smsmith#define TWE_DEFINE_TABLES 3967555Smsmith#include <dev/twe/twe_tables.h> 4060894Smsmith 4160894Smsmith/* 4260894Smsmith * Command submission. 4360894Smsmith */ 4467555Smsmithstatic int twe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result); 4567555Smsmithstatic int twe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result); 4667555Smsmithstatic int twe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result); 4767555Smsmithstatic void *twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, 4860894Smsmith void (* func)(struct twe_request *tr)); 4991449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 5067555Smsmithstatic int twe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value); 5191449Speter#endif 5291449Speter#if 0 5367555Smsmithstatic int twe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value); 5467555Smsmithstatic int twe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value); 5591449Speter#endif 5667555Smsmithstatic int twe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, 5767555Smsmith void *data); 5867555Smsmithstatic int twe_init_connection(struct twe_softc *sc, int mode); 5967555Smsmithstatic int twe_wait_request(struct twe_request *tr); 6067555Smsmithstatic int twe_immediate_request(struct twe_request *tr); 6167555Smsmithstatic void twe_completeio(struct twe_request *tr); 6267555Smsmithstatic void twe_reset(struct twe_softc *sc); 6360894Smsmith 6460894Smsmith/* 6560894Smsmith * Command I/O to controller. 6660894Smsmith */ 6767555Smsmithstatic int twe_start(struct twe_request *tr); 6867555Smsmithstatic void twe_done(struct twe_softc *sc); 6967555Smsmithstatic void twe_complete(struct twe_softc *sc); 7067555Smsmithstatic int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout); 7167555Smsmithstatic int twe_drain_response_queue(struct twe_softc *sc); 7267555Smsmithstatic int twe_check_bits(struct twe_softc *sc, u_int32_t status_reg); 7367555Smsmithstatic int twe_soft_reset(struct twe_softc *sc); 7460894Smsmith 7560894Smsmith/* 7660894Smsmith * Interrupt handling. 7760894Smsmith */ 7867555Smsmithstatic void twe_host_intr(struct twe_softc *sc); 7967555Smsmithstatic void twe_attention_intr(struct twe_softc *sc); 8067555Smsmithstatic void twe_command_intr(struct twe_softc *sc); 8160894Smsmith 8260894Smsmith/* 8360894Smsmith * Asynchronous event handling. 8460894Smsmith */ 8567555Smsmithstatic int twe_fetch_aen(struct twe_softc *sc); 8667555Smsmithstatic void twe_handle_aen(struct twe_request *tr); 8767555Smsmithstatic void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen); 8867555Smsmithstatic int twe_dequeue_aen(struct twe_softc *sc); 8967555Smsmithstatic int twe_drain_aen_queue(struct twe_softc *sc); 9067555Smsmithstatic int twe_find_aen(struct twe_softc *sc, u_int16_t aen); 9160894Smsmith 9260894Smsmith/* 9360894Smsmith * Command buffer management. 9460894Smsmith */ 9567555Smsmithstatic int twe_get_request(struct twe_softc *sc, struct twe_request **tr); 9667555Smsmithstatic void twe_release_request(struct twe_request *tr); 9760894Smsmith 9860894Smsmith/* 9960894Smsmith * Debugging. 10060894Smsmith */ 10167555Smsmithstatic char *twe_format_aen(struct twe_softc *sc, u_int16_t aen); 10269543Smsmithstatic int twe_report_request(struct twe_request *tr); 10367555Smsmithstatic void twe_panic(struct twe_softc *sc, char *reason); 10460894Smsmith 10560894Smsmith/******************************************************************************** 10660894Smsmith ******************************************************************************** 10760894Smsmith Public Interfaces 10860894Smsmith ******************************************************************************** 10960894Smsmith ********************************************************************************/ 11060894Smsmith 11160894Smsmith/******************************************************************************** 11267555Smsmith * Initialise the controller, set up driver data structures. 11360894Smsmith */ 11467555Smsmithint 11567555Smsmithtwe_setup(struct twe_softc *sc) 11660894Smsmith{ 11760894Smsmith struct twe_request *tr; 11891790Smsmith u_int32_t status_reg; 11967555Smsmith int i; 12060894Smsmith 12160894Smsmith debug_called(4); 12260894Smsmith 12360894Smsmith /* 12467555Smsmith * Initialise request queues. 12560894Smsmith */ 12667555Smsmith twe_initq_free(sc); 12767555Smsmith twe_initq_bio(sc); 12867555Smsmith twe_initq_ready(sc); 12967555Smsmith twe_initq_busy(sc); 13067555Smsmith twe_initq_complete(sc); 13167555Smsmith sc->twe_wait_aen = -1; 13260894Smsmith 13360894Smsmith /* 13467555Smsmith * Allocate request structures up front. 13560894Smsmith */ 13667555Smsmith for (i = 0; i < TWE_Q_LENGTH; i++) { 13767555Smsmith if ((tr = twe_allocate_request(sc)) == NULL) 13867555Smsmith return(ENOMEM); 13967555Smsmith /* 14067555Smsmith * Set global defaults that won't change. 14167555Smsmith */ 14267555Smsmith tr->tr_command.generic.host_id = sc->twe_host_id; /* controller-assigned host ID */ 14367555Smsmith tr->tr_command.generic.request_id = i; /* our index number */ 14467555Smsmith sc->twe_lookup[i] = tr; 14560894Smsmith 14667555Smsmith /* 14767555Smsmith * Put command onto the freelist. 14867555Smsmith */ 14967555Smsmith twe_release_request(tr); 15060894Smsmith } 15160894Smsmith 15260894Smsmith /* 15391790Smsmith * Check status register for errors, clear them. 15491790Smsmith */ 15591790Smsmith status_reg = TWE_STATUS(sc); 15691790Smsmith twe_check_bits(sc, status_reg); 15791790Smsmith 15891790Smsmith /* 15967555Smsmith * Wait for the controller to come ready. 16060894Smsmith */ 16167555Smsmith if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { 16267555Smsmith twe_printf(sc, "microcontroller not ready\n"); 16360894Smsmith return(ENXIO); 16460894Smsmith } 16560894Smsmith 16660894Smsmith /* 16767555Smsmith * Disable interrupts from the card. 16860894Smsmith */ 16967555Smsmith twe_disable_interrupts(sc); 17060894Smsmith 17160894Smsmith /* 17267555Smsmith * Soft reset the controller, look for the AEN acknowledging the reset, 17367555Smsmith * check for errors, drain the response queue. 17460894Smsmith */ 17567555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 17660894Smsmith 17767555Smsmith if (i > 0) 17867555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 17967555Smsmith 18067555Smsmith if (!twe_soft_reset(sc)) 18167555Smsmith break; /* reset process complete */ 18267555Smsmith } 18367555Smsmith /* did we give up? */ 18467555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 18567555Smsmith twe_printf(sc, "can't initialise controller, giving up\n"); 18660894Smsmith return(ENXIO); 18760894Smsmith } 18860894Smsmith 18960894Smsmith return(0); 19060894Smsmith} 19160894Smsmith 19260894Smsmith/******************************************************************************** 19367555Smsmith * Locate disk devices and attach children to them. 19460894Smsmith */ 19567555Smsmithvoid 19667555Smsmithtwe_init(struct twe_softc *sc) 19760894Smsmith{ 19860894Smsmith struct twe_drive *dr; 19967555Smsmith int i, table; 20067555Smsmith u_int16_t dsize; 20167555Smsmith TWE_Param *drives, *param; 20267555Smsmith TWE_Unit_Descriptor *ud; 20360894Smsmith 20460894Smsmith 20560894Smsmith /* 20667555Smsmith * The controller is in a safe state, so try to find drives attached to it. 20760894Smsmith */ 20867555Smsmith if ((drives = twe_get_param(sc, TWE_PARAM_UNITSUMMARY, TWE_PARAM_UNITSUMMARY_Status, 20967555Smsmith TWE_MAX_UNITS, NULL)) == NULL) { 21067555Smsmith twe_printf(sc, "can't detect attached units\n"); 21160894Smsmith return; 21260894Smsmith } 21367555Smsmith 21460894Smsmith /* 21567555Smsmith * For each detected unit, create a child device. 21660894Smsmith */ 21767555Smsmith for (i = 0, dr = &sc->twe_drive[0]; i < TWE_MAX_UNITS; i++, dr++) { 21860894Smsmith 21967555Smsmith /* check that the drive is online */ 22067555Smsmith if (!(drives->data[i] & TWE_PARAM_UNITSTATUS_Online)) 22167555Smsmith continue; 22260894Smsmith 22367555Smsmith table = TWE_PARAM_UNITINFO + i; 22460894Smsmith 22567555Smsmith if (twe_get_param_4(sc, table, TWE_PARAM_UNITINFO_Capacity, &dr->td_size)) { 22667555Smsmith twe_printf(sc, "error fetching capacity for unit %d\n", i); 22760894Smsmith continue; 22860894Smsmith } 22967555Smsmith if (twe_get_param_1(sc, table, TWE_PARAM_UNITINFO_Status, &dr->td_state)) { 23067555Smsmith twe_printf(sc, "error fetching state for unit %d\n", i); 23160894Smsmith continue; 23260894Smsmith } 23367555Smsmith if (twe_get_param_2(sc, table, TWE_PARAM_UNITINFO_DescriptorSize, &dsize)) { 23467555Smsmith twe_printf(sc, "error fetching descriptor size for unit %d\n", i); 23560894Smsmith continue; 23660894Smsmith } 23767555Smsmith if ((param = twe_get_param(sc, table, TWE_PARAM_UNITINFO_Descriptor, dsize - 3, NULL)) == NULL) { 23867555Smsmith twe_printf(sc, "error fetching descriptor for unit %d\n", i); 23960894Smsmith continue; 24060894Smsmith } 24167555Smsmith ud = (TWE_Unit_Descriptor *)param->data; 24267555Smsmith dr->td_type = ud->configuration; 24367555Smsmith free(param, M_DEVBUF); 24460894Smsmith 24560894Smsmith /* build synthetic geometry as per controller internal rules */ 24660894Smsmith if (dr->td_size > 0x200000) { 24760894Smsmith dr->td_heads = 255; 24860894Smsmith dr->td_sectors = 63; 24960894Smsmith } else { 25060894Smsmith dr->td_heads = 64; 25160894Smsmith dr->td_sectors = 32; 25260894Smsmith } 25360894Smsmith dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors); 25460894Smsmith dr->td_unit = i; 25560894Smsmith 25667555Smsmith twe_attach_drive(sc, dr); 25760894Smsmith } 25860894Smsmith free(drives, M_DEVBUF); 25960894Smsmith 26060894Smsmith /* 26160894Smsmith * Initialise connection with controller. 26260894Smsmith */ 26367555Smsmith twe_init_connection(sc, TWE_INIT_MESSAGE_CREDITS); 26460894Smsmith 26567555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 26667555Smsmith /* 26767555Smsmith * Tell the controller we support shutdown notification. 26867555Smsmith */ 26967555Smsmith twe_set_param_1(sc, TWE_PARAM_FEATURES, TWE_PARAM_FEATURES_DriverShutdown, 1); 27067555Smsmith#endif 27167555Smsmith 27260894Smsmith /* 27360894Smsmith * Mark controller up and ready to run. 27460894Smsmith */ 27560894Smsmith sc->twe_state &= ~TWE_STATE_SHUTDOWN; 27660894Smsmith 27760894Smsmith /* 27867555Smsmith * Finally enable interrupts. 27960894Smsmith */ 28060894Smsmith twe_enable_interrupts(sc); 28160894Smsmith} 28260894Smsmith 28360894Smsmith/******************************************************************************** 28467555Smsmith * Stop the controller 28560894Smsmith */ 28667555Smsmithvoid 28767555Smsmithtwe_deinit(struct twe_softc *sc) 28860894Smsmith{ 28960894Smsmith /* 29060894Smsmith * Mark the controller as shutting down, and disable any further interrupts. 29160894Smsmith */ 29260894Smsmith sc->twe_state |= TWE_STATE_SHUTDOWN; 29360894Smsmith twe_disable_interrupts(sc); 29460894Smsmith 29567555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 29667555Smsmith /* 29767555Smsmith * Disconnect from the controller 29860894Smsmith */ 29967555Smsmith twe_init_connection(sc, TWE_SHUTDOWN_MESSAGE_CREDITS); 30067555Smsmith#endif 30160894Smsmith} 30260894Smsmith 30360894Smsmith/******************************************************************************* 30460894Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy 30560894Smsmith * status. 30660894Smsmith */ 30767555Smsmithvoid 30867555Smsmithtwe_intr(struct twe_softc *sc) 30960894Smsmith{ 31060894Smsmith u_int32_t status_reg; 31160894Smsmith 31260894Smsmith debug_called(4); 31360894Smsmith 31460894Smsmith /* 31560894Smsmith * Collect current interrupt status. 31660894Smsmith */ 31760894Smsmith status_reg = TWE_STATUS(sc); 31860894Smsmith twe_check_bits(sc, status_reg); 31960894Smsmith 32060894Smsmith /* 32160894Smsmith * Dispatch based on interrupt status 32260894Smsmith */ 32360894Smsmith if (status_reg & TWE_STATUS_HOST_INTERRUPT) 32460894Smsmith twe_host_intr(sc); 32560894Smsmith if (status_reg & TWE_STATUS_ATTENTION_INTERRUPT) 32660894Smsmith twe_attention_intr(sc); 32760894Smsmith if (status_reg & TWE_STATUS_COMMAND_INTERRUPT) 32860894Smsmith twe_command_intr(sc); 32973104Smsmith if (status_reg & TWE_STATUS_RESPONSE_INTERRUPT) 33060894Smsmith twe_done(sc); 33160894Smsmith}; 33260894Smsmith 33369543Smsmith/******************************************************************************** 33469543Smsmith * Pull as much work off the softc's work queue as possible and give it to the 33569543Smsmith * controller. 33660894Smsmith */ 33769543Smsmithvoid 33869543Smsmithtwe_startio(struct twe_softc *sc) 33960894Smsmith{ 34069543Smsmith struct twe_request *tr; 34169543Smsmith TWE_Command *cmd; 34269543Smsmith twe_bio *bp; 34369543Smsmith int error; 34469543Smsmith 34567555Smsmith debug_called(4); 34660894Smsmith 34769543Smsmith /* spin until something prevents us from doing any work */ 34869543Smsmith for (;;) { 34967555Smsmith 35069543Smsmith /* try to get a command that's already ready to go */ 35169543Smsmith tr = twe_dequeue_ready(sc); 35269543Smsmith 35369543Smsmith /* build a command from an outstanding bio */ 35469543Smsmith if (tr == NULL) { 35569543Smsmith 35669543Smsmith /* see if there's work to be done */ 35769543Smsmith if ((bp = twe_dequeue_bio(sc)) == NULL) 35869543Smsmith break; 35969543Smsmith 36069543Smsmith /* get a command to handle the bio with */ 36169543Smsmith if (twe_get_request(sc, &tr)) { 36269543Smsmith twe_enqueue_bio(sc, bp); /* failed, put the bio back */ 36369543Smsmith break; 36469543Smsmith } 36569543Smsmith 36669543Smsmith /* connect the bio to the command */ 36769543Smsmith tr->tr_complete = twe_completeio; 36869543Smsmith tr->tr_private = bp; 36969543Smsmith tr->tr_data = TWE_BIO_DATA(bp); 37069543Smsmith tr->tr_length = TWE_BIO_LENGTH(bp); 37169543Smsmith cmd = &tr->tr_command; 37269543Smsmith if (TWE_BIO_IS_READ(bp)) { 37369543Smsmith tr->tr_flags |= TWE_CMD_DATAIN; 37469543Smsmith cmd->io.opcode = TWE_OP_READ; 37569543Smsmith } else { 37669543Smsmith tr->tr_flags |= TWE_CMD_DATAOUT; 37769543Smsmith cmd->io.opcode = TWE_OP_WRITE; 37869543Smsmith } 37969543Smsmith 38069543Smsmith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 38169543Smsmith cmd->io.size = 3; 38269543Smsmith cmd->io.unit = TWE_BIO_UNIT(bp); 38369543Smsmith cmd->io.block_count = (tr->tr_length + TWE_BLOCK_SIZE - 1) / TWE_BLOCK_SIZE; 38469543Smsmith cmd->io.lba = TWE_BIO_LBA(bp); 38569543Smsmith 38669543Smsmith /* map the command so the controller can work with it */ 38769543Smsmith twe_map_request(tr); 38869543Smsmith } 38969543Smsmith 39069543Smsmith /* did we find something to do? */ 39169543Smsmith if (tr == NULL) 39269543Smsmith break; 39369543Smsmith 39469543Smsmith /* try to give command to controller */ 39569543Smsmith error = twe_start(tr); 39669543Smsmith 39769543Smsmith if (error != 0) { 39869543Smsmith if (error == EBUSY) { 39969543Smsmith twe_requeue_ready(tr); /* try it again later */ 40069543Smsmith break; /* don't try anything more for now */ 40169543Smsmith } 40269543Smsmith /* we don't support any other return from twe_start */ 40369543Smsmith twe_panic(sc, "twe_start returned nonsense"); 40469543Smsmith } 40569543Smsmith } 40660894Smsmith} 40760894Smsmith 40860894Smsmith/******************************************************************************** 40969543Smsmith * Write blocks from memory to disk, for system crash dumps. 41069543Smsmith */ 41169543Smsmithint 41269543Smsmithtwe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) 41369543Smsmith{ 41469543Smsmith struct twe_request *tr; 41569543Smsmith TWE_Command *cmd; 41669543Smsmith int error; 41769543Smsmith 41869543Smsmith if (twe_get_request(sc, &tr)) 41969543Smsmith return(ENOMEM); 42069543Smsmith 42169543Smsmith tr->tr_data = data; 42269543Smsmith tr->tr_status = TWE_CMD_SETUP; 42369543Smsmith tr->tr_length = nblks * TWE_BLOCK_SIZE; 42469543Smsmith tr->tr_flags = TWE_CMD_DATAOUT; 42569543Smsmith 42669543Smsmith cmd = &tr->tr_command; 42769543Smsmith cmd->io.opcode = TWE_OP_WRITE; 42869543Smsmith cmd->io.size = 3; 42969543Smsmith cmd->io.unit = unit; 43069543Smsmith cmd->io.block_count = nblks; 43169543Smsmith cmd->io.lba = lba; 43269543Smsmith 43369543Smsmith twe_map_request(tr); 43469543Smsmith error = twe_immediate_request(tr); 43569543Smsmith if (error == 0) 43669543Smsmith if (twe_report_request(tr)) 43769543Smsmith error = EIO; 43869543Smsmith twe_release_request(tr); 43969543Smsmith return(error); 44069543Smsmith} 44169543Smsmith 44269543Smsmith/******************************************************************************** 44367555Smsmith * Handle controller-specific control operations. 44460894Smsmith */ 44567555Smsmithint 44667555Smsmithtwe_ioctl(struct twe_softc *sc, int cmd, void *addr) 44760894Smsmith{ 44867555Smsmith struct twe_usercommand *tu = (struct twe_usercommand *)addr; 44967555Smsmith struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; 45067555Smsmith union twe_statrequest *ts = (union twe_statrequest *)addr; 45167555Smsmith TWE_Param *param; 45267555Smsmith void *data; 45367555Smsmith int *arg = (int *)addr; 45467555Smsmith struct twe_request *tr; 45569543Smsmith u_int8_t srid; 45667555Smsmith int s, error; 45760894Smsmith 45867555Smsmith error = 0; 45967555Smsmith switch(cmd) { 46067555Smsmith /* handle a command from userspace */ 46167555Smsmith case TWEIO_COMMAND: 46267555Smsmith /* get a request */ 46367555Smsmith if (twe_get_request(sc, &tr)) { 46467555Smsmith error = EBUSY; 46567555Smsmith goto cmd_done; 46667555Smsmith } 46767555Smsmith 46869543Smsmith /* 46969543Smsmith * Save the command's request ID, copy the user-supplied command in, 47069543Smsmith * restore the request ID. 47169543Smsmith */ 47269543Smsmith srid = tr->tr_command.generic.request_id; 47367555Smsmith bcopy(&tu->tu_command, &tr->tr_command, sizeof(TWE_Command)); 47469543Smsmith tr->tr_command.generic.request_id = srid; 47567555Smsmith 47667555Smsmith /* if there's a data buffer, allocate and copy it in */ 47767555Smsmith tr->tr_length = tu->tu_size; 47867555Smsmith if (tr->tr_length > 0) { 47967555Smsmith if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { 48067555Smsmith error = ENOMEM; 48167555Smsmith goto cmd_done; 48267555Smsmith } 48367555Smsmith if ((error = copyin(tu->tu_data, tr->tr_data, tr->tr_length)) != 0) 48467555Smsmith goto cmd_done; 48567555Smsmith tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 48667555Smsmith } 48767555Smsmith 48867555Smsmith /* run the command */ 48969543Smsmith twe_map_request(tr); 49067555Smsmith twe_wait_request(tr); 49167555Smsmith 49267555Smsmith /* copy the command out again */ 49367555Smsmith bcopy(&tr->tr_command, &tu->tu_command, sizeof(TWE_Command)); 49467555Smsmith 49567555Smsmith /* if there was a data buffer, copy it out */ 49667555Smsmith if (tr->tr_length > 0) 49767555Smsmith error = copyout(tr->tr_data, tu->tu_data, tr->tr_length); 49867555Smsmith 49967555Smsmith cmd_done: 50067555Smsmith /* free resources */ 50167555Smsmith if (tr->tr_data != NULL) 50267555Smsmith free(tr->tr_data, M_DEVBUF); 50367555Smsmith if (tr != NULL) 50467555Smsmith twe_release_request(tr); 50567555Smsmith 50667555Smsmith break; 50767555Smsmith 50867555Smsmith /* fetch statistics counter */ 50967555Smsmith case TWEIO_STATS: 51067555Smsmith switch (ts->ts_item) { 51167555Smsmith#ifdef TWE_PERFORMANCE_MONITOR 51267555Smsmith case TWEQ_FREE: 51367555Smsmith case TWEQ_BIO: 51467555Smsmith case TWEQ_READY: 51567555Smsmith case TWEQ_BUSY: 51667555Smsmith case TWEQ_COMPLETE: 51767555Smsmith bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); 51867555Smsmith break; 51967555Smsmith#endif 52067555Smsmith default: 52167555Smsmith error = ENOENT; 52267555Smsmith break; 52367555Smsmith } 52467555Smsmith break; 52567555Smsmith 52667555Smsmith /* poll for an AEN */ 52767555Smsmith case TWEIO_AEN_POLL: 52867555Smsmith *arg = twe_dequeue_aen(sc); 52967555Smsmith if (*arg == -1) 53067555Smsmith error = ENOENT; 53167555Smsmith break; 53267555Smsmith 53367555Smsmith /* wait for another AEN to show up */ 53467555Smsmith case TWEIO_AEN_WAIT: 53567555Smsmith s = splbio(); 53667555Smsmith while ((*arg = twe_dequeue_aen(sc)) == -1) { 53767555Smsmith error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); 53867555Smsmith if (error == EINTR) 53967555Smsmith break; 54067555Smsmith } 54167555Smsmith splx(s); 54267555Smsmith break; 54367555Smsmith 54467555Smsmith case TWEIO_GET_PARAM: 54567555Smsmith if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { 54667555Smsmith twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", 54767555Smsmith tp->tp_table_id, tp->tp_param_id, tp->tp_size); 54867555Smsmith error = EINVAL; 54967555Smsmith } else { 55067555Smsmith if (param->parameter_size_bytes > tp->tp_size) { 55167555Smsmith twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", 55267555Smsmith param->parameter_size_bytes, tp->tp_size); 55367555Smsmith error = EFAULT; 55467555Smsmith } else { 55567555Smsmith error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); 55667555Smsmith } 55767555Smsmith free(param, M_DEVBUF); 55867555Smsmith } 55967555Smsmith break; 56067555Smsmith 56167555Smsmith case TWEIO_SET_PARAM: 56267555Smsmith if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { 56367555Smsmith error = ENOMEM; 56467555Smsmith } else { 56567555Smsmith error = copyin(tp->tp_data, data, tp->tp_size); 56667555Smsmith if (error == 0) 56767555Smsmith error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); 56867555Smsmith free(data, M_DEVBUF); 56967555Smsmith } 57067555Smsmith break; 57167555Smsmith 57267555Smsmith case TWEIO_RESET: 57367555Smsmith twe_reset(sc); 57467555Smsmith break; 57567555Smsmith 57691790Smsmith /* XXX implement ATA PASSTHROUGH */ 57791790Smsmith 57867555Smsmith /* nothing we understand */ 57967555Smsmith default: 58067555Smsmith error = ENOTTY; 58167555Smsmith } 58267555Smsmith 58367555Smsmith return(error); 58460894Smsmith} 58560894Smsmith 58660894Smsmith/******************************************************************************** 58767555Smsmith * Enable the useful interrupts from the controller. 58860894Smsmith */ 58967555Smsmithvoid 59067555Smsmithtwe_enable_interrupts(struct twe_softc *sc) 59160894Smsmith{ 59267555Smsmith sc->twe_state |= TWE_STATE_INTEN; 59367555Smsmith TWE_CONTROL(sc, 59467555Smsmith TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | 59567555Smsmith TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | 59667555Smsmith TWE_CONTROL_ENABLE_INTERRUPTS); 59760894Smsmith} 59860894Smsmith 59960894Smsmith/******************************************************************************** 60067555Smsmith * Disable interrupts from the controller. 60167555Smsmith */ 60267555Smsmithvoid 60367555Smsmithtwe_disable_interrupts(struct twe_softc *sc) 60467555Smsmith{ 60567555Smsmith TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); 60667555Smsmith sc->twe_state &= ~TWE_STATE_INTEN; 60767555Smsmith} 60867555Smsmith 60967555Smsmith/******************************************************************************** 61060894Smsmith ******************************************************************************** 61160894Smsmith Command Submission 61260894Smsmith ******************************************************************************** 61360894Smsmith ********************************************************************************/ 61460894Smsmith 61567555Smsmith/******************************************************************************** 61667555Smsmith * Read integer parameter table entries. 61760894Smsmith */ 61867555Smsmithstatic int 61967555Smsmithtwe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) 62060894Smsmith{ 62167555Smsmith TWE_Param *param; 62260894Smsmith 62367555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) 62467555Smsmith return(ENOENT); 62567555Smsmith *result = *(u_int8_t *)param->data; 62667555Smsmith free(param, M_DEVBUF); 62767555Smsmith return(0); 62867555Smsmith} 62960894Smsmith 63067555Smsmithstatic int 63167555Smsmithtwe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) 63267555Smsmith{ 63367555Smsmith TWE_Param *param; 63467555Smsmith 63567555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) 63667555Smsmith return(ENOENT); 63767555Smsmith *result = *(u_int16_t *)param->data; 63867555Smsmith free(param, M_DEVBUF); 63960894Smsmith return(0); 64060894Smsmith} 64160894Smsmith 64267555Smsmithstatic int 64367555Smsmithtwe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) 64467555Smsmith{ 64567555Smsmith TWE_Param *param; 64667555Smsmith 64767555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) 64867555Smsmith return(ENOENT); 64967555Smsmith *result = *(u_int32_t *)param->data; 65067555Smsmith free(param, M_DEVBUF); 65167555Smsmith return(0); 65267555Smsmith} 65367555Smsmith 65460894Smsmith/******************************************************************************** 65560894Smsmith * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it 65660894Smsmith * will be called with the command when it's completed. If no callback is 65760894Smsmith * provided, we will wait for the command to complete and then return just the data. 65860894Smsmith * The caller is responsible for freeing the data when done with it. 65960894Smsmith */ 66060894Smsmithstatic void * 66167555Smsmithtwe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, 66267555Smsmith void (* func)(struct twe_request *tr)) 66360894Smsmith{ 66460894Smsmith struct twe_request *tr; 66560894Smsmith TWE_Command *cmd; 66660894Smsmith TWE_Param *param; 66760894Smsmith int error; 66860894Smsmith 66960894Smsmith debug_called(4); 67060894Smsmith 67160894Smsmith tr = NULL; 67260894Smsmith param = NULL; 67360894Smsmith 67460894Smsmith /* get a command */ 67567555Smsmith if (twe_get_request(sc, &tr)) 67660894Smsmith goto err; 67760894Smsmith 67860894Smsmith /* get a buffer */ 67960894Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 68060894Smsmith goto err; 68160894Smsmith tr->tr_data = param; 68260894Smsmith tr->tr_length = TWE_SECTOR_SIZE; 68360894Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 68460894Smsmith 68560894Smsmith /* build the command for the controller */ 68660894Smsmith cmd = &tr->tr_command; 68767555Smsmith cmd->param.opcode = TWE_OP_GET_PARAM; 68867555Smsmith cmd->param.size = 2; 68967555Smsmith cmd->param.unit = 0; 69067555Smsmith cmd->param.param_count = 1; 69160894Smsmith 69260894Smsmith /* map the command/data into controller-visible space */ 69360894Smsmith twe_map_request(tr); 69460894Smsmith 69560894Smsmith /* fill in the outbound parameter data */ 69660894Smsmith param->table_id = table_id; 69767555Smsmith param->parameter_id = param_id; 69867555Smsmith param->parameter_size_bytes = param_size; 69960894Smsmith 70060894Smsmith /* submit the command and either wait or let the callback handle it */ 70160894Smsmith if (func == NULL) { 70260894Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 70360894Smsmith error = twe_immediate_request(tr); 70460894Smsmith if (error == 0) { 70569543Smsmith if (twe_report_request(tr)) 70660894Smsmith goto err; 70760894Smsmith } 70867555Smsmith twe_release_request(tr); 70967555Smsmith return(param); 71060894Smsmith } else { 71160894Smsmith tr->tr_complete = func; 71260894Smsmith error = twe_start(tr); 71360894Smsmith if (error == 0) 71460894Smsmith return(func); 71560894Smsmith } 71660894Smsmith 71760894Smsmith /* something failed */ 71860894Smsmitherr: 71960894Smsmith debug(1, "failed"); 72060894Smsmith if (tr != NULL) 72160894Smsmith twe_release_request(tr); 72260894Smsmith if (param != NULL) 72360894Smsmith free(param, M_DEVBUF); 72460894Smsmith return(NULL); 72560894Smsmith} 72660894Smsmith 72760894Smsmith/******************************************************************************** 72867555Smsmith * Set integer parameter table entries. 72967555Smsmith */ 73091449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 73167555Smsmithstatic int 73267555Smsmithtwe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) 73367555Smsmith{ 73467555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 73567555Smsmith} 73691449Speter#endif 73767555Smsmith 73891449Speter#if 0 73967555Smsmithstatic int 74067555Smsmithtwe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) 74167555Smsmith{ 74267555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 74367555Smsmith} 74467555Smsmith 74567555Smsmithstatic int 74667555Smsmithtwe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) 74767555Smsmith{ 74867555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 74967555Smsmith} 75091449Speter#endif 75167555Smsmith 75267555Smsmith/******************************************************************************** 75367555Smsmith * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. 75467555Smsmith */ 75567555Smsmithstatic int 75667555Smsmithtwe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) 75767555Smsmith{ 75867555Smsmith struct twe_request *tr; 75967555Smsmith TWE_Command *cmd; 76067555Smsmith TWE_Param *param; 76167555Smsmith int error; 76267555Smsmith 76367555Smsmith debug_called(4); 76467555Smsmith 76567555Smsmith tr = NULL; 76667555Smsmith param = NULL; 76767555Smsmith error = ENOMEM; 76867555Smsmith 76967555Smsmith /* get a command */ 77067555Smsmith if (twe_get_request(sc, &tr)) 77167555Smsmith goto out; 77267555Smsmith 77367555Smsmith /* get a buffer */ 77467555Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 77567555Smsmith goto out; 77667555Smsmith tr->tr_data = param; 77767555Smsmith tr->tr_length = TWE_SECTOR_SIZE; 77867555Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 77967555Smsmith 78067555Smsmith /* build the command for the controller */ 78167555Smsmith cmd = &tr->tr_command; 78267555Smsmith cmd->param.opcode = TWE_OP_SET_PARAM; 78367555Smsmith cmd->param.size = 2; 78467555Smsmith cmd->param.unit = 0; 78567555Smsmith cmd->param.param_count = 1; 78667555Smsmith 78767555Smsmith /* map the command/data into controller-visible space */ 78867555Smsmith twe_map_request(tr); 78967555Smsmith 79067555Smsmith /* fill in the outbound parameter data */ 79167555Smsmith param->table_id = table_id; 79267555Smsmith param->parameter_id = param_id; 79367555Smsmith param->parameter_size_bytes = param_size; 79467555Smsmith bcopy(data, param->data, param_size); 79567555Smsmith 79667555Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 79767555Smsmith error = twe_immediate_request(tr); 79867555Smsmith if (error == 0) { 79969543Smsmith if (twe_report_request(tr)) 80067555Smsmith error = EIO; 80167555Smsmith } 80267555Smsmith 80367555Smsmithout: 80467555Smsmith if (tr != NULL) 80567555Smsmith twe_release_request(tr); 80667555Smsmith if (param != NULL) 80767555Smsmith free(param, M_DEVBUF); 80867555Smsmith return(error); 80967555Smsmith} 81067555Smsmith 81167555Smsmith/******************************************************************************** 81260894Smsmith * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. 81360894Smsmith * 81460894Smsmith * Typically called with interrupts disabled. 81560894Smsmith */ 81660894Smsmithstatic int 81767555Smsmithtwe_init_connection(struct twe_softc *sc, int mode) 81860894Smsmith{ 81960894Smsmith struct twe_request *tr; 82060894Smsmith TWE_Command *cmd; 82160894Smsmith int error; 82260894Smsmith 82360894Smsmith debug_called(4); 82460894Smsmith 82560894Smsmith /* get a command */ 82667555Smsmith if (twe_get_request(sc, &tr)) 82760894Smsmith return(NULL); 82860894Smsmith 82960894Smsmith /* build the command */ 83060894Smsmith cmd = &tr->tr_command; 83167555Smsmith cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; 83267555Smsmith cmd->initconnection.size = 3; 83367555Smsmith cmd->initconnection.host_id = 0; 83467555Smsmith cmd->initconnection.message_credits = mode; 83567555Smsmith cmd->initconnection.response_queue_pointer = 0; 83660894Smsmith 83760894Smsmith /* map the command into controller-visible space */ 83860894Smsmith twe_map_request(tr); 83960894Smsmith 84060894Smsmith /* submit the command */ 84160894Smsmith error = twe_immediate_request(tr); 84260894Smsmith /* XXX check command result? */ 84360894Smsmith twe_unmap_request(tr); 84460894Smsmith twe_release_request(tr); 84560894Smsmith 84667555Smsmith if (mode == TWE_INIT_MESSAGE_CREDITS) 84767555Smsmith sc->twe_host_id = cmd->initconnection.host_id; 84860894Smsmith return(error); 84960894Smsmith} 85060894Smsmith 85160894Smsmith/******************************************************************************** 85260894Smsmith * Start the command (tr) and sleep waiting for it to complete. 85360894Smsmith * 85460894Smsmith * Successfully completed commands are dequeued. 85560894Smsmith */ 85660894Smsmithstatic int 85760894Smsmithtwe_wait_request(struct twe_request *tr) 85860894Smsmith{ 85967555Smsmith int s; 86060894Smsmith 86160894Smsmith debug_called(4); 86260894Smsmith 86367555Smsmith tr->tr_flags |= TWE_CMD_SLEEPER; 86467555Smsmith tr->tr_status = TWE_CMD_BUSY; 86567555Smsmith twe_enqueue_ready(tr); 86667555Smsmith twe_startio(tr->tr_sc); 86760894Smsmith s = splbio(); 86867555Smsmith while (tr->tr_status == TWE_CMD_BUSY) 86967555Smsmith tsleep(tr, PRIBIO, "twewait", 0); 87060894Smsmith splx(s); 87167555Smsmith 87267555Smsmith return(0); 87360894Smsmith} 87460894Smsmith 87560894Smsmith/******************************************************************************** 87660894Smsmith * Start the command (tr) and busy-wait for it to complete. 87760894Smsmith * This should only be used when interrupts are actually disabled (although it 87860894Smsmith * will work if they are not). 87960894Smsmith */ 88060894Smsmithstatic int 88160894Smsmithtwe_immediate_request(struct twe_request *tr) 88260894Smsmith{ 88360894Smsmith int error; 88460894Smsmith 88560894Smsmith debug_called(4); 88660894Smsmith 88760894Smsmith error = 0; 88860894Smsmith 88960894Smsmith if ((error = twe_start(tr)) != 0) 89060894Smsmith return(error); 89160894Smsmith while (tr->tr_status == TWE_CMD_BUSY){ 89260894Smsmith twe_done(tr->tr_sc); 89360894Smsmith } 89460894Smsmith return(tr->tr_status != TWE_CMD_COMPLETE); 89560894Smsmith} 89660894Smsmith 89760894Smsmith/******************************************************************************** 89860894Smsmith * Handle completion of an I/O command. 89960894Smsmith */ 90060894Smsmithstatic void 90160894Smsmithtwe_completeio(struct twe_request *tr) 90260894Smsmith{ 90360894Smsmith struct twe_softc *sc = tr->tr_sc; 90467555Smsmith twe_bio *bp = (twe_bio *)tr->tr_private; 90560894Smsmith 90660894Smsmith debug_called(4); 90760894Smsmith 90860894Smsmith if (tr->tr_status == TWE_CMD_COMPLETE) { 90967555Smsmith 91069543Smsmith if (twe_report_request(tr)) 91167555Smsmith TWE_BIO_SET_ERROR(bp, EIO); 91267555Smsmith 91369543Smsmith } else { 91469543Smsmith twe_panic(sc, "twe_completeio on incomplete command"); 91560894Smsmith } 91669543Smsmith tr->tr_private = NULL; 91769543Smsmith twed_intr(bp); 91860894Smsmith twe_release_request(tr); 91960894Smsmith} 92060894Smsmith 92160894Smsmith/******************************************************************************** 92267555Smsmith * Reset the controller and pull all the active commands back onto the ready 92367555Smsmith * queue. Used to restart a controller that's exhibiting bad behaviour. 92467555Smsmith */ 92567555Smsmithstatic void 92667555Smsmithtwe_reset(struct twe_softc *sc) 92767555Smsmith{ 92867555Smsmith struct twe_request *tr; 92967555Smsmith int i, s; 93067555Smsmith 93191790Smsmith /* 93291790Smsmith * Sleep for a short period to allow AENs to be signalled. 93391790Smsmith */ 93491790Smsmith tsleep(NULL, PRIBIO, "twereset", hz); 93567555Smsmith 93667555Smsmith /* 93767555Smsmith * Disable interrupts from the controller, and mask any accidental entry 93867555Smsmith * into our interrupt handler. 93967555Smsmith */ 94091790Smsmith twe_printf(sc, "controller reset in progress...\n"); 94167555Smsmith twe_disable_interrupts(sc); 94267555Smsmith s = splbio(); 94367555Smsmith 94467555Smsmith /* 94567555Smsmith * Try to soft-reset the controller. 94667555Smsmith */ 94767555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 94867555Smsmith 94967555Smsmith if (i > 0) 95067555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 95167555Smsmith 95267555Smsmith if (!twe_soft_reset(sc)) 95367555Smsmith break; /* reset process complete */ 95467555Smsmith } 95567555Smsmith /* did we give up? */ 95667555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 95767555Smsmith twe_printf(sc, "can't reset controller, giving up\n"); 95867555Smsmith goto out; 95967555Smsmith } 96067555Smsmith 96167555Smsmith /* 96267555Smsmith * Move all of the commands that were busy back to the ready queue. 96367555Smsmith */ 96467555Smsmith i = 0; 96567555Smsmith while ((tr = twe_dequeue_busy(sc)) != NULL) { 96667555Smsmith twe_enqueue_ready(tr); 96767555Smsmith i++; 96867555Smsmith } 96967555Smsmith 97067555Smsmith /* 97167555Smsmith * Kick the controller to start things going again, then re-enable interrupts. 97267555Smsmith */ 97367555Smsmith twe_startio(sc); 97467555Smsmith twe_enable_interrupts(sc); 97567555Smsmith twe_printf(sc, "controller reset done, %d commands restarted\n", i); 97667555Smsmith 97767555Smsmithout: 97867555Smsmith splx(s); 97967555Smsmith twe_enable_interrupts(sc); 98067555Smsmith} 98167555Smsmith 98267555Smsmith/******************************************************************************** 98360894Smsmith ******************************************************************************** 98460894Smsmith Command I/O to Controller 98560894Smsmith ******************************************************************************** 98660894Smsmith ********************************************************************************/ 98760894Smsmith 98860894Smsmith/******************************************************************************** 98960894Smsmith * Try to deliver (tr) to the controller. 99060894Smsmith * 99160894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 99260894Smsmith */ 99360894Smsmithstatic int 99460894Smsmithtwe_start(struct twe_request *tr) 99560894Smsmith{ 99660894Smsmith struct twe_softc *sc = tr->tr_sc; 99760894Smsmith int i, s, done; 99860894Smsmith u_int32_t status_reg; 99960894Smsmith 100060894Smsmith debug_called(4); 100160894Smsmith 100260894Smsmith /* mark the command as currently being processed */ 100360894Smsmith tr->tr_status = TWE_CMD_BUSY; 100460894Smsmith 100567555Smsmith /* 100667555Smsmith * Spin briefly waiting for the controller to come ready 100767555Smsmith * 100867555Smsmith * XXX it might be more efficient to return EBUSY immediately 100967555Smsmith * and let the command be rescheduled. 101067555Smsmith */ 101160894Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 101260894Smsmith s = splbio(); 101360894Smsmith 101460894Smsmith /* check to see if we can post a command */ 101560894Smsmith status_reg = TWE_STATUS(sc); 101660894Smsmith twe_check_bits(sc, status_reg); 101760894Smsmith 101860894Smsmith if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { 101960894Smsmith TWE_COMMAND_QUEUE(sc, tr->tr_cmdphys); 102060894Smsmith done = 1; 102160894Smsmith /* move command to work queue */ 102267555Smsmith twe_enqueue_busy(tr); 102367555Smsmith#ifdef TWE_DEBUG 102460894Smsmith if (tr->tr_complete != NULL) { 102567555Smsmith debug(3, "queued request %d with callback %p", tr->tr_command.generic.request_id, tr->tr_complete); 102667555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { 102767555Smsmith debug(3, "queued request %d with wait channel %p", tr->tr_command.generic.request_id, tr); 102860894Smsmith } else { 102967555Smsmith debug(3, "queued request %d for polling caller", tr->tr_command.generic.request_id); 103060894Smsmith } 103167555Smsmith#endif 103260894Smsmith } 103360894Smsmith splx(s); /* drop spl to allow completion interrupts */ 103460894Smsmith } 103560894Smsmith 103660894Smsmith /* command is enqueued */ 103760894Smsmith if (done) 103860894Smsmith return(0); 103960894Smsmith 104060894Smsmith /* 104160894Smsmith * We couldn't get the controller to take the command; try submitting it again later. 104260894Smsmith * This should only happen if something is wrong with the controller, or if we have 104360894Smsmith * overestimated the number of commands it can accept. (Should we actually reject 104460894Smsmith * the command at this point?) 104560894Smsmith */ 104660894Smsmith return(EBUSY); 104760894Smsmith} 104860894Smsmith 104960894Smsmith/******************************************************************************** 105060894Smsmith * Poll the controller (sc) for completed commands. 105160894Smsmith * 105260894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 105360894Smsmith */ 105460894Smsmithstatic void 105560894Smsmithtwe_done(struct twe_softc *sc) 105660894Smsmith{ 105760894Smsmith TWE_Response_Queue rq; 105860894Smsmith struct twe_request *tr; 105960894Smsmith int s, found; 106060894Smsmith u_int32_t status_reg; 106160894Smsmith 106260894Smsmith debug_called(5); 106360894Smsmith 106460894Smsmith /* loop collecting completed commands */ 106560894Smsmith found = 0; 106660894Smsmith s = splbio(); 106760894Smsmith for (;;) { 106860894Smsmith status_reg = TWE_STATUS(sc); 106960894Smsmith twe_check_bits(sc, status_reg); /* XXX should this fail? */ 107060894Smsmith 107160894Smsmith if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { 107260894Smsmith found = 1; 107360894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 107467555Smsmith tr = sc->twe_lookup[rq.u.response_id]; /* find command */ 107569543Smsmith if (tr->tr_status != TWE_CMD_BUSY) 107669543Smsmith twe_printf(sc, "completion event for nonbusy command\n"); 107769543Smsmith tr->tr_status = TWE_CMD_COMPLETE; 107869543Smsmith debug(3, "completed request id %d with status %d", 107969543Smsmith tr->tr_command.generic.request_id, tr->tr_command.generic.status); 108069543Smsmith /* move to completed queue */ 108169543Smsmith twe_remove_busy(tr); 108269543Smsmith twe_enqueue_complete(tr); 108360894Smsmith } else { 108460894Smsmith break; /* no response ready */ 108560894Smsmith } 108660894Smsmith } 108760894Smsmith splx(s); 108860894Smsmith 108960894Smsmith /* if we've completed any commands, try posting some more */ 109060894Smsmith if (found) 109160894Smsmith twe_startio(sc); 109260894Smsmith 109360894Smsmith /* handle completion and timeouts */ 109467555Smsmith twe_complete(sc); /* XXX use deferred completion? */ 109560894Smsmith} 109660894Smsmith 109760894Smsmith/******************************************************************************** 109860894Smsmith * Perform post-completion processing for commands on (sc). 109960894Smsmith * 110060894Smsmith * This is split from twe_done as it can be safely deferred and run at a lower 110160894Smsmith * priority level should facilities for such a thing become available. 110260894Smsmith */ 110360894Smsmithstatic void 110460894Smsmithtwe_complete(struct twe_softc *sc) 110560894Smsmith{ 110667555Smsmith struct twe_request *tr; 110760894Smsmith 110860894Smsmith debug_called(5); 110960894Smsmith 111060894Smsmith /* 111167555Smsmith * Pull commands off the completed list, dispatch them appropriately 111260894Smsmith */ 111367555Smsmith while ((tr = twe_dequeue_complete(sc)) != NULL) { 111460894Smsmith 111567555Smsmith /* unmap the command's data buffer */ 111667555Smsmith twe_unmap_request(tr); 111760894Smsmith 111867555Smsmith /* dispatch to suit command originator */ 111967555Smsmith if (tr->tr_complete != NULL) { /* completion callback */ 112067555Smsmith debug(2, "call completion handler %p", tr->tr_complete); 112167555Smsmith tr->tr_complete(tr); 112260894Smsmith 112367555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ 112467555Smsmith debug(2, "wake up command owner on %p", tr); 112567555Smsmith wakeup_one(tr); 112660894Smsmith 112767555Smsmith } else { /* caller is polling command */ 112867555Smsmith debug(2, "command left for owner"); 112960894Smsmith } 113067555Smsmith } 113160894Smsmith} 113260894Smsmith 113360894Smsmith/******************************************************************************** 113460894Smsmith * Wait for (status) to be set in the controller status register for up to 113560894Smsmith * (timeout) seconds. Returns 0 if found, nonzero if we time out. 113660894Smsmith * 113760894Smsmith * Note: this busy-waits, rather than sleeping, since we may be called with 113860894Smsmith * eg. clock interrupts masked. 113960894Smsmith */ 114060894Smsmithstatic int 114160894Smsmithtwe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) 114260894Smsmith{ 114360894Smsmith time_t expiry; 114460894Smsmith u_int32_t status_reg; 114560894Smsmith 114660894Smsmith debug_called(4); 114760894Smsmith 114860894Smsmith expiry = time_second + timeout; 114960894Smsmith 115060894Smsmith do { 115160894Smsmith status_reg = TWE_STATUS(sc); 115260894Smsmith if (status_reg & status) /* got the required bit(s)? */ 115360894Smsmith return(0); 115460894Smsmith DELAY(100000); 115560894Smsmith } while (time_second <= expiry); 115660894Smsmith 115760894Smsmith return(1); 115860894Smsmith} 115960894Smsmith 116060894Smsmith/******************************************************************************** 116160894Smsmith * Drain the response queue, which may contain responses to commands we know 116260894Smsmith * nothing about. 116360894Smsmith */ 116460894Smsmithstatic int 116560894Smsmithtwe_drain_response_queue(struct twe_softc *sc) 116660894Smsmith{ 116760894Smsmith TWE_Response_Queue rq; 116860894Smsmith u_int32_t status_reg; 116960894Smsmith 117060894Smsmith debug_called(4); 117160894Smsmith 117260894Smsmith for (;;) { /* XXX give up eventually? */ 117360894Smsmith status_reg = TWE_STATUS(sc); 117460894Smsmith if (twe_check_bits(sc, status_reg)) 117560894Smsmith return(1); 117660894Smsmith if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) 117760894Smsmith return(0); 117860894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 117960894Smsmith } 118060894Smsmith} 118160894Smsmith 118260894Smsmith/******************************************************************************** 118367555Smsmith * Soft-reset the controller 118467555Smsmith */ 118567555Smsmithstatic int 118667555Smsmithtwe_soft_reset(struct twe_softc *sc) 118767555Smsmith{ 118867555Smsmith u_int32_t status_reg; 118967555Smsmith 119067555Smsmith debug_called(2); 119167555Smsmith 119267555Smsmith TWE_SOFT_RESET(sc); 119367555Smsmith 119491790Smsmith if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) { 119567683Smsmith twe_printf(sc, "no attention interrupt\n"); 119667555Smsmith return(1); 119767555Smsmith } 119891790Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 119967555Smsmith if (twe_drain_aen_queue(sc)) { 120067555Smsmith twe_printf(sc, "can't drain AEN queue\n"); 120167555Smsmith return(1); 120267555Smsmith } 120367555Smsmith if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { 120467555Smsmith twe_printf(sc, "reset not reported\n"); 120567555Smsmith return(1); 120667555Smsmith } 120767555Smsmith status_reg = TWE_STATUS(sc); 120867555Smsmith if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { 120967555Smsmith twe_printf(sc, "controller errors detected\n"); 121067555Smsmith return(1); 121167555Smsmith } 121267555Smsmith if (twe_drain_response_queue(sc)) { 121367555Smsmith twe_printf(sc, "can't drain response queue\n"); 121467555Smsmith return(1); 121567555Smsmith } 121667555Smsmith return(0); 121767555Smsmith} 121867555Smsmith 121967555Smsmith/******************************************************************************** 122060894Smsmith ******************************************************************************** 122160894Smsmith Interrupt Handling 122260894Smsmith ******************************************************************************** 122360894Smsmith ********************************************************************************/ 122460894Smsmith 122560894Smsmith/******************************************************************************** 122660894Smsmith * Host interrupt. 122760894Smsmith * 122860894Smsmith * XXX what does this mean? 122960894Smsmith */ 123060894Smsmithstatic void 123160894Smsmithtwe_host_intr(struct twe_softc *sc) 123260894Smsmith{ 123360894Smsmith debug_called(4); 123460894Smsmith 123567555Smsmith twe_printf(sc, "host interrupt\n"); 123660894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); 123760894Smsmith} 123860894Smsmith 123960894Smsmith/******************************************************************************** 124060894Smsmith * Attention interrupt. 124160894Smsmith * 124260894Smsmith * Signalled when the controller has one or more AENs for us. 124360894Smsmith */ 124460894Smsmithstatic void 124560894Smsmithtwe_attention_intr(struct twe_softc *sc) 124660894Smsmith{ 124760894Smsmith debug_called(4); 124860894Smsmith 124960894Smsmith /* instigate a poll for AENs */ 125060894Smsmith if (twe_fetch_aen(sc)) { 125167555Smsmith twe_printf(sc, "error polling for signalled AEN\n"); 125260894Smsmith } else { 125360894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 125460894Smsmith } 125560894Smsmith} 125660894Smsmith 125760894Smsmith/******************************************************************************** 125860894Smsmith * Command interrupt. 125960894Smsmith * 126060894Smsmith * Signalled when the controller can handle more commands. 126160894Smsmith */ 126260894Smsmithstatic void 126360894Smsmithtwe_command_intr(struct twe_softc *sc) 126460894Smsmith{ 126560894Smsmith debug_called(4); 126660894Smsmith 126760894Smsmith /* 126860894Smsmith * We don't use this, rather we try to submit commands when we receive 126960894Smsmith * them, and when other commands have completed. Mask it so we don't get 127060894Smsmith * another one. 127160894Smsmith */ 127267555Smsmith twe_printf(sc, "command interrupt\n"); 127360894Smsmith TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); 127460894Smsmith} 127560894Smsmith 127660894Smsmith/******************************************************************************** 127760894Smsmith ******************************************************************************** 127860894Smsmith Asynchronous Event Handling 127960894Smsmith ******************************************************************************** 128060894Smsmith ********************************************************************************/ 128160894Smsmith 128260894Smsmith/******************************************************************************** 128360894Smsmith * Request an AEN from the controller. 128460894Smsmith */ 128560894Smsmithstatic int 128660894Smsmithtwe_fetch_aen(struct twe_softc *sc) 128760894Smsmith{ 128860894Smsmith 128960894Smsmith debug_called(4); 129060894Smsmith 129167555Smsmith if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) 129260894Smsmith return(EIO); 129360894Smsmith return(0); 129460894Smsmith} 129560894Smsmith 129660894Smsmith/******************************************************************************** 129760894Smsmith * Handle an AEN returned by the controller. 129860894Smsmith */ 129960894Smsmithstatic void 130060894Smsmithtwe_handle_aen(struct twe_request *tr) 130160894Smsmith{ 130260894Smsmith struct twe_softc *sc = tr->tr_sc; 130360894Smsmith TWE_Param *param; 130460894Smsmith u_int16_t aen; 130560894Smsmith 130660894Smsmith debug_called(4); 130760894Smsmith 130860894Smsmith /* XXX check for command success somehow? */ 130960894Smsmith 131060894Smsmith param = (TWE_Param *)tr->tr_data; 131160894Smsmith aen = *(u_int16_t *)(param->data); 131260894Smsmith 131360894Smsmith free(tr->tr_data, M_DEVBUF); 131460894Smsmith twe_release_request(tr); 131560894Smsmith twe_enqueue_aen(sc, aen); 131660894Smsmith 131760894Smsmith /* XXX poll for more AENs? */ 131860894Smsmith} 131960894Smsmith 132060894Smsmith/******************************************************************************** 132160894Smsmith * Pull AENs out of the controller and park them in the queue, in a context where 132260894Smsmith * interrupts aren't active. Return nonzero if we encounter any errors in the 132360894Smsmith * process of obtaining all the available AENs. 132460894Smsmith */ 132560894Smsmithstatic int 132660894Smsmithtwe_drain_aen_queue(struct twe_softc *sc) 132760894Smsmith{ 132860894Smsmith u_int16_t aen; 132960894Smsmith 133060894Smsmith for (;;) { 133167555Smsmith if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) 133260894Smsmith return(1); 133360894Smsmith if (aen == TWE_AEN_QUEUE_EMPTY) 133460894Smsmith return(0); 133560894Smsmith twe_enqueue_aen(sc, aen); 133660894Smsmith } 133760894Smsmith} 133860894Smsmith 133960894Smsmith/******************************************************************************** 134060894Smsmith * Push an AEN that we've received onto the queue. 134160894Smsmith * 134260894Smsmith * Note that we have to lock this against reentrance, since it may be called 134360894Smsmith * from both interrupt and non-interrupt context. 134460894Smsmith * 134560894Smsmith * If someone is waiting for the AEN we have, wake them up. 134660894Smsmith */ 134760894Smsmithstatic void 134860894Smsmithtwe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) 134960894Smsmith{ 135067555Smsmith char *msg; 135167555Smsmith int s, next, nextnext; 135260894Smsmith 135360894Smsmith debug_called(4); 135460894Smsmith 135567555Smsmith if ((msg = twe_format_aen(sc, aen)) != NULL) 135667555Smsmith twe_printf(sc, "AEN: <%s>\n", msg); 135760894Smsmith 135860894Smsmith s = splbio(); 135960894Smsmith /* enqueue the AEN */ 136060894Smsmith next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); 136167555Smsmith nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); 136267555Smsmith 136367555Smsmith /* check to see if this is the last free slot, and subvert the AEN if it is */ 136467555Smsmith if (nextnext == sc->twe_aen_tail) 136567555Smsmith aen = TWE_AEN_QUEUE_FULL; 136667555Smsmith 136767555Smsmith /* look to see if there's room for this AEN */ 136860894Smsmith if (next != sc->twe_aen_tail) { 136960894Smsmith sc->twe_aen_queue[sc->twe_aen_head] = aen; 137060894Smsmith sc->twe_aen_head = next; 137160894Smsmith } 137260894Smsmith 137367555Smsmith /* wake up anyone asleep on the queue */ 137467555Smsmith wakeup(&sc->twe_aen_queue); 137567555Smsmith 137660894Smsmith /* anyone looking for this AEN? */ 137760894Smsmith if (sc->twe_wait_aen == aen) { 137860894Smsmith sc->twe_wait_aen = -1; 137960894Smsmith wakeup(&sc->twe_wait_aen); 138060894Smsmith } 138160894Smsmith splx(s); 138260894Smsmith} 138360894Smsmith 138460894Smsmith/******************************************************************************** 138560894Smsmith * Pop an AEN off the queue, or return -1 if there are none left. 138660894Smsmith * 138760894Smsmith * We are more or less interrupt-safe, so don't block interrupts. 138860894Smsmith */ 138960894Smsmithstatic int 139060894Smsmithtwe_dequeue_aen(struct twe_softc *sc) 139160894Smsmith{ 139260894Smsmith int result; 139360894Smsmith 139460894Smsmith debug_called(4); 139560894Smsmith 139660894Smsmith if (sc->twe_aen_tail == sc->twe_aen_head) { 139760894Smsmith result = -1; 139860894Smsmith } else { 139960894Smsmith result = sc->twe_aen_queue[sc->twe_aen_tail]; 140060894Smsmith sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); 140160894Smsmith } 140260894Smsmith return(result); 140360894Smsmith} 140460894Smsmith 140560894Smsmith/******************************************************************************** 140660894Smsmith * Check to see if the requested AEN is in the queue. 140760894Smsmith * 140860894Smsmith * XXX we could probably avoid masking interrupts here 140960894Smsmith */ 141060894Smsmithstatic int 141160894Smsmithtwe_find_aen(struct twe_softc *sc, u_int16_t aen) 141260894Smsmith{ 141360894Smsmith int i, s, missing; 141460894Smsmith 141560894Smsmith missing = 1; 141660894Smsmith s = splbio(); 141760894Smsmith for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { 141860894Smsmith if (sc->twe_aen_queue[i] == aen) 141960894Smsmith missing = 0; 142060894Smsmith } 142167555Smsmith splx(s); 142260894Smsmith return(missing); 142360894Smsmith} 142460894Smsmith 142560894Smsmith 142660894Smsmith#if 0 /* currently unused */ 142760894Smsmith/******************************************************************************** 142860894Smsmith * Sleep waiting for at least (timeout) seconds until we see (aen) as 142960894Smsmith * requested. Returns nonzero on timeout or failure. 143060894Smsmith * 143160894Smsmith * XXX: this should not be used in cases where there may be more than one sleeper 143260894Smsmith * without a mechanism for registering multiple sleepers. 143360894Smsmith */ 143460894Smsmithstatic int 143560894Smsmithtwe_wait_aen(struct twe_softc *sc, int aen, int timeout) 143660894Smsmith{ 143760894Smsmith time_t expiry; 143860894Smsmith int found, s; 143960894Smsmith 144060894Smsmith debug_called(4); 144160894Smsmith 144260894Smsmith expiry = time_second + timeout; 144360894Smsmith found = 0; 144460894Smsmith 144560894Smsmith s = splbio(); 144660894Smsmith sc->twe_wait_aen = aen; 144760894Smsmith do { 144860894Smsmith twe_fetch_aen(sc); 144960894Smsmith tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); 145060894Smsmith if (sc->twe_wait_aen == -1) 145160894Smsmith found = 1; 145260894Smsmith } while ((time_second <= expiry) && !found); 145360894Smsmith splx(s); 145460894Smsmith return(!found); 145560894Smsmith} 145660894Smsmith#endif 145760894Smsmith 145860894Smsmith/******************************************************************************** 145960894Smsmith ******************************************************************************** 146060894Smsmith Command Buffer Management 146160894Smsmith ******************************************************************************** 146260894Smsmith ********************************************************************************/ 146360894Smsmith 146460894Smsmith/******************************************************************************** 146560894Smsmith * Get a new command buffer. 146660894Smsmith * 146767555Smsmith * This will return NULL if all command buffers are in use. 146860894Smsmith */ 146967555Smsmithstatic int 147067555Smsmithtwe_get_request(struct twe_softc *sc, struct twe_request **tr) 147160894Smsmith{ 147260894Smsmith debug_called(4); 147360894Smsmith 147460894Smsmith /* try to reuse an old buffer */ 147567555Smsmith *tr = twe_dequeue_free(sc); 147660894Smsmith 147767555Smsmith /* initialise some fields to their defaults */ 147867555Smsmith if (*tr != NULL) { 147967555Smsmith (*tr)->tr_data = NULL; 148069543Smsmith (*tr)->tr_private = NULL; 148167555Smsmith (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ 148267555Smsmith (*tr)->tr_flags = 0; 148367555Smsmith (*tr)->tr_complete = NULL; 148467555Smsmith (*tr)->tr_command.generic.status = 0; /* before submission to controller */ 148567555Smsmith (*tr)->tr_command.generic.flags = 0; /* not used */ 148660894Smsmith } 148767555Smsmith return(*tr == NULL); 148860894Smsmith} 148960894Smsmith 149060894Smsmith/******************************************************************************** 149167555Smsmith * Release a command buffer for reuse. 149260894Smsmith * 149360894Smsmith */ 149460894Smsmithstatic void 149560894Smsmithtwe_release_request(struct twe_request *tr) 149660894Smsmith{ 149760894Smsmith debug_called(4); 149860894Smsmith 149969543Smsmith if (tr->tr_private != NULL) 150069543Smsmith twe_panic(tr->tr_sc, "tr_private != NULL"); 150167555Smsmith twe_enqueue_free(tr); 150260894Smsmith} 150360894Smsmith 150460894Smsmith/******************************************************************************** 150567555Smsmith ******************************************************************************** 150667555Smsmith Debugging 150767555Smsmith ******************************************************************************** 150867555Smsmith ********************************************************************************/ 150960894Smsmith 151060894Smsmith/******************************************************************************** 151167555Smsmith * Print some information about the controller 151260894Smsmith */ 151367555Smsmithvoid 151467555Smsmithtwe_describe_controller(struct twe_softc *sc) 151560894Smsmith{ 151667555Smsmith TWE_Param *p[6]; 151767555Smsmith u_int8_t ports; 151867555Smsmith u_int32_t size; 151960894Smsmith int i; 152060894Smsmith 152167555Smsmith debug_called(2); 152260894Smsmith 152367555Smsmith /* get the port count */ 152467555Smsmith twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); 152560894Smsmith 152667555Smsmith /* get version strings */ 152767555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); 152867555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); 152967555Smsmith p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); 153067555Smsmith p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); 153167555Smsmith p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); 153267555Smsmith p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); 153360894Smsmith 153467555Smsmith twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[1]->data, p[2]->data); 153567555Smsmith if (bootverbose) 153667555Smsmith twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[0]->data, p[3]->data, 153767555Smsmith p[4]->data, p[5]->data); 153867555Smsmith free(p[0], M_DEVBUF); 153967555Smsmith free(p[1], M_DEVBUF); 154067555Smsmith free(p[2], M_DEVBUF); 154167555Smsmith free(p[3], M_DEVBUF); 154267555Smsmith free(p[4], M_DEVBUF); 154367555Smsmith free(p[5], M_DEVBUF); 154460894Smsmith 154567555Smsmith /* print attached drives */ 154667555Smsmith if (bootverbose) { 154767555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); 154867555Smsmith for (i = 0; i < ports; i++) { 154967555Smsmith if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) 155067555Smsmith continue; 155167555Smsmith twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); 155267555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); 155367555Smsmith if (p[1] != NULL) { 155467555Smsmith twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); 155567555Smsmith free(p[1], M_DEVBUF); 155667555Smsmith } else { 155767555Smsmith twe_printf(sc, "port %d, drive status unavailable\n", i); 155867555Smsmith } 155960894Smsmith } 156067555Smsmith free(p[0], M_DEVBUF); 156160894Smsmith } 156260894Smsmith} 156360894Smsmith 156460894Smsmith/******************************************************************************** 156560894Smsmith * Complain if the status bits aren't what we're expecting. 156667555Smsmith * 156767555Smsmith * Rate-limit the complaints to at most one of each every five seconds, but 156867555Smsmith * always return the correct status. 156960894Smsmith */ 157060894Smsmithstatic int 157160894Smsmithtwe_check_bits(struct twe_softc *sc, u_int32_t status_reg) 157260894Smsmith{ 157367555Smsmith int result; 157467555Smsmith static time_t lastwarn[2] = {0, 0}; 157560894Smsmith 157667555Smsmith /* 157767555Smsmith * This can be a little problematic, as twe_panic may call twe_reset if 157867555Smsmith * TWE_DEBUG is not set, which will call us again as part of the soft reset. 157967555Smsmith */ 158067555Smsmith if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { 158167555Smsmith twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, 158267555Smsmith TWE_STATUS_BITS_DESCRIPTION); 158367555Smsmith twe_panic(sc, "fatal status bits"); 158467555Smsmith } 158567555Smsmith 158660894Smsmith result = 0; 158760894Smsmith if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { 158867555Smsmith if (time_second > (lastwarn[0] + 5)) { 158967555Smsmith twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, 159067555Smsmith TWE_STATUS_BITS_DESCRIPTION); 159167555Smsmith lastwarn[0] = time_second; 159267555Smsmith } 159360894Smsmith result = 1; 159460894Smsmith } 159560894Smsmith 159660894Smsmith if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { 159767555Smsmith if (time_second > (lastwarn[1] + 5)) { 159867555Smsmith twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, 159967555Smsmith TWE_STATUS_BITS_DESCRIPTION); 160067555Smsmith lastwarn[1] = time_second; 160167555Smsmith } 160260894Smsmith result = 1; 160391790Smsmith if (status_reg & TWE_STATUS_PCI_PARITY_ERROR) { 160491790Smsmith twe_printf(sc, "PCI parity error: Reseat card, move card or buggy device present."); 160591790Smsmith twe_clear_pci_parity_error(sc); 160691790Smsmith } 160791790Smsmith if (status_reg & TWE_STATUS_PCI_ABORT) { 160891790Smsmith twe_printf(sc, "PCI abort, clearing."); 160991790Smsmith twe_clear_pci_abort(sc); 161091790Smsmith } 161160894Smsmith } 161267555Smsmith 161360894Smsmith return(result); 161460894Smsmith} 161560894Smsmith 161660894Smsmith/******************************************************************************** 161767555Smsmith * Return a string describing (aen). 161867555Smsmith * 161967555Smsmith * The low 8 bits of the aen are the code, the high 8 bits give the unit number 162067555Smsmith * where an AEN is specific to a unit. 162167555Smsmith * 162267555Smsmith * Note that we could expand this routine to handle eg. up/downgrading the status 162367555Smsmith * of a drive if we had some idea of what the drive's initial status was. 162460894Smsmith */ 162560894Smsmith 162660894Smsmithstatic char * 162767555Smsmithtwe_format_aen(struct twe_softc *sc, u_int16_t aen) 162860894Smsmith{ 162967555Smsmith static char buf[80]; 163067555Smsmith device_t child; 163167555Smsmith char *code, *msg; 163260894Smsmith 163367555Smsmith code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); 163467555Smsmith msg = code + 2; 163560894Smsmith 163667555Smsmith switch (*code) { 163767555Smsmith case 'q': 163867555Smsmith if (!bootverbose) 163967555Smsmith return(NULL); 164067555Smsmith /* FALLTHROUGH */ 164191790Smsmith case 'a': 164267555Smsmith return(msg); 164367555Smsmith 164467555Smsmith case 'c': 164567555Smsmith if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { 164667555Smsmith sprintf(buf, "twed%d: %s", device_get_unit(child), msg); 164767555Smsmith } else { 164867555Smsmith sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), 164967555Smsmith msg, TWE_AEN_UNIT(aen)); 165067555Smsmith } 165167555Smsmith return(buf); 165291790Smsmith 165391790Smsmith case 'p': 165491790Smsmith sprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen), 165591790Smsmith msg); 165691790Smsmith return(buf); 165791790Smsmith 165867555Smsmith 165967555Smsmith case 'x': 166067555Smsmith default: 166167555Smsmith break; 166267555Smsmith } 166367555Smsmith sprintf(buf, "unknown AEN 0x%x", aen); 166460894Smsmith return(buf); 166560894Smsmith} 166660894Smsmith 166769543Smsmith/******************************************************************************** 166869543Smsmith * Print a diagnostic if the status of the command warrants it, and return 166969543Smsmith * either zero (command was ok) or nonzero (command failed). 167069543Smsmith */ 167167555Smsmithstatic int 167269543Smsmithtwe_report_request(struct twe_request *tr) 167367555Smsmith{ 167469543Smsmith struct twe_softc *sc = tr->tr_sc; 167569543Smsmith TWE_Command *cmd = &tr->tr_command; 167676340Smsmith int result = 0; 167760894Smsmith 167876340Smsmith /* 167976340Smsmith * Check the command status value and handle accordingly. 168076340Smsmith */ 168176340Smsmith if (cmd->generic.status == TWE_STATUS_RESET) { 168276340Smsmith /* 168376340Smsmith * The status code 0xff requests a controller reset. 168476340Smsmith */ 168576340Smsmith twe_printf(sc, "command returned with controller rest request\n"); 168676340Smsmith twe_reset(sc); 168769543Smsmith result = 1; 168876340Smsmith } else if (cmd->generic.status > TWE_STATUS_FATAL) { 168969543Smsmith /* 169076340Smsmith * Fatal errors that don't require controller reset. 169191790Smsmith * 169291790Smsmith * We know a few special flags values. 169369543Smsmith */ 169491790Smsmith switch (cmd->generic.flags) { 169591790Smsmith case 0x1b: 169691790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 169791790Smsmith "drive timeout"); 169891790Smsmith break; 169991790Smsmith case 0x51: 170091790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 170191790Smsmith "unrecoverable drive error"); 170291790Smsmith break; 170391790Smsmith default: 170491790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 170591790Smsmith "controller error - %s (flags = 0x%x)\n", 170691790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 170791790Smsmith cmd->generic.flags); 170891790Smsmith result = 1; 170991790Smsmith } 171076340Smsmith } else if (cmd->generic.status > TWE_STATUS_WARNING) { 171176340Smsmith /* 171276340Smsmith * Warning level status. 171376340Smsmith */ 171491790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 171591790Smsmith "warning - %s (flags = 0x%x)\n", 171691790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 171791790Smsmith cmd->generic.flags); 171876340Smsmith } else if (cmd->generic.status > 0x40) { 171976340Smsmith /* 172076340Smsmith * Info level status. 172176340Smsmith */ 172291790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 172391790Smsmith "attention - %s (flags = 0x%x)\n", 172491790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 172591790Smsmith cmd->generic.flags); 172667555Smsmith } 172776340Smsmith 172869543Smsmith return(result); 172967555Smsmith} 173067555Smsmith 173169543Smsmith/******************************************************************************** 173269543Smsmith * Print some controller state to aid in debugging error/panic conditions 173369543Smsmith */ 173467555Smsmithvoid 173567555Smsmithtwe_print_controller(struct twe_softc *sc) 173660894Smsmith{ 173767555Smsmith u_int32_t status_reg; 173860894Smsmith 173967555Smsmith status_reg = TWE_STATUS(sc); 174067555Smsmith twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); 174169543Smsmith twe_printf(sc, " current max\n"); 174269543Smsmith twe_printf(sc, "free %04d %04d\n", sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max); 174369543Smsmith twe_printf(sc, "ready %04d %04d\n", sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max); 174469543Smsmith twe_printf(sc, "busy %04d %04d\n", sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max); 174569543Smsmith twe_printf(sc, "complete %04d %04d\n", sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max); 174669543Smsmith twe_printf(sc, "bioq %04d %04d\n", sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max); 174767555Smsmith twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); 174867555Smsmith} 174960894Smsmith 175067555Smsmithstatic void 175167555Smsmithtwe_panic(struct twe_softc *sc, char *reason) 175267555Smsmith{ 175367555Smsmith twe_print_controller(sc); 175467555Smsmith#ifdef TWE_DEBUG 175567555Smsmith panic(reason); 175667555Smsmith#else 175767555Smsmith twe_reset(sc); 175867555Smsmith#endif 175960894Smsmith} 176060894Smsmith 176167555Smsmith#if 0 176260894Smsmith/******************************************************************************** 176360894Smsmith * Print a request/command in human-readable format. 176460894Smsmith */ 176560894Smsmithstatic void 176660894Smsmithtwe_print_request(struct twe_request *tr) 176760894Smsmith{ 176867555Smsmith struct twe_softc *sc = tr->tr_sc; 176960894Smsmith TWE_Command *cmd = &tr->tr_command; 177060894Smsmith int i; 177160894Smsmith 177267555Smsmith twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", 177367555Smsmith cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, 177467555Smsmith cmd->generic.unit, cmd->generic.host_id); 177567555Smsmith twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", 177667555Smsmith cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); 177767555Smsmith 177867555Smsmith switch(cmd->generic.opcode) { /* XXX add more opcodes? */ 177967555Smsmith case TWE_OP_READ: 178067555Smsmith case TWE_OP_WRITE: 178167555Smsmith twe_printf(sc, " lba %d\n", cmd->io.lba); 178267555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) 178367555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 178467555Smsmith i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); 178560894Smsmith break; 178660894Smsmith 178767555Smsmith case TWE_OP_GET_PARAM: 178867555Smsmith case TWE_OP_SET_PARAM: 178967555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) 179067555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 179167555Smsmith i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); 179260894Smsmith break; 179360894Smsmith 179467555Smsmith case TWE_OP_INIT_CONNECTION: 179567555Smsmith twe_printf(sc, " response queue pointer 0x%x\n", 179667555Smsmith cmd->initconnection.response_queue_pointer); 179767555Smsmith break; 179867555Smsmith 179960894Smsmith default: 180067555Smsmith break; 180160894Smsmith } 180267555Smsmith twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", 180367555Smsmith tr, tr->tr_cmdphys, tr->tr_data, tr->tr_dataphys, tr->tr_length); 180467555Smsmith twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", 180567555Smsmith tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); 180660894Smsmith} 180760894Smsmith 180860894Smsmith#endif 1809