twe.c revision 119124
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 119124 2003-08-19 12:22:17Z ps $ 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); 63118508Spsstatic void twe_add_unit(struct twe_softc *sc, int unit); 64118508Spsstatic void twe_del_unit(struct twe_softc *sc, int unit); 6560894Smsmith 6660894Smsmith/* 6760894Smsmith * Command I/O to controller. 6860894Smsmith */ 6967555Smsmithstatic void twe_done(struct twe_softc *sc); 7067555Smsmithstatic void twe_complete(struct twe_softc *sc); 7167555Smsmithstatic int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout); 7267555Smsmithstatic int twe_drain_response_queue(struct twe_softc *sc); 7367555Smsmithstatic int twe_check_bits(struct twe_softc *sc, u_int32_t status_reg); 7467555Smsmithstatic int twe_soft_reset(struct twe_softc *sc); 7560894Smsmith 7660894Smsmith/* 7760894Smsmith * Interrupt handling. 7860894Smsmith */ 7967555Smsmithstatic void twe_host_intr(struct twe_softc *sc); 8067555Smsmithstatic void twe_attention_intr(struct twe_softc *sc); 8167555Smsmithstatic void twe_command_intr(struct twe_softc *sc); 8260894Smsmith 8360894Smsmith/* 8460894Smsmith * Asynchronous event handling. 8560894Smsmith */ 8667555Smsmithstatic int twe_fetch_aen(struct twe_softc *sc); 8767555Smsmithstatic void twe_handle_aen(struct twe_request *tr); 8867555Smsmithstatic void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen); 8967555Smsmithstatic int twe_dequeue_aen(struct twe_softc *sc); 9067555Smsmithstatic int twe_drain_aen_queue(struct twe_softc *sc); 9167555Smsmithstatic int twe_find_aen(struct twe_softc *sc, u_int16_t aen); 9260894Smsmith 9360894Smsmith/* 9460894Smsmith * Command buffer management. 9560894Smsmith */ 9667555Smsmithstatic int twe_get_request(struct twe_softc *sc, struct twe_request **tr); 9767555Smsmithstatic void twe_release_request(struct twe_request *tr); 9860894Smsmith 9960894Smsmith/* 10060894Smsmith * Debugging. 10160894Smsmith */ 10267555Smsmithstatic char *twe_format_aen(struct twe_softc *sc, u_int16_t aen); 10369543Smsmithstatic int twe_report_request(struct twe_request *tr); 10467555Smsmithstatic void twe_panic(struct twe_softc *sc, char *reason); 10560894Smsmith 10660894Smsmith/******************************************************************************** 10760894Smsmith ******************************************************************************** 10860894Smsmith Public Interfaces 10960894Smsmith ******************************************************************************** 11060894Smsmith ********************************************************************************/ 11160894Smsmith 11260894Smsmith/******************************************************************************** 11367555Smsmith * Initialise the controller, set up driver data structures. 11460894Smsmith */ 11567555Smsmithint 11667555Smsmithtwe_setup(struct twe_softc *sc) 11760894Smsmith{ 11860894Smsmith struct twe_request *tr; 119118816Sps TWE_Command *cmd; 12091790Smsmith u_int32_t status_reg; 12167555Smsmith int i; 12260894Smsmith 12360894Smsmith debug_called(4); 12460894Smsmith 12560894Smsmith /* 12667555Smsmith * Initialise request queues. 12760894Smsmith */ 12867555Smsmith twe_initq_free(sc); 12967555Smsmith twe_initq_bio(sc); 13067555Smsmith twe_initq_ready(sc); 13167555Smsmith twe_initq_busy(sc); 13267555Smsmith twe_initq_complete(sc); 13367555Smsmith sc->twe_wait_aen = -1; 13460894Smsmith 13560894Smsmith /* 13667555Smsmith * Allocate request structures up front. 13760894Smsmith */ 13867555Smsmith for (i = 0; i < TWE_Q_LENGTH; i++) { 139118816Sps if ((tr = twe_allocate_request(sc, i)) == NULL) 14067555Smsmith return(ENOMEM); 14167555Smsmith /* 14267555Smsmith * Set global defaults that won't change. 14367555Smsmith */ 144118816Sps cmd = TWE_FIND_COMMAND(tr); 145118816Sps cmd->generic.host_id = sc->twe_host_id; /* controller-assigned host ID */ 146118816Sps cmd->generic.request_id = i; /* our index number */ 14767555Smsmith sc->twe_lookup[i] = tr; 14860894Smsmith 14967555Smsmith /* 15067555Smsmith * Put command onto the freelist. 15167555Smsmith */ 15267555Smsmith twe_release_request(tr); 15360894Smsmith } 15460894Smsmith 15560894Smsmith /* 15691790Smsmith * Check status register for errors, clear them. 15791790Smsmith */ 15891790Smsmith status_reg = TWE_STATUS(sc); 15991790Smsmith twe_check_bits(sc, status_reg); 16091790Smsmith 16191790Smsmith /* 16267555Smsmith * Wait for the controller to come ready. 16360894Smsmith */ 16467555Smsmith if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { 16567555Smsmith twe_printf(sc, "microcontroller not ready\n"); 16660894Smsmith return(ENXIO); 16760894Smsmith } 16860894Smsmith 16960894Smsmith /* 17067555Smsmith * Disable interrupts from the card. 17160894Smsmith */ 17267555Smsmith twe_disable_interrupts(sc); 17360894Smsmith 17460894Smsmith /* 17567555Smsmith * Soft reset the controller, look for the AEN acknowledging the reset, 17667555Smsmith * check for errors, drain the response queue. 17760894Smsmith */ 17867555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 17960894Smsmith 18067555Smsmith if (i > 0) 18167555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 18267555Smsmith 18367555Smsmith if (!twe_soft_reset(sc)) 18467555Smsmith break; /* reset process complete */ 18567555Smsmith } 18667555Smsmith /* did we give up? */ 18767555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 18867555Smsmith twe_printf(sc, "can't initialise controller, giving up\n"); 18960894Smsmith return(ENXIO); 19060894Smsmith } 19160894Smsmith 19260894Smsmith return(0); 19360894Smsmith} 19460894Smsmith 195118508Spsstatic void 196118508Spstwe_add_unit(struct twe_softc *sc, int unit) 19760894Smsmith{ 19860894Smsmith struct twe_drive *dr; 199118508Sps int table; 20067555Smsmith u_int16_t dsize; 201118508Sps TWE_Param *drives = NULL, *param = NULL; 20267555Smsmith TWE_Unit_Descriptor *ud; 20360894Smsmith 204118508Sps if (unit < 0 || unit > TWE_MAX_UNITS) 205118508Sps return; 20660894Smsmith 20760894Smsmith /* 20867555Smsmith * The controller is in a safe state, so try to find drives attached to it. 20960894Smsmith */ 21067555Smsmith if ((drives = twe_get_param(sc, TWE_PARAM_UNITSUMMARY, TWE_PARAM_UNITSUMMARY_Status, 21167555Smsmith TWE_MAX_UNITS, NULL)) == NULL) { 21267555Smsmith twe_printf(sc, "can't detect attached units\n"); 21360894Smsmith return; 21460894Smsmith } 21560894Smsmith 216118508Sps dr = &sc->twe_drive[unit]; 217118508Sps /* check that the drive is online */ 218118508Sps if (!(drives->data[unit] & TWE_PARAM_UNITSTATUS_Online)) 219118508Sps goto out; 22060894Smsmith 221118508Sps table = TWE_PARAM_UNITINFO + unit; 22260894Smsmith 223118508Sps if (twe_get_param_4(sc, table, TWE_PARAM_UNITINFO_Capacity, &dr->td_size)) { 224118508Sps twe_printf(sc, "error fetching capacity for unit %d\n", unit); 225118508Sps goto out; 226118508Sps } 227118508Sps if (twe_get_param_1(sc, table, TWE_PARAM_UNITINFO_Status, &dr->td_state)) { 228118508Sps twe_printf(sc, "error fetching state for unit %d\n", unit); 229118508Sps goto out; 230118508Sps } 231118508Sps if (twe_get_param_2(sc, table, TWE_PARAM_UNITINFO_DescriptorSize, &dsize)) { 232118508Sps twe_printf(sc, "error fetching descriptor size for unit %d\n", unit); 233118508Sps goto out; 234118508Sps } 235118508Sps if ((param = twe_get_param(sc, table, TWE_PARAM_UNITINFO_Descriptor, dsize - 3, NULL)) == NULL) { 236118508Sps twe_printf(sc, "error fetching descriptor for unit %d\n", unit); 237118508Sps goto out; 238118508Sps } 239118508Sps ud = (TWE_Unit_Descriptor *)param->data; 240118508Sps dr->td_type = ud->configuration; 241118508Sps 242118508Sps /* build synthetic geometry as per controller internal rules */ 243118508Sps if (dr->td_size > 0x200000) { 244118508Sps dr->td_heads = 255; 245118508Sps dr->td_sectors = 63; 246118508Sps } else { 247118508Sps dr->td_heads = 64; 248118508Sps dr->td_sectors = 32; 249118508Sps } 250118508Sps dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors); 251118508Sps dr->td_unit = unit; 252118508Sps 253118508Sps twe_attach_drive(sc, dr); 254118508Sps 255118508Spsout: 256118508Sps if (param != NULL) 25767555Smsmith free(param, M_DEVBUF); 258118508Sps if (drives != NULL) 259118508Sps free(drives, M_DEVBUF); 260118508Sps} 26160894Smsmith 262118508Spsstatic void 263118508Spstwe_del_unit(struct twe_softc *sc, int unit) 264118508Sps{ 26560894Smsmith 266118508Sps if (unit < 0 || unit > TWE_MAX_UNITS) 267118508Sps return; 26860894Smsmith 269118508Sps twe_detach_drive(sc, unit); 270118508Sps} 271118508Sps 272118508Sps/******************************************************************************** 273118508Sps * Locate disk devices and attach children to them. 274118508Sps */ 275118508Spsvoid 276118508Spstwe_init(struct twe_softc *sc) 277118508Sps{ 278118508Sps int i; 279118508Sps 28060894Smsmith /* 281118508Sps * Scan for drives 282118508Sps */ 283118508Sps for (i = 0; i < TWE_MAX_UNITS; i++) 284118508Sps twe_add_unit(sc, i); 285118508Sps 286118508Sps /* 28760894Smsmith * Initialise connection with controller. 28860894Smsmith */ 28967555Smsmith twe_init_connection(sc, TWE_INIT_MESSAGE_CREDITS); 29060894Smsmith 29167555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 29267555Smsmith /* 29367555Smsmith * Tell the controller we support shutdown notification. 29467555Smsmith */ 29567555Smsmith twe_set_param_1(sc, TWE_PARAM_FEATURES, TWE_PARAM_FEATURES_DriverShutdown, 1); 29667555Smsmith#endif 29767555Smsmith 29860894Smsmith /* 29960894Smsmith * Mark controller up and ready to run. 30060894Smsmith */ 30160894Smsmith sc->twe_state &= ~TWE_STATE_SHUTDOWN; 30260894Smsmith 30360894Smsmith /* 30467555Smsmith * Finally enable interrupts. 30560894Smsmith */ 30660894Smsmith twe_enable_interrupts(sc); 30760894Smsmith} 30860894Smsmith 30960894Smsmith/******************************************************************************** 31067555Smsmith * Stop the controller 31160894Smsmith */ 31267555Smsmithvoid 31367555Smsmithtwe_deinit(struct twe_softc *sc) 31460894Smsmith{ 31560894Smsmith /* 31660894Smsmith * Mark the controller as shutting down, and disable any further interrupts. 31760894Smsmith */ 31860894Smsmith sc->twe_state |= TWE_STATE_SHUTDOWN; 31960894Smsmith twe_disable_interrupts(sc); 32060894Smsmith 32167555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 32267555Smsmith /* 32367555Smsmith * Disconnect from the controller 32460894Smsmith */ 32567555Smsmith twe_init_connection(sc, TWE_SHUTDOWN_MESSAGE_CREDITS); 32667555Smsmith#endif 32760894Smsmith} 32860894Smsmith 32960894Smsmith/******************************************************************************* 33060894Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy 33160894Smsmith * status. 33260894Smsmith */ 33367555Smsmithvoid 33467555Smsmithtwe_intr(struct twe_softc *sc) 33560894Smsmith{ 33660894Smsmith u_int32_t status_reg; 33760894Smsmith 33860894Smsmith debug_called(4); 33960894Smsmith 34060894Smsmith /* 34160894Smsmith * Collect current interrupt status. 34260894Smsmith */ 34360894Smsmith status_reg = TWE_STATUS(sc); 34460894Smsmith twe_check_bits(sc, status_reg); 34560894Smsmith 34660894Smsmith /* 34760894Smsmith * Dispatch based on interrupt status 34860894Smsmith */ 34960894Smsmith if (status_reg & TWE_STATUS_HOST_INTERRUPT) 35060894Smsmith twe_host_intr(sc); 35160894Smsmith if (status_reg & TWE_STATUS_ATTENTION_INTERRUPT) 35260894Smsmith twe_attention_intr(sc); 35360894Smsmith if (status_reg & TWE_STATUS_COMMAND_INTERRUPT) 35460894Smsmith twe_command_intr(sc); 35573104Smsmith if (status_reg & TWE_STATUS_RESPONSE_INTERRUPT) 35660894Smsmith twe_done(sc); 35760894Smsmith}; 35860894Smsmith 35969543Smsmith/******************************************************************************** 36069543Smsmith * Pull as much work off the softc's work queue as possible and give it to the 36169543Smsmith * controller. 36260894Smsmith */ 36369543Smsmithvoid 36469543Smsmithtwe_startio(struct twe_softc *sc) 36560894Smsmith{ 36669543Smsmith struct twe_request *tr; 36769543Smsmith TWE_Command *cmd; 36869543Smsmith twe_bio *bp; 36969543Smsmith int error; 37069543Smsmith 37167555Smsmith debug_called(4); 37260894Smsmith 373118816Sps if (sc->twe_state & TWE_STATE_FRZN) 374118816Sps return; 375118816Sps 37669543Smsmith /* spin until something prevents us from doing any work */ 37769543Smsmith for (;;) { 37867555Smsmith 37969543Smsmith /* try to get a command that's already ready to go */ 38069543Smsmith tr = twe_dequeue_ready(sc); 38169543Smsmith 38269543Smsmith /* build a command from an outstanding bio */ 38369543Smsmith if (tr == NULL) { 38469543Smsmith 38569543Smsmith /* see if there's work to be done */ 38669543Smsmith if ((bp = twe_dequeue_bio(sc)) == NULL) 38769543Smsmith break; 38869543Smsmith 38969543Smsmith /* get a command to handle the bio with */ 39069543Smsmith if (twe_get_request(sc, &tr)) { 39169543Smsmith twe_enqueue_bio(sc, bp); /* failed, put the bio back */ 39269543Smsmith break; 39369543Smsmith } 39469543Smsmith 39569543Smsmith /* connect the bio to the command */ 39669543Smsmith tr->tr_complete = twe_completeio; 39769543Smsmith tr->tr_private = bp; 39869543Smsmith tr->tr_data = TWE_BIO_DATA(bp); 39969543Smsmith tr->tr_length = TWE_BIO_LENGTH(bp); 400118816Sps cmd = TWE_FIND_COMMAND(tr); 40169543Smsmith if (TWE_BIO_IS_READ(bp)) { 40269543Smsmith tr->tr_flags |= TWE_CMD_DATAIN; 40369543Smsmith cmd->io.opcode = TWE_OP_READ; 40469543Smsmith } else { 40569543Smsmith tr->tr_flags |= TWE_CMD_DATAOUT; 40669543Smsmith cmd->io.opcode = TWE_OP_WRITE; 40769543Smsmith } 40869543Smsmith 40969543Smsmith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 41069543Smsmith cmd->io.size = 3; 41169543Smsmith cmd->io.unit = TWE_BIO_UNIT(bp); 41269543Smsmith cmd->io.block_count = (tr->tr_length + TWE_BLOCK_SIZE - 1) / TWE_BLOCK_SIZE; 41369543Smsmith cmd->io.lba = TWE_BIO_LBA(bp); 41469543Smsmith } 41569543Smsmith 41669543Smsmith /* did we find something to do? */ 41769543Smsmith if (tr == NULL) 41869543Smsmith break; 41969543Smsmith 420118816Sps /* map the command so the controller can work with it */ 421118816Sps error = twe_map_request(tr); 42269543Smsmith if (error != 0) { 42369543Smsmith if (error == EBUSY) { 42469543Smsmith twe_requeue_ready(tr); /* try it again later */ 42569543Smsmith break; /* don't try anything more for now */ 42669543Smsmith } 427118816Sps 42869543Smsmith /* we don't support any other return from twe_start */ 429118816Sps twe_panic(sc, "twe_map_request returned nonsense"); 43069543Smsmith } 43169543Smsmith } 43260894Smsmith} 43360894Smsmith 43460894Smsmith/******************************************************************************** 43569543Smsmith * Write blocks from memory to disk, for system crash dumps. 43669543Smsmith */ 43769543Smsmithint 43869543Smsmithtwe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) 43969543Smsmith{ 44069543Smsmith struct twe_request *tr; 44169543Smsmith TWE_Command *cmd; 44269543Smsmith int error; 44369543Smsmith 44469543Smsmith if (twe_get_request(sc, &tr)) 44569543Smsmith return(ENOMEM); 44669543Smsmith 44769543Smsmith tr->tr_data = data; 44869543Smsmith tr->tr_status = TWE_CMD_SETUP; 44969543Smsmith tr->tr_length = nblks * TWE_BLOCK_SIZE; 45069543Smsmith tr->tr_flags = TWE_CMD_DATAOUT; 45169543Smsmith 452118816Sps cmd = TWE_FIND_COMMAND(tr); 45369543Smsmith cmd->io.opcode = TWE_OP_WRITE; 45469543Smsmith cmd->io.size = 3; 45569543Smsmith cmd->io.unit = unit; 45669543Smsmith cmd->io.block_count = nblks; 45769543Smsmith cmd->io.lba = lba; 45869543Smsmith 45969543Smsmith error = twe_immediate_request(tr); 46069543Smsmith if (error == 0) 46169543Smsmith if (twe_report_request(tr)) 46269543Smsmith error = EIO; 46369543Smsmith twe_release_request(tr); 46469543Smsmith return(error); 46569543Smsmith} 46669543Smsmith 46769543Smsmith/******************************************************************************** 46867555Smsmith * Handle controller-specific control operations. 46960894Smsmith */ 47067555Smsmithint 471118816Spstwe_ioctl(struct twe_softc *sc, int ioctlcmd, void *addr) 47260894Smsmith{ 47367555Smsmith struct twe_usercommand *tu = (struct twe_usercommand *)addr; 47467555Smsmith struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; 475118508Sps struct twe_drivecommand *td = (struct twe_drivecommand *)addr; 47667555Smsmith union twe_statrequest *ts = (union twe_statrequest *)addr; 47767555Smsmith TWE_Param *param; 478118816Sps TWE_Command *cmd; 47967555Smsmith void *data; 48067555Smsmith int *arg = (int *)addr; 48167555Smsmith struct twe_request *tr; 48269543Smsmith u_int8_t srid; 48367555Smsmith int s, error; 48460894Smsmith 48567555Smsmith error = 0; 486118816Sps switch(ioctlcmd) { 48767555Smsmith /* handle a command from userspace */ 48867555Smsmith case TWEIO_COMMAND: 48967555Smsmith /* get a request */ 490118508Sps while (twe_get_request(sc, &tr)) 491119124Sps tsleep(sc, PPAUSE, "twioctl", hz); 49267555Smsmith 49369543Smsmith /* 49469543Smsmith * Save the command's request ID, copy the user-supplied command in, 49569543Smsmith * restore the request ID. 49669543Smsmith */ 497118816Sps cmd = TWE_FIND_COMMAND(tr); 498118816Sps srid = cmd->generic.request_id; 499118816Sps bcopy(&tu->tu_command, cmd, sizeof(TWE_Command)); 500118816Sps cmd->generic.request_id = srid; 50167555Smsmith 502118508Sps /* 503118508Sps * if there's a data buffer, allocate and copy it in. 504118508Sps * Must be in multipled of 512 bytes. 505118508Sps */ 506118508Sps tr->tr_length = (tu->tu_size + 511) & ~511; 50767555Smsmith if (tr->tr_length > 0) { 508111119Simp if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { 50967555Smsmith error = ENOMEM; 51067555Smsmith goto cmd_done; 51167555Smsmith } 512118508Sps if ((error = copyin(tu->tu_data, tr->tr_data, tu->tu_size)) != 0) 51367555Smsmith goto cmd_done; 51467555Smsmith tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 51567555Smsmith } 51667555Smsmith 51767555Smsmith /* run the command */ 51867555Smsmith twe_wait_request(tr); 51967555Smsmith 52067555Smsmith /* copy the command out again */ 521118816Sps bcopy(cmd, &tu->tu_command, sizeof(TWE_Command)); 52267555Smsmith 52367555Smsmith /* if there was a data buffer, copy it out */ 52467555Smsmith if (tr->tr_length > 0) 525118508Sps error = copyout(tr->tr_data, tu->tu_data, tu->tu_size); 52667555Smsmith 52767555Smsmith cmd_done: 52867555Smsmith /* free resources */ 52967555Smsmith if (tr->tr_data != NULL) 53067555Smsmith free(tr->tr_data, M_DEVBUF); 53167555Smsmith if (tr != NULL) 53267555Smsmith twe_release_request(tr); 53367555Smsmith 53467555Smsmith break; 53567555Smsmith 53667555Smsmith /* fetch statistics counter */ 53767555Smsmith case TWEIO_STATS: 53867555Smsmith switch (ts->ts_item) { 53967555Smsmith#ifdef TWE_PERFORMANCE_MONITOR 54067555Smsmith case TWEQ_FREE: 54167555Smsmith case TWEQ_BIO: 54267555Smsmith case TWEQ_READY: 54367555Smsmith case TWEQ_BUSY: 54467555Smsmith case TWEQ_COMPLETE: 54567555Smsmith bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); 54667555Smsmith break; 54767555Smsmith#endif 54867555Smsmith default: 54967555Smsmith error = ENOENT; 55067555Smsmith break; 55167555Smsmith } 55267555Smsmith break; 55367555Smsmith 55467555Smsmith /* poll for an AEN */ 55567555Smsmith case TWEIO_AEN_POLL: 55667555Smsmith *arg = twe_dequeue_aen(sc); 55767555Smsmith break; 55867555Smsmith 55967555Smsmith /* wait for another AEN to show up */ 56067555Smsmith case TWEIO_AEN_WAIT: 56167555Smsmith s = splbio(); 562118508Sps while ((*arg = twe_dequeue_aen(sc)) == TWE_AEN_QUEUE_EMPTY) { 56367555Smsmith error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); 56467555Smsmith if (error == EINTR) 56567555Smsmith break; 56667555Smsmith } 56767555Smsmith splx(s); 56867555Smsmith break; 56967555Smsmith 57067555Smsmith case TWEIO_GET_PARAM: 57167555Smsmith if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { 57267555Smsmith twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", 57367555Smsmith tp->tp_table_id, tp->tp_param_id, tp->tp_size); 57467555Smsmith error = EINVAL; 57567555Smsmith } else { 57667555Smsmith if (param->parameter_size_bytes > tp->tp_size) { 57767555Smsmith twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", 57867555Smsmith param->parameter_size_bytes, tp->tp_size); 57967555Smsmith error = EFAULT; 58067555Smsmith } else { 58167555Smsmith error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); 58267555Smsmith } 58367555Smsmith free(param, M_DEVBUF); 58467555Smsmith } 58567555Smsmith break; 58667555Smsmith 58767555Smsmith case TWEIO_SET_PARAM: 588111119Simp if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { 58967555Smsmith error = ENOMEM; 59067555Smsmith } else { 59167555Smsmith error = copyin(tp->tp_data, data, tp->tp_size); 59267555Smsmith if (error == 0) 59367555Smsmith error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); 59467555Smsmith free(data, M_DEVBUF); 59567555Smsmith } 59667555Smsmith break; 59767555Smsmith 59867555Smsmith case TWEIO_RESET: 59967555Smsmith twe_reset(sc); 60067555Smsmith break; 60167555Smsmith 602118508Sps case TWEIO_ADD_UNIT: 603118508Sps twe_add_unit(sc, td->td_unit); 604118508Sps break; 605118508Sps 606118508Sps case TWEIO_DEL_UNIT: 607118508Sps twe_del_unit(sc, td->td_unit); 608118508Sps break; 609118508Sps 61091790Smsmith /* XXX implement ATA PASSTHROUGH */ 61191790Smsmith 61267555Smsmith /* nothing we understand */ 61367555Smsmith default: 61467555Smsmith error = ENOTTY; 61567555Smsmith } 61667555Smsmith 61767555Smsmith return(error); 61860894Smsmith} 61960894Smsmith 62060894Smsmith/******************************************************************************** 62167555Smsmith * Enable the useful interrupts from the controller. 62260894Smsmith */ 62367555Smsmithvoid 62467555Smsmithtwe_enable_interrupts(struct twe_softc *sc) 62560894Smsmith{ 62667555Smsmith sc->twe_state |= TWE_STATE_INTEN; 62767555Smsmith TWE_CONTROL(sc, 62867555Smsmith TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | 62967555Smsmith TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | 63067555Smsmith TWE_CONTROL_ENABLE_INTERRUPTS); 63160894Smsmith} 63260894Smsmith 63360894Smsmith/******************************************************************************** 63467555Smsmith * Disable interrupts from the controller. 63567555Smsmith */ 63667555Smsmithvoid 63767555Smsmithtwe_disable_interrupts(struct twe_softc *sc) 63867555Smsmith{ 63967555Smsmith TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); 64067555Smsmith sc->twe_state &= ~TWE_STATE_INTEN; 64167555Smsmith} 64267555Smsmith 64367555Smsmith/******************************************************************************** 64460894Smsmith ******************************************************************************** 64560894Smsmith Command Submission 64660894Smsmith ******************************************************************************** 64760894Smsmith ********************************************************************************/ 64860894Smsmith 64967555Smsmith/******************************************************************************** 65067555Smsmith * Read integer parameter table entries. 65160894Smsmith */ 65267555Smsmithstatic int 65367555Smsmithtwe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) 65460894Smsmith{ 65567555Smsmith TWE_Param *param; 65660894Smsmith 65767555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) 65867555Smsmith return(ENOENT); 65967555Smsmith *result = *(u_int8_t *)param->data; 66067555Smsmith free(param, M_DEVBUF); 66167555Smsmith return(0); 66267555Smsmith} 66360894Smsmith 66467555Smsmithstatic int 66567555Smsmithtwe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) 66667555Smsmith{ 66767555Smsmith TWE_Param *param; 66867555Smsmith 66967555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) 67067555Smsmith return(ENOENT); 67167555Smsmith *result = *(u_int16_t *)param->data; 67267555Smsmith free(param, M_DEVBUF); 67360894Smsmith return(0); 67460894Smsmith} 67560894Smsmith 67667555Smsmithstatic int 67767555Smsmithtwe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) 67867555Smsmith{ 67967555Smsmith TWE_Param *param; 68067555Smsmith 68167555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) 68267555Smsmith return(ENOENT); 68367555Smsmith *result = *(u_int32_t *)param->data; 68467555Smsmith free(param, M_DEVBUF); 68567555Smsmith return(0); 68667555Smsmith} 68767555Smsmith 68860894Smsmith/******************************************************************************** 68960894Smsmith * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it 69060894Smsmith * will be called with the command when it's completed. If no callback is 69160894Smsmith * provided, we will wait for the command to complete and then return just the data. 69260894Smsmith * The caller is responsible for freeing the data when done with it. 69360894Smsmith */ 69460894Smsmithstatic void * 69567555Smsmithtwe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, 69667555Smsmith void (* func)(struct twe_request *tr)) 69760894Smsmith{ 69860894Smsmith struct twe_request *tr; 69960894Smsmith TWE_Command *cmd; 70060894Smsmith TWE_Param *param; 70160894Smsmith int error; 70260894Smsmith 70360894Smsmith debug_called(4); 70460894Smsmith 70560894Smsmith tr = NULL; 70660894Smsmith param = NULL; 70760894Smsmith 70860894Smsmith /* get a command */ 70967555Smsmith if (twe_get_request(sc, &tr)) 71060894Smsmith goto err; 71160894Smsmith 71260894Smsmith /* get a buffer */ 71360894Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 71460894Smsmith goto err; 71560894Smsmith tr->tr_data = param; 71660894Smsmith tr->tr_length = TWE_SECTOR_SIZE; 71760894Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 71860894Smsmith 71960894Smsmith /* build the command for the controller */ 720118816Sps cmd = TWE_FIND_COMMAND(tr); 72167555Smsmith cmd->param.opcode = TWE_OP_GET_PARAM; 72267555Smsmith cmd->param.size = 2; 72367555Smsmith cmd->param.unit = 0; 72467555Smsmith cmd->param.param_count = 1; 72560894Smsmith 72660894Smsmith /* fill in the outbound parameter data */ 72760894Smsmith param->table_id = table_id; 72867555Smsmith param->parameter_id = param_id; 72967555Smsmith param->parameter_size_bytes = param_size; 73060894Smsmith 73160894Smsmith /* submit the command and either wait or let the callback handle it */ 73260894Smsmith if (func == NULL) { 73360894Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 73460894Smsmith error = twe_immediate_request(tr); 73560894Smsmith if (error == 0) { 73669543Smsmith if (twe_report_request(tr)) 73760894Smsmith goto err; 73860894Smsmith } 73967555Smsmith twe_release_request(tr); 74067555Smsmith return(param); 74160894Smsmith } else { 74260894Smsmith tr->tr_complete = func; 743118816Sps error = twe_map_request(tr); 74460894Smsmith if (error == 0) 74560894Smsmith return(func); 74660894Smsmith } 74760894Smsmith 74860894Smsmith /* something failed */ 74960894Smsmitherr: 75060894Smsmith debug(1, "failed"); 75160894Smsmith if (tr != NULL) 75260894Smsmith twe_release_request(tr); 75360894Smsmith if (param != NULL) 75460894Smsmith free(param, M_DEVBUF); 75560894Smsmith return(NULL); 75660894Smsmith} 75760894Smsmith 75860894Smsmith/******************************************************************************** 75967555Smsmith * Set integer parameter table entries. 76067555Smsmith */ 76191449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 76267555Smsmithstatic int 76367555Smsmithtwe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) 76467555Smsmith{ 76567555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 76667555Smsmith} 76791449Speter#endif 76867555Smsmith 76991449Speter#if 0 77067555Smsmithstatic int 77167555Smsmithtwe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) 77267555Smsmith{ 77367555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 77467555Smsmith} 77567555Smsmith 77667555Smsmithstatic int 77767555Smsmithtwe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) 77867555Smsmith{ 77967555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 78067555Smsmith} 78191449Speter#endif 78267555Smsmith 78367555Smsmith/******************************************************************************** 78467555Smsmith * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. 78567555Smsmith */ 78667555Smsmithstatic int 78767555Smsmithtwe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) 78867555Smsmith{ 78967555Smsmith struct twe_request *tr; 79067555Smsmith TWE_Command *cmd; 79167555Smsmith TWE_Param *param; 79267555Smsmith int error; 79367555Smsmith 79467555Smsmith debug_called(4); 79567555Smsmith 79667555Smsmith tr = NULL; 79767555Smsmith param = NULL; 79867555Smsmith error = ENOMEM; 79967555Smsmith 80067555Smsmith /* get a command */ 80167555Smsmith if (twe_get_request(sc, &tr)) 80267555Smsmith goto out; 80367555Smsmith 80467555Smsmith /* get a buffer */ 80567555Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 80667555Smsmith goto out; 80767555Smsmith tr->tr_data = param; 80867555Smsmith tr->tr_length = TWE_SECTOR_SIZE; 80967555Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 81067555Smsmith 81167555Smsmith /* build the command for the controller */ 812118816Sps cmd = TWE_FIND_COMMAND(tr); 81367555Smsmith cmd->param.opcode = TWE_OP_SET_PARAM; 81467555Smsmith cmd->param.size = 2; 81567555Smsmith cmd->param.unit = 0; 81667555Smsmith cmd->param.param_count = 1; 81767555Smsmith 81867555Smsmith /* fill in the outbound parameter data */ 81967555Smsmith param->table_id = table_id; 82067555Smsmith param->parameter_id = param_id; 82167555Smsmith param->parameter_size_bytes = param_size; 82267555Smsmith bcopy(data, param->data, param_size); 82367555Smsmith 82467555Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 82567555Smsmith error = twe_immediate_request(tr); 82667555Smsmith if (error == 0) { 82769543Smsmith if (twe_report_request(tr)) 82867555Smsmith error = EIO; 82967555Smsmith } 83067555Smsmith 83167555Smsmithout: 83267555Smsmith if (tr != NULL) 83367555Smsmith twe_release_request(tr); 83467555Smsmith if (param != NULL) 83567555Smsmith free(param, M_DEVBUF); 83667555Smsmith return(error); 83767555Smsmith} 83867555Smsmith 83967555Smsmith/******************************************************************************** 84060894Smsmith * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. 84160894Smsmith * 84260894Smsmith * Typically called with interrupts disabled. 84360894Smsmith */ 84460894Smsmithstatic int 84567555Smsmithtwe_init_connection(struct twe_softc *sc, int mode) 84660894Smsmith{ 84760894Smsmith struct twe_request *tr; 84860894Smsmith TWE_Command *cmd; 84960894Smsmith int error; 85060894Smsmith 85160894Smsmith debug_called(4); 85260894Smsmith 85360894Smsmith /* get a command */ 85467555Smsmith if (twe_get_request(sc, &tr)) 855102291Sarchie return(0); 85660894Smsmith 85760894Smsmith /* build the command */ 858118816Sps cmd = TWE_FIND_COMMAND(tr); 85967555Smsmith cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; 86067555Smsmith cmd->initconnection.size = 3; 86167555Smsmith cmd->initconnection.host_id = 0; 86267555Smsmith cmd->initconnection.message_credits = mode; 86367555Smsmith cmd->initconnection.response_queue_pointer = 0; 86460894Smsmith 86560894Smsmith /* submit the command */ 86660894Smsmith error = twe_immediate_request(tr); 86760894Smsmith /* XXX check command result? */ 86860894Smsmith twe_unmap_request(tr); 86960894Smsmith twe_release_request(tr); 87060894Smsmith 87167555Smsmith if (mode == TWE_INIT_MESSAGE_CREDITS) 87267555Smsmith sc->twe_host_id = cmd->initconnection.host_id; 87360894Smsmith return(error); 87460894Smsmith} 87560894Smsmith 87660894Smsmith/******************************************************************************** 87760894Smsmith * Start the command (tr) and sleep waiting for it to complete. 87860894Smsmith * 87960894Smsmith * Successfully completed commands are dequeued. 88060894Smsmith */ 88160894Smsmithstatic int 88260894Smsmithtwe_wait_request(struct twe_request *tr) 88360894Smsmith{ 88467555Smsmith int s; 88560894Smsmith 88660894Smsmith debug_called(4); 88760894Smsmith 88867555Smsmith tr->tr_flags |= TWE_CMD_SLEEPER; 88967555Smsmith tr->tr_status = TWE_CMD_BUSY; 89067555Smsmith twe_enqueue_ready(tr); 89167555Smsmith twe_startio(tr->tr_sc); 89260894Smsmith s = splbio(); 89367555Smsmith while (tr->tr_status == TWE_CMD_BUSY) 89467555Smsmith tsleep(tr, PRIBIO, "twewait", 0); 89560894Smsmith splx(s); 89667555Smsmith 89767555Smsmith return(0); 89860894Smsmith} 89960894Smsmith 90060894Smsmith/******************************************************************************** 90160894Smsmith * Start the command (tr) and busy-wait for it to complete. 90260894Smsmith * This should only be used when interrupts are actually disabled (although it 90360894Smsmith * will work if they are not). 90460894Smsmith */ 90560894Smsmithstatic int 90660894Smsmithtwe_immediate_request(struct twe_request *tr) 90760894Smsmith{ 90860894Smsmith 90960894Smsmith debug_called(4); 91060894Smsmith 911118816Sps tr->tr_flags |= TWE_CMD_IMMEDIATE; 912118816Sps tr->tr_status = TWE_CMD_BUSY; 913118816Sps twe_map_request(tr); 91460894Smsmith 91560894Smsmith while (tr->tr_status == TWE_CMD_BUSY){ 91660894Smsmith twe_done(tr->tr_sc); 91760894Smsmith } 91860894Smsmith return(tr->tr_status != TWE_CMD_COMPLETE); 91960894Smsmith} 92060894Smsmith 92160894Smsmith/******************************************************************************** 92260894Smsmith * Handle completion of an I/O command. 92360894Smsmith */ 92460894Smsmithstatic void 92560894Smsmithtwe_completeio(struct twe_request *tr) 92660894Smsmith{ 92760894Smsmith struct twe_softc *sc = tr->tr_sc; 92867555Smsmith twe_bio *bp = (twe_bio *)tr->tr_private; 92960894Smsmith 93060894Smsmith debug_called(4); 93160894Smsmith 93260894Smsmith if (tr->tr_status == TWE_CMD_COMPLETE) { 93367555Smsmith 93469543Smsmith if (twe_report_request(tr)) 93567555Smsmith TWE_BIO_SET_ERROR(bp, EIO); 93667555Smsmith 93769543Smsmith } else { 93869543Smsmith twe_panic(sc, "twe_completeio on incomplete command"); 93960894Smsmith } 94069543Smsmith tr->tr_private = NULL; 94169543Smsmith twed_intr(bp); 94260894Smsmith twe_release_request(tr); 94360894Smsmith} 94460894Smsmith 94560894Smsmith/******************************************************************************** 94667555Smsmith * Reset the controller and pull all the active commands back onto the ready 94767555Smsmith * queue. Used to restart a controller that's exhibiting bad behaviour. 94867555Smsmith */ 94967555Smsmithstatic void 95067555Smsmithtwe_reset(struct twe_softc *sc) 95167555Smsmith{ 95267555Smsmith struct twe_request *tr; 95367555Smsmith int i, s; 95467555Smsmith 95591790Smsmith /* 95691790Smsmith * Sleep for a short period to allow AENs to be signalled. 95791790Smsmith */ 958119124Sps tsleep(sc, PRIBIO, "twereset", hz); 95967555Smsmith 96067555Smsmith /* 96167555Smsmith * Disable interrupts from the controller, and mask any accidental entry 96267555Smsmith * into our interrupt handler. 96367555Smsmith */ 96491790Smsmith twe_printf(sc, "controller reset in progress...\n"); 96567555Smsmith twe_disable_interrupts(sc); 96667555Smsmith s = splbio(); 96767555Smsmith 96867555Smsmith /* 96967555Smsmith * Try to soft-reset the controller. 97067555Smsmith */ 97167555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 97267555Smsmith 97367555Smsmith if (i > 0) 97467555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 97567555Smsmith 97667555Smsmith if (!twe_soft_reset(sc)) 97767555Smsmith break; /* reset process complete */ 97867555Smsmith } 97967555Smsmith /* did we give up? */ 98067555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 98167555Smsmith twe_printf(sc, "can't reset controller, giving up\n"); 98267555Smsmith goto out; 98367555Smsmith } 98467555Smsmith 98567555Smsmith /* 98667555Smsmith * Move all of the commands that were busy back to the ready queue. 98767555Smsmith */ 98867555Smsmith i = 0; 98967555Smsmith while ((tr = twe_dequeue_busy(sc)) != NULL) { 99067555Smsmith twe_enqueue_ready(tr); 99167555Smsmith i++; 99267555Smsmith } 99367555Smsmith 99467555Smsmith /* 99567555Smsmith * Kick the controller to start things going again, then re-enable interrupts. 99667555Smsmith */ 99767555Smsmith twe_startio(sc); 99867555Smsmith twe_enable_interrupts(sc); 99967555Smsmith twe_printf(sc, "controller reset done, %d commands restarted\n", i); 100067555Smsmith 100167555Smsmithout: 100267555Smsmith splx(s); 100367555Smsmith twe_enable_interrupts(sc); 100467555Smsmith} 100567555Smsmith 100667555Smsmith/******************************************************************************** 100760894Smsmith ******************************************************************************** 100860894Smsmith Command I/O to Controller 100960894Smsmith ******************************************************************************** 101060894Smsmith ********************************************************************************/ 101160894Smsmith 101260894Smsmith/******************************************************************************** 101360894Smsmith * Try to deliver (tr) to the controller. 101460894Smsmith * 101560894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 101660894Smsmith */ 1017118816Spsint 101860894Smsmithtwe_start(struct twe_request *tr) 101960894Smsmith{ 102060894Smsmith struct twe_softc *sc = tr->tr_sc; 1021118816Sps TWE_Command *cmd; 102260894Smsmith int i, s, done; 102360894Smsmith u_int32_t status_reg; 102460894Smsmith 102560894Smsmith debug_called(4); 102660894Smsmith 102760894Smsmith /* mark the command as currently being processed */ 102860894Smsmith tr->tr_status = TWE_CMD_BUSY; 1029118816Sps cmd = TWE_FIND_COMMAND(tr); 103060894Smsmith 103167555Smsmith /* 103267555Smsmith * Spin briefly waiting for the controller to come ready 103367555Smsmith * 103467555Smsmith * XXX it might be more efficient to return EBUSY immediately 103567555Smsmith * and let the command be rescheduled. 103667555Smsmith */ 103760894Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 103860894Smsmith s = splbio(); 103960894Smsmith 104060894Smsmith /* check to see if we can post a command */ 104160894Smsmith status_reg = TWE_STATUS(sc); 104260894Smsmith twe_check_bits(sc, status_reg); 104360894Smsmith 104460894Smsmith if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { 1045118816Sps twe_enqueue_busy(tr); 1046118816Sps 1047118816Sps TWE_COMMAND_QUEUE(sc, TWE_FIND_COMMANDPHYS(tr)); 104860894Smsmith done = 1; 104960894Smsmith /* move command to work queue */ 105067555Smsmith#ifdef TWE_DEBUG 105160894Smsmith if (tr->tr_complete != NULL) { 1052118816Sps debug(3, "queued request %d with callback %p", cmd->generic.request_id, tr->tr_complete); 105367555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { 1054118816Sps debug(3, "queued request %d with wait channel %p", cmd->generic.request_id, tr); 105560894Smsmith } else { 1056118816Sps debug(3, "queued request %d for polling caller", cmd->generic.request_id); 105760894Smsmith } 105867555Smsmith#endif 105960894Smsmith } 106060894Smsmith splx(s); /* drop spl to allow completion interrupts */ 106160894Smsmith } 106260894Smsmith 106360894Smsmith /* command is enqueued */ 106460894Smsmith if (done) 106560894Smsmith return(0); 106660894Smsmith 106760894Smsmith /* 106860894Smsmith * We couldn't get the controller to take the command; try submitting it again later. 106960894Smsmith * This should only happen if something is wrong with the controller, or if we have 107060894Smsmith * overestimated the number of commands it can accept. (Should we actually reject 107160894Smsmith * the command at this point?) 107260894Smsmith */ 107360894Smsmith return(EBUSY); 107460894Smsmith} 107560894Smsmith 107660894Smsmith/******************************************************************************** 107760894Smsmith * Poll the controller (sc) for completed commands. 107860894Smsmith * 107960894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 108060894Smsmith */ 108160894Smsmithstatic void 108260894Smsmithtwe_done(struct twe_softc *sc) 108360894Smsmith{ 108460894Smsmith TWE_Response_Queue rq; 1085118816Sps TWE_Command *cmd; 108660894Smsmith struct twe_request *tr; 108760894Smsmith int s, found; 108860894Smsmith u_int32_t status_reg; 108960894Smsmith 109060894Smsmith debug_called(5); 109160894Smsmith 109260894Smsmith /* loop collecting completed commands */ 109360894Smsmith found = 0; 109460894Smsmith s = splbio(); 109560894Smsmith for (;;) { 109660894Smsmith status_reg = TWE_STATUS(sc); 109760894Smsmith twe_check_bits(sc, status_reg); /* XXX should this fail? */ 109860894Smsmith 109960894Smsmith if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { 110060894Smsmith found = 1; 110160894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 110267555Smsmith tr = sc->twe_lookup[rq.u.response_id]; /* find command */ 1103118816Sps cmd = TWE_FIND_COMMAND(tr); 110469543Smsmith if (tr->tr_status != TWE_CMD_BUSY) 110569543Smsmith twe_printf(sc, "completion event for nonbusy command\n"); 110669543Smsmith tr->tr_status = TWE_CMD_COMPLETE; 110769543Smsmith debug(3, "completed request id %d with status %d", 1108118816Sps cmd->generic.request_id, cmd->generic.status); 110969543Smsmith /* move to completed queue */ 111069543Smsmith twe_remove_busy(tr); 111169543Smsmith twe_enqueue_complete(tr); 111260894Smsmith } else { 111360894Smsmith break; /* no response ready */ 111460894Smsmith } 111560894Smsmith } 111660894Smsmith splx(s); 111760894Smsmith 111860894Smsmith /* if we've completed any commands, try posting some more */ 111960894Smsmith if (found) 112060894Smsmith twe_startio(sc); 112160894Smsmith 112260894Smsmith /* handle completion and timeouts */ 112367555Smsmith twe_complete(sc); /* XXX use deferred completion? */ 112460894Smsmith} 112560894Smsmith 112660894Smsmith/******************************************************************************** 112760894Smsmith * Perform post-completion processing for commands on (sc). 112860894Smsmith * 112960894Smsmith * This is split from twe_done as it can be safely deferred and run at a lower 113060894Smsmith * priority level should facilities for such a thing become available. 113160894Smsmith */ 113260894Smsmithstatic void 113360894Smsmithtwe_complete(struct twe_softc *sc) 113460894Smsmith{ 113567555Smsmith struct twe_request *tr; 113660894Smsmith 113760894Smsmith debug_called(5); 113860894Smsmith 113960894Smsmith /* 114067555Smsmith * Pull commands off the completed list, dispatch them appropriately 114160894Smsmith */ 114267555Smsmith while ((tr = twe_dequeue_complete(sc)) != NULL) { 114367555Smsmith /* unmap the command's data buffer */ 114467555Smsmith twe_unmap_request(tr); 114560894Smsmith 114667555Smsmith /* dispatch to suit command originator */ 114767555Smsmith if (tr->tr_complete != NULL) { /* completion callback */ 114867555Smsmith debug(2, "call completion handler %p", tr->tr_complete); 114967555Smsmith tr->tr_complete(tr); 115060894Smsmith 115167555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ 115267555Smsmith debug(2, "wake up command owner on %p", tr); 115367555Smsmith wakeup_one(tr); 115460894Smsmith 115567555Smsmith } else { /* caller is polling command */ 115667555Smsmith debug(2, "command left for owner"); 115760894Smsmith } 115867555Smsmith } 1159118816Sps 1160118816Sps sc->twe_state &= ~TWE_STATE_FRZN; 116160894Smsmith} 116260894Smsmith 116360894Smsmith/******************************************************************************** 116460894Smsmith * Wait for (status) to be set in the controller status register for up to 116560894Smsmith * (timeout) seconds. Returns 0 if found, nonzero if we time out. 116660894Smsmith * 116760894Smsmith * Note: this busy-waits, rather than sleeping, since we may be called with 116860894Smsmith * eg. clock interrupts masked. 116960894Smsmith */ 117060894Smsmithstatic int 117160894Smsmithtwe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) 117260894Smsmith{ 117360894Smsmith time_t expiry; 117460894Smsmith u_int32_t status_reg; 117560894Smsmith 117660894Smsmith debug_called(4); 117760894Smsmith 117860894Smsmith expiry = time_second + timeout; 117960894Smsmith 118060894Smsmith do { 118160894Smsmith status_reg = TWE_STATUS(sc); 118260894Smsmith if (status_reg & status) /* got the required bit(s)? */ 118360894Smsmith return(0); 118460894Smsmith DELAY(100000); 118560894Smsmith } while (time_second <= expiry); 118660894Smsmith 118760894Smsmith return(1); 118860894Smsmith} 118960894Smsmith 119060894Smsmith/******************************************************************************** 119160894Smsmith * Drain the response queue, which may contain responses to commands we know 119260894Smsmith * nothing about. 119360894Smsmith */ 119460894Smsmithstatic int 119560894Smsmithtwe_drain_response_queue(struct twe_softc *sc) 119660894Smsmith{ 119760894Smsmith TWE_Response_Queue rq; 119860894Smsmith u_int32_t status_reg; 119960894Smsmith 120060894Smsmith debug_called(4); 120160894Smsmith 120260894Smsmith for (;;) { /* XXX give up eventually? */ 120360894Smsmith status_reg = TWE_STATUS(sc); 120460894Smsmith if (twe_check_bits(sc, status_reg)) 120560894Smsmith return(1); 120660894Smsmith if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) 120760894Smsmith return(0); 120860894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 120960894Smsmith } 121060894Smsmith} 121160894Smsmith 121260894Smsmith/******************************************************************************** 121367555Smsmith * Soft-reset the controller 121467555Smsmith */ 121567555Smsmithstatic int 121667555Smsmithtwe_soft_reset(struct twe_softc *sc) 121767555Smsmith{ 121867555Smsmith u_int32_t status_reg; 121967555Smsmith 122067555Smsmith debug_called(2); 122167555Smsmith 122267555Smsmith TWE_SOFT_RESET(sc); 122367555Smsmith 122491790Smsmith if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) { 122567683Smsmith twe_printf(sc, "no attention interrupt\n"); 122667555Smsmith return(1); 122767555Smsmith } 122891790Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 122967555Smsmith if (twe_drain_aen_queue(sc)) { 123067555Smsmith twe_printf(sc, "can't drain AEN queue\n"); 123167555Smsmith return(1); 123267555Smsmith } 123367555Smsmith if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { 123467555Smsmith twe_printf(sc, "reset not reported\n"); 123567555Smsmith return(1); 123667555Smsmith } 123767555Smsmith status_reg = TWE_STATUS(sc); 123867555Smsmith if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { 123967555Smsmith twe_printf(sc, "controller errors detected\n"); 124067555Smsmith return(1); 124167555Smsmith } 124267555Smsmith if (twe_drain_response_queue(sc)) { 124367555Smsmith twe_printf(sc, "can't drain response queue\n"); 124467555Smsmith return(1); 124567555Smsmith } 124667555Smsmith return(0); 124767555Smsmith} 124867555Smsmith 124967555Smsmith/******************************************************************************** 125060894Smsmith ******************************************************************************** 125160894Smsmith Interrupt Handling 125260894Smsmith ******************************************************************************** 125360894Smsmith ********************************************************************************/ 125460894Smsmith 125560894Smsmith/******************************************************************************** 125660894Smsmith * Host interrupt. 125760894Smsmith * 125860894Smsmith * XXX what does this mean? 125960894Smsmith */ 126060894Smsmithstatic void 126160894Smsmithtwe_host_intr(struct twe_softc *sc) 126260894Smsmith{ 126360894Smsmith debug_called(4); 126460894Smsmith 126567555Smsmith twe_printf(sc, "host interrupt\n"); 126660894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); 126760894Smsmith} 126860894Smsmith 126960894Smsmith/******************************************************************************** 127060894Smsmith * Attention interrupt. 127160894Smsmith * 127260894Smsmith * Signalled when the controller has one or more AENs for us. 127360894Smsmith */ 127460894Smsmithstatic void 127560894Smsmithtwe_attention_intr(struct twe_softc *sc) 127660894Smsmith{ 127760894Smsmith debug_called(4); 127860894Smsmith 127960894Smsmith /* instigate a poll for AENs */ 128060894Smsmith if (twe_fetch_aen(sc)) { 128167555Smsmith twe_printf(sc, "error polling for signalled AEN\n"); 128260894Smsmith } else { 128360894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 128460894Smsmith } 128560894Smsmith} 128660894Smsmith 128760894Smsmith/******************************************************************************** 128860894Smsmith * Command interrupt. 128960894Smsmith * 129060894Smsmith * Signalled when the controller can handle more commands. 129160894Smsmith */ 129260894Smsmithstatic void 129360894Smsmithtwe_command_intr(struct twe_softc *sc) 129460894Smsmith{ 129560894Smsmith debug_called(4); 129660894Smsmith 129760894Smsmith /* 129860894Smsmith * We don't use this, rather we try to submit commands when we receive 129960894Smsmith * them, and when other commands have completed. Mask it so we don't get 130060894Smsmith * another one. 130160894Smsmith */ 130267555Smsmith twe_printf(sc, "command interrupt\n"); 130360894Smsmith TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); 130460894Smsmith} 130560894Smsmith 130660894Smsmith/******************************************************************************** 130760894Smsmith ******************************************************************************** 130860894Smsmith Asynchronous Event Handling 130960894Smsmith ******************************************************************************** 131060894Smsmith ********************************************************************************/ 131160894Smsmith 131260894Smsmith/******************************************************************************** 131360894Smsmith * Request an AEN from the controller. 131460894Smsmith */ 131560894Smsmithstatic int 131660894Smsmithtwe_fetch_aen(struct twe_softc *sc) 131760894Smsmith{ 131860894Smsmith 131960894Smsmith debug_called(4); 132060894Smsmith 132167555Smsmith if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) 132260894Smsmith return(EIO); 132360894Smsmith return(0); 132460894Smsmith} 132560894Smsmith 132660894Smsmith/******************************************************************************** 132760894Smsmith * Handle an AEN returned by the controller. 132860894Smsmith */ 132960894Smsmithstatic void 133060894Smsmithtwe_handle_aen(struct twe_request *tr) 133160894Smsmith{ 133260894Smsmith struct twe_softc *sc = tr->tr_sc; 133360894Smsmith TWE_Param *param; 133460894Smsmith u_int16_t aen; 133560894Smsmith 133660894Smsmith debug_called(4); 133760894Smsmith 133860894Smsmith /* XXX check for command success somehow? */ 133960894Smsmith 134060894Smsmith param = (TWE_Param *)tr->tr_data; 134160894Smsmith aen = *(u_int16_t *)(param->data); 134260894Smsmith 134360894Smsmith free(tr->tr_data, M_DEVBUF); 134460894Smsmith twe_release_request(tr); 134560894Smsmith twe_enqueue_aen(sc, aen); 134660894Smsmith 134760894Smsmith /* XXX poll for more AENs? */ 134860894Smsmith} 134960894Smsmith 135060894Smsmith/******************************************************************************** 135160894Smsmith * Pull AENs out of the controller and park them in the queue, in a context where 135260894Smsmith * interrupts aren't active. Return nonzero if we encounter any errors in the 135360894Smsmith * process of obtaining all the available AENs. 135460894Smsmith */ 135560894Smsmithstatic int 135660894Smsmithtwe_drain_aen_queue(struct twe_softc *sc) 135760894Smsmith{ 135860894Smsmith u_int16_t aen; 135960894Smsmith 136060894Smsmith for (;;) { 136167555Smsmith if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) 136260894Smsmith return(1); 136360894Smsmith if (aen == TWE_AEN_QUEUE_EMPTY) 136460894Smsmith return(0); 136560894Smsmith twe_enqueue_aen(sc, aen); 136660894Smsmith } 136760894Smsmith} 136860894Smsmith 136960894Smsmith/******************************************************************************** 137060894Smsmith * Push an AEN that we've received onto the queue. 137160894Smsmith * 137260894Smsmith * Note that we have to lock this against reentrance, since it may be called 137360894Smsmith * from both interrupt and non-interrupt context. 137460894Smsmith * 137560894Smsmith * If someone is waiting for the AEN we have, wake them up. 137660894Smsmith */ 137760894Smsmithstatic void 137860894Smsmithtwe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) 137960894Smsmith{ 138067555Smsmith char *msg; 138167555Smsmith int s, next, nextnext; 138260894Smsmith 138360894Smsmith debug_called(4); 138460894Smsmith 138567555Smsmith if ((msg = twe_format_aen(sc, aen)) != NULL) 138667555Smsmith twe_printf(sc, "AEN: <%s>\n", msg); 138760894Smsmith 138860894Smsmith s = splbio(); 138960894Smsmith /* enqueue the AEN */ 139060894Smsmith next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); 139167555Smsmith nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); 139267555Smsmith 139367555Smsmith /* check to see if this is the last free slot, and subvert the AEN if it is */ 139467555Smsmith if (nextnext == sc->twe_aen_tail) 139567555Smsmith aen = TWE_AEN_QUEUE_FULL; 139667555Smsmith 139767555Smsmith /* look to see if there's room for this AEN */ 139860894Smsmith if (next != sc->twe_aen_tail) { 139960894Smsmith sc->twe_aen_queue[sc->twe_aen_head] = aen; 140060894Smsmith sc->twe_aen_head = next; 140160894Smsmith } 140260894Smsmith 140367555Smsmith /* wake up anyone asleep on the queue */ 140467555Smsmith wakeup(&sc->twe_aen_queue); 140567555Smsmith 140660894Smsmith /* anyone looking for this AEN? */ 140760894Smsmith if (sc->twe_wait_aen == aen) { 140860894Smsmith sc->twe_wait_aen = -1; 140960894Smsmith wakeup(&sc->twe_wait_aen); 141060894Smsmith } 141160894Smsmith splx(s); 141260894Smsmith} 141360894Smsmith 141460894Smsmith/******************************************************************************** 141560894Smsmith * Pop an AEN off the queue, or return -1 if there are none left. 141660894Smsmith * 141760894Smsmith * We are more or less interrupt-safe, so don't block interrupts. 141860894Smsmith */ 141960894Smsmithstatic int 142060894Smsmithtwe_dequeue_aen(struct twe_softc *sc) 142160894Smsmith{ 142260894Smsmith int result; 142360894Smsmith 142460894Smsmith debug_called(4); 142560894Smsmith 142660894Smsmith if (sc->twe_aen_tail == sc->twe_aen_head) { 1427118508Sps result = TWE_AEN_QUEUE_EMPTY; 142860894Smsmith } else { 142960894Smsmith result = sc->twe_aen_queue[sc->twe_aen_tail]; 143060894Smsmith sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); 143160894Smsmith } 143260894Smsmith return(result); 143360894Smsmith} 143460894Smsmith 143560894Smsmith/******************************************************************************** 143660894Smsmith * Check to see if the requested AEN is in the queue. 143760894Smsmith * 143860894Smsmith * XXX we could probably avoid masking interrupts here 143960894Smsmith */ 144060894Smsmithstatic int 144160894Smsmithtwe_find_aen(struct twe_softc *sc, u_int16_t aen) 144260894Smsmith{ 144360894Smsmith int i, s, missing; 144460894Smsmith 144560894Smsmith missing = 1; 144660894Smsmith s = splbio(); 144760894Smsmith for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { 144860894Smsmith if (sc->twe_aen_queue[i] == aen) 144960894Smsmith missing = 0; 145060894Smsmith } 145167555Smsmith splx(s); 145260894Smsmith return(missing); 145360894Smsmith} 145460894Smsmith 145560894Smsmith 145660894Smsmith#if 0 /* currently unused */ 145760894Smsmith/******************************************************************************** 145860894Smsmith * Sleep waiting for at least (timeout) seconds until we see (aen) as 145960894Smsmith * requested. Returns nonzero on timeout or failure. 146060894Smsmith * 146160894Smsmith * XXX: this should not be used in cases where there may be more than one sleeper 146260894Smsmith * without a mechanism for registering multiple sleepers. 146360894Smsmith */ 146460894Smsmithstatic int 146560894Smsmithtwe_wait_aen(struct twe_softc *sc, int aen, int timeout) 146660894Smsmith{ 146760894Smsmith time_t expiry; 146860894Smsmith int found, s; 146960894Smsmith 147060894Smsmith debug_called(4); 147160894Smsmith 147260894Smsmith expiry = time_second + timeout; 147360894Smsmith found = 0; 147460894Smsmith 147560894Smsmith s = splbio(); 147660894Smsmith sc->twe_wait_aen = aen; 147760894Smsmith do { 147860894Smsmith twe_fetch_aen(sc); 147960894Smsmith tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); 148060894Smsmith if (sc->twe_wait_aen == -1) 148160894Smsmith found = 1; 148260894Smsmith } while ((time_second <= expiry) && !found); 148360894Smsmith splx(s); 148460894Smsmith return(!found); 148560894Smsmith} 148660894Smsmith#endif 148760894Smsmith 148860894Smsmith/******************************************************************************** 148960894Smsmith ******************************************************************************** 149060894Smsmith Command Buffer Management 149160894Smsmith ******************************************************************************** 149260894Smsmith ********************************************************************************/ 149360894Smsmith 149460894Smsmith/******************************************************************************** 149560894Smsmith * Get a new command buffer. 149660894Smsmith * 149767555Smsmith * This will return NULL if all command buffers are in use. 149860894Smsmith */ 149967555Smsmithstatic int 150067555Smsmithtwe_get_request(struct twe_softc *sc, struct twe_request **tr) 150160894Smsmith{ 1502118816Sps TWE_Command *cmd; 150360894Smsmith debug_called(4); 150460894Smsmith 150560894Smsmith /* try to reuse an old buffer */ 150667555Smsmith *tr = twe_dequeue_free(sc); 150760894Smsmith 150867555Smsmith /* initialise some fields to their defaults */ 150967555Smsmith if (*tr != NULL) { 1510118816Sps cmd = TWE_FIND_COMMAND(*tr); 151167555Smsmith (*tr)->tr_data = NULL; 151269543Smsmith (*tr)->tr_private = NULL; 151367555Smsmith (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ 151467555Smsmith (*tr)->tr_flags = 0; 151567555Smsmith (*tr)->tr_complete = NULL; 1516118816Sps cmd->generic.status = 0; /* before submission to controller */ 1517118816Sps cmd->generic.flags = 0; /* not used */ 151860894Smsmith } 151967555Smsmith return(*tr == NULL); 152060894Smsmith} 152160894Smsmith 152260894Smsmith/******************************************************************************** 152367555Smsmith * Release a command buffer for reuse. 152460894Smsmith * 152560894Smsmith */ 152660894Smsmithstatic void 152760894Smsmithtwe_release_request(struct twe_request *tr) 152860894Smsmith{ 152960894Smsmith debug_called(4); 153060894Smsmith 153169543Smsmith if (tr->tr_private != NULL) 153269543Smsmith twe_panic(tr->tr_sc, "tr_private != NULL"); 153367555Smsmith twe_enqueue_free(tr); 153460894Smsmith} 153560894Smsmith 153660894Smsmith/******************************************************************************** 153767555Smsmith ******************************************************************************** 153867555Smsmith Debugging 153967555Smsmith ******************************************************************************** 154067555Smsmith ********************************************************************************/ 154160894Smsmith 154260894Smsmith/******************************************************************************** 154367555Smsmith * Print some information about the controller 154460894Smsmith */ 154567555Smsmithvoid 154667555Smsmithtwe_describe_controller(struct twe_softc *sc) 154760894Smsmith{ 154867555Smsmith TWE_Param *p[6]; 154967555Smsmith u_int8_t ports; 155067555Smsmith u_int32_t size; 155160894Smsmith int i; 155260894Smsmith 155367555Smsmith debug_called(2); 155460894Smsmith 155567555Smsmith /* get the port count */ 155667555Smsmith twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); 155760894Smsmith 155867555Smsmith /* get version strings */ 155967555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); 156067555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); 156167555Smsmith p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); 156267555Smsmith p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); 156367555Smsmith p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); 156467555Smsmith p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); 156560894Smsmith 156667555Smsmith twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[1]->data, p[2]->data); 156767555Smsmith if (bootverbose) 156867555Smsmith twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[0]->data, p[3]->data, 156967555Smsmith p[4]->data, p[5]->data); 157067555Smsmith free(p[0], M_DEVBUF); 157167555Smsmith free(p[1], M_DEVBUF); 157267555Smsmith free(p[2], M_DEVBUF); 157367555Smsmith free(p[3], M_DEVBUF); 157467555Smsmith free(p[4], M_DEVBUF); 157567555Smsmith free(p[5], M_DEVBUF); 157660894Smsmith 157767555Smsmith /* print attached drives */ 157867555Smsmith if (bootverbose) { 157967555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); 158067555Smsmith for (i = 0; i < ports; i++) { 158167555Smsmith if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) 158267555Smsmith continue; 158367555Smsmith twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); 158467555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); 158567555Smsmith if (p[1] != NULL) { 158667555Smsmith twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); 158767555Smsmith free(p[1], M_DEVBUF); 158867555Smsmith } else { 158967555Smsmith twe_printf(sc, "port %d, drive status unavailable\n", i); 159067555Smsmith } 159160894Smsmith } 159267555Smsmith free(p[0], M_DEVBUF); 159360894Smsmith } 159460894Smsmith} 159560894Smsmith 159660894Smsmith/******************************************************************************** 159760894Smsmith * Complain if the status bits aren't what we're expecting. 159867555Smsmith * 159967555Smsmith * Rate-limit the complaints to at most one of each every five seconds, but 160067555Smsmith * always return the correct status. 160160894Smsmith */ 160260894Smsmithstatic int 160360894Smsmithtwe_check_bits(struct twe_softc *sc, u_int32_t status_reg) 160460894Smsmith{ 160567555Smsmith int result; 160667555Smsmith static time_t lastwarn[2] = {0, 0}; 160760894Smsmith 160867555Smsmith /* 160967555Smsmith * This can be a little problematic, as twe_panic may call twe_reset if 161067555Smsmith * TWE_DEBUG is not set, which will call us again as part of the soft reset. 161167555Smsmith */ 161267555Smsmith if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { 161367555Smsmith twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, 161467555Smsmith TWE_STATUS_BITS_DESCRIPTION); 161567555Smsmith twe_panic(sc, "fatal status bits"); 161667555Smsmith } 161767555Smsmith 161860894Smsmith result = 0; 161960894Smsmith if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { 162067555Smsmith if (time_second > (lastwarn[0] + 5)) { 162167555Smsmith twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, 162267555Smsmith TWE_STATUS_BITS_DESCRIPTION); 162367555Smsmith lastwarn[0] = time_second; 162467555Smsmith } 162560894Smsmith result = 1; 162660894Smsmith } 162760894Smsmith 162860894Smsmith if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { 162967555Smsmith if (time_second > (lastwarn[1] + 5)) { 163067555Smsmith twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, 163167555Smsmith TWE_STATUS_BITS_DESCRIPTION); 163267555Smsmith lastwarn[1] = time_second; 163367555Smsmith } 163460894Smsmith result = 1; 163591790Smsmith if (status_reg & TWE_STATUS_PCI_PARITY_ERROR) { 163691790Smsmith twe_printf(sc, "PCI parity error: Reseat card, move card or buggy device present."); 163791790Smsmith twe_clear_pci_parity_error(sc); 163891790Smsmith } 163991790Smsmith if (status_reg & TWE_STATUS_PCI_ABORT) { 1640118816Sps twe_printf(sc, "PCI abort, clearing.\n"); 164191790Smsmith twe_clear_pci_abort(sc); 164291790Smsmith } 164360894Smsmith } 164467555Smsmith 164560894Smsmith return(result); 164660894Smsmith} 164760894Smsmith 164860894Smsmith/******************************************************************************** 164967555Smsmith * Return a string describing (aen). 165067555Smsmith * 165167555Smsmith * The low 8 bits of the aen are the code, the high 8 bits give the unit number 165267555Smsmith * where an AEN is specific to a unit. 165367555Smsmith * 165467555Smsmith * Note that we could expand this routine to handle eg. up/downgrading the status 165567555Smsmith * of a drive if we had some idea of what the drive's initial status was. 165660894Smsmith */ 165760894Smsmith 165860894Smsmithstatic char * 165967555Smsmithtwe_format_aen(struct twe_softc *sc, u_int16_t aen) 166060894Smsmith{ 166167555Smsmith static char buf[80]; 166267555Smsmith device_t child; 166367555Smsmith char *code, *msg; 166460894Smsmith 166567555Smsmith code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); 166667555Smsmith msg = code + 2; 166760894Smsmith 166867555Smsmith switch (*code) { 166967555Smsmith case 'q': 167067555Smsmith if (!bootverbose) 167167555Smsmith return(NULL); 167267555Smsmith /* FALLTHROUGH */ 167391790Smsmith case 'a': 167467555Smsmith return(msg); 167567555Smsmith 167667555Smsmith case 'c': 167767555Smsmith if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { 167867555Smsmith sprintf(buf, "twed%d: %s", device_get_unit(child), msg); 167967555Smsmith } else { 168067555Smsmith sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), 168167555Smsmith msg, TWE_AEN_UNIT(aen)); 168267555Smsmith } 168367555Smsmith return(buf); 168491790Smsmith 168591790Smsmith case 'p': 168691790Smsmith sprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen), 168791790Smsmith msg); 168891790Smsmith return(buf); 168991790Smsmith 169067555Smsmith 169167555Smsmith case 'x': 169267555Smsmith default: 169367555Smsmith break; 169467555Smsmith } 169567555Smsmith sprintf(buf, "unknown AEN 0x%x", aen); 169660894Smsmith return(buf); 169760894Smsmith} 169860894Smsmith 169969543Smsmith/******************************************************************************** 170069543Smsmith * Print a diagnostic if the status of the command warrants it, and return 170169543Smsmith * either zero (command was ok) or nonzero (command failed). 170269543Smsmith */ 170367555Smsmithstatic int 170469543Smsmithtwe_report_request(struct twe_request *tr) 170567555Smsmith{ 170669543Smsmith struct twe_softc *sc = tr->tr_sc; 1707118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 170876340Smsmith int result = 0; 170960894Smsmith 171076340Smsmith /* 171176340Smsmith * Check the command status value and handle accordingly. 171276340Smsmith */ 171376340Smsmith if (cmd->generic.status == TWE_STATUS_RESET) { 171476340Smsmith /* 171576340Smsmith * The status code 0xff requests a controller reset. 171676340Smsmith */ 171776340Smsmith twe_printf(sc, "command returned with controller rest request\n"); 171876340Smsmith twe_reset(sc); 171969543Smsmith result = 1; 172076340Smsmith } else if (cmd->generic.status > TWE_STATUS_FATAL) { 172169543Smsmith /* 172276340Smsmith * Fatal errors that don't require controller reset. 172391790Smsmith * 172491790Smsmith * We know a few special flags values. 172569543Smsmith */ 172691790Smsmith switch (cmd->generic.flags) { 172791790Smsmith case 0x1b: 172891790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 172991790Smsmith "drive timeout"); 173091790Smsmith break; 173191790Smsmith case 0x51: 173291790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 173391790Smsmith "unrecoverable drive error"); 173491790Smsmith break; 173591790Smsmith default: 173691790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 173791790Smsmith "controller error - %s (flags = 0x%x)\n", 173891790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 173991790Smsmith cmd->generic.flags); 174091790Smsmith result = 1; 174191790Smsmith } 174276340Smsmith } else if (cmd->generic.status > TWE_STATUS_WARNING) { 174376340Smsmith /* 174476340Smsmith * Warning level status. 174576340Smsmith */ 174691790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 174791790Smsmith "warning - %s (flags = 0x%x)\n", 174891790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 174991790Smsmith cmd->generic.flags); 175076340Smsmith } else if (cmd->generic.status > 0x40) { 175176340Smsmith /* 175276340Smsmith * Info level status. 175376340Smsmith */ 175491790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 175591790Smsmith "attention - %s (flags = 0x%x)\n", 175691790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 175791790Smsmith cmd->generic.flags); 175867555Smsmith } 175976340Smsmith 176069543Smsmith return(result); 176167555Smsmith} 176267555Smsmith 176369543Smsmith/******************************************************************************** 176469543Smsmith * Print some controller state to aid in debugging error/panic conditions 176569543Smsmith */ 176667555Smsmithvoid 176767555Smsmithtwe_print_controller(struct twe_softc *sc) 176860894Smsmith{ 176967555Smsmith u_int32_t status_reg; 177060894Smsmith 177167555Smsmith status_reg = TWE_STATUS(sc); 177267555Smsmith twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); 177369543Smsmith twe_printf(sc, " current max\n"); 177469543Smsmith twe_printf(sc, "free %04d %04d\n", sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max); 177569543Smsmith twe_printf(sc, "ready %04d %04d\n", sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max); 177669543Smsmith twe_printf(sc, "busy %04d %04d\n", sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max); 177769543Smsmith twe_printf(sc, "complete %04d %04d\n", sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max); 177869543Smsmith twe_printf(sc, "bioq %04d %04d\n", sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max); 177967555Smsmith twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); 178067555Smsmith} 178160894Smsmith 178267555Smsmithstatic void 178367555Smsmithtwe_panic(struct twe_softc *sc, char *reason) 178467555Smsmith{ 178567555Smsmith twe_print_controller(sc); 178667555Smsmith#ifdef TWE_DEBUG 178767555Smsmith panic(reason); 178867555Smsmith#else 178967555Smsmith twe_reset(sc); 179067555Smsmith#endif 179160894Smsmith} 179260894Smsmith 1793118816Sps#ifdef TWE_DEBUG 179460894Smsmith/******************************************************************************** 179560894Smsmith * Print a request/command in human-readable format. 179660894Smsmith */ 179760894Smsmithstatic void 179860894Smsmithtwe_print_request(struct twe_request *tr) 179960894Smsmith{ 180067555Smsmith struct twe_softc *sc = tr->tr_sc; 1801118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 180260894Smsmith int i; 180360894Smsmith 180467555Smsmith twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", 180567555Smsmith cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, 180667555Smsmith cmd->generic.unit, cmd->generic.host_id); 180767555Smsmith twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", 180867555Smsmith cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); 180967555Smsmith 181067555Smsmith switch(cmd->generic.opcode) { /* XXX add more opcodes? */ 181167555Smsmith case TWE_OP_READ: 181267555Smsmith case TWE_OP_WRITE: 181367555Smsmith twe_printf(sc, " lba %d\n", cmd->io.lba); 181467555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) 181567555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 181667555Smsmith i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); 181760894Smsmith break; 181860894Smsmith 181967555Smsmith case TWE_OP_GET_PARAM: 182067555Smsmith case TWE_OP_SET_PARAM: 182167555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) 182267555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 182367555Smsmith i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); 182460894Smsmith break; 182560894Smsmith 182667555Smsmith case TWE_OP_INIT_CONNECTION: 182767555Smsmith twe_printf(sc, " response queue pointer 0x%x\n", 182867555Smsmith cmd->initconnection.response_queue_pointer); 182967555Smsmith break; 183067555Smsmith 183160894Smsmith default: 183267555Smsmith break; 183360894Smsmith } 183467555Smsmith twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", 1835118816Sps tr, TWE_FIND_COMMANDPHYS(tr), tr->tr_data, tr->tr_dataphys, tr->tr_length); 183667555Smsmith twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", 183767555Smsmith tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); 183860894Smsmith} 183960894Smsmith 184060894Smsmith#endif 1841