twe.c revision 126099
160894Smsmith/*- 260894Smsmith * Copyright (c) 2000 Michael Smith 3123103Sps * Copyright (c) 2003 Paul Saab 4123103Sps * Copyright (c) 2003 Vinod Kashyap 560894Smsmith * Copyright (c) 2000 BSDi 660894Smsmith * All rights reserved. 760894Smsmith * 860894Smsmith * Redistribution and use in source and binary forms, with or without 960894Smsmith * modification, are permitted provided that the following conditions 1060894Smsmith * are met: 1160894Smsmith * 1. Redistributions of source code must retain the above copyright 1260894Smsmith * notice, this list of conditions and the following disclaimer. 1360894Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1460894Smsmith * notice, this list of conditions and the following disclaimer in the 1560894Smsmith * documentation and/or other materials provided with the distribution. 1660894Smsmith * 1760894Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1860894Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1960894Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2060894Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2160894Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2260894Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2360894Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2460894Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2560894Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2660894Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2760894Smsmith * SUCH DAMAGE. 2860894Smsmith * 2960894Smsmith * $FreeBSD: head/sys/dev/twe/twe.c 126099 2004-02-22 01:03:38Z cperciva $ 3060894Smsmith */ 3160894Smsmith 3260894Smsmith/* 3360894Smsmith * Driver for the 3ware Escalade family of IDE RAID controllers. 3460894Smsmith */ 3560894Smsmith 3667555Smsmith#include <dev/twe/twe_compat.h> 3760894Smsmith#include <dev/twe/twereg.h> 3867555Smsmith#include <dev/twe/tweio.h> 3960894Smsmith#include <dev/twe/twevar.h> 4067555Smsmith#define TWE_DEFINE_TABLES 4167555Smsmith#include <dev/twe/twe_tables.h> 4260894Smsmith 4360894Smsmith/* 4460894Smsmith * Command submission. 4560894Smsmith */ 4667555Smsmithstatic int twe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result); 4767555Smsmithstatic int twe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result); 4867555Smsmithstatic int twe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result); 4967555Smsmithstatic void *twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, 5060894Smsmith void (* func)(struct twe_request *tr)); 5191449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 5267555Smsmithstatic int twe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value); 5391449Speter#endif 5491449Speter#if 0 5567555Smsmithstatic int twe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value); 5667555Smsmithstatic int twe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value); 5791449Speter#endif 5867555Smsmithstatic int twe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, 5967555Smsmith void *data); 6067555Smsmithstatic int twe_init_connection(struct twe_softc *sc, int mode); 6167555Smsmithstatic int twe_wait_request(struct twe_request *tr); 6267555Smsmithstatic int twe_immediate_request(struct twe_request *tr); 6367555Smsmithstatic void twe_completeio(struct twe_request *tr); 6467555Smsmithstatic void twe_reset(struct twe_softc *sc); 65123103Spsstatic int twe_add_unit(struct twe_softc *sc, int unit); 66123103Spsstatic int twe_del_unit(struct twe_softc *sc, int unit); 6760894Smsmith 6860894Smsmith/* 6960894Smsmith * Command I/O to controller. 7060894Smsmith */ 7167555Smsmithstatic void twe_done(struct twe_softc *sc); 7267555Smsmithstatic void twe_complete(struct twe_softc *sc); 7367555Smsmithstatic int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout); 7467555Smsmithstatic int twe_drain_response_queue(struct twe_softc *sc); 7567555Smsmithstatic int twe_check_bits(struct twe_softc *sc, u_int32_t status_reg); 7667555Smsmithstatic int twe_soft_reset(struct twe_softc *sc); 7760894Smsmith 7860894Smsmith/* 7960894Smsmith * Interrupt handling. 8060894Smsmith */ 8167555Smsmithstatic void twe_host_intr(struct twe_softc *sc); 8267555Smsmithstatic void twe_attention_intr(struct twe_softc *sc); 8367555Smsmithstatic void twe_command_intr(struct twe_softc *sc); 8460894Smsmith 8560894Smsmith/* 8660894Smsmith * Asynchronous event handling. 8760894Smsmith */ 8867555Smsmithstatic int twe_fetch_aen(struct twe_softc *sc); 8967555Smsmithstatic void twe_handle_aen(struct twe_request *tr); 9067555Smsmithstatic void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen); 91123103Spsstatic u_int16_t twe_dequeue_aen(struct twe_softc *sc); 9267555Smsmithstatic int twe_drain_aen_queue(struct twe_softc *sc); 9367555Smsmithstatic int twe_find_aen(struct twe_softc *sc, u_int16_t aen); 9460894Smsmith 9560894Smsmith/* 9660894Smsmith * Command buffer management. 9760894Smsmith */ 9867555Smsmithstatic int twe_get_request(struct twe_softc *sc, struct twe_request **tr); 9967555Smsmithstatic void twe_release_request(struct twe_request *tr); 10060894Smsmith 10160894Smsmith/* 10260894Smsmith * Debugging. 10360894Smsmith */ 10467555Smsmithstatic char *twe_format_aen(struct twe_softc *sc, u_int16_t aen); 10569543Smsmithstatic int twe_report_request(struct twe_request *tr); 10667555Smsmithstatic void twe_panic(struct twe_softc *sc, char *reason); 10760894Smsmith 10860894Smsmith/******************************************************************************** 10960894Smsmith ******************************************************************************** 11060894Smsmith Public Interfaces 11160894Smsmith ******************************************************************************** 11260894Smsmith ********************************************************************************/ 11360894Smsmith 11460894Smsmith/******************************************************************************** 11567555Smsmith * Initialise the controller, set up driver data structures. 11660894Smsmith */ 11767555Smsmithint 11867555Smsmithtwe_setup(struct twe_softc *sc) 11960894Smsmith{ 12060894Smsmith struct twe_request *tr; 121118816Sps TWE_Command *cmd; 12291790Smsmith u_int32_t status_reg; 12367555Smsmith int i; 12460894Smsmith 12560894Smsmith debug_called(4); 12660894Smsmith 12760894Smsmith /* 12867555Smsmith * Initialise request queues. 12960894Smsmith */ 13067555Smsmith twe_initq_free(sc); 13167555Smsmith twe_initq_bio(sc); 13267555Smsmith twe_initq_ready(sc); 13367555Smsmith twe_initq_busy(sc); 13467555Smsmith twe_initq_complete(sc); 13567555Smsmith sc->twe_wait_aen = -1; 13660894Smsmith 13760894Smsmith /* 13867555Smsmith * Allocate request structures up front. 13960894Smsmith */ 14067555Smsmith for (i = 0; i < TWE_Q_LENGTH; i++) { 141118816Sps if ((tr = twe_allocate_request(sc, i)) == NULL) 14267555Smsmith return(ENOMEM); 14367555Smsmith /* 14467555Smsmith * Set global defaults that won't change. 14567555Smsmith */ 146118816Sps cmd = TWE_FIND_COMMAND(tr); 147118816Sps cmd->generic.host_id = sc->twe_host_id; /* controller-assigned host ID */ 148118816Sps cmd->generic.request_id = i; /* our index number */ 14967555Smsmith sc->twe_lookup[i] = tr; 15060894Smsmith 15167555Smsmith /* 15267555Smsmith * Put command onto the freelist. 15367555Smsmith */ 15467555Smsmith twe_release_request(tr); 15560894Smsmith } 15660894Smsmith 15760894Smsmith /* 15891790Smsmith * Check status register for errors, clear them. 15991790Smsmith */ 16091790Smsmith status_reg = TWE_STATUS(sc); 16191790Smsmith twe_check_bits(sc, status_reg); 16291790Smsmith 16391790Smsmith /* 16467555Smsmith * Wait for the controller to come ready. 16560894Smsmith */ 16667555Smsmith if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { 16767555Smsmith twe_printf(sc, "microcontroller not ready\n"); 16860894Smsmith return(ENXIO); 16960894Smsmith } 17060894Smsmith 17160894Smsmith /* 17267555Smsmith * Disable interrupts from the card. 17360894Smsmith */ 17467555Smsmith twe_disable_interrupts(sc); 17560894Smsmith 17660894Smsmith /* 17767555Smsmith * Soft reset the controller, look for the AEN acknowledging the reset, 17867555Smsmith * check for errors, drain the response queue. 17960894Smsmith */ 18067555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 18160894Smsmith 18267555Smsmith if (i > 0) 18367555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 18467555Smsmith 18567555Smsmith if (!twe_soft_reset(sc)) 18667555Smsmith break; /* reset process complete */ 18767555Smsmith } 18867555Smsmith /* did we give up? */ 18967555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 19067555Smsmith twe_printf(sc, "can't initialise controller, giving up\n"); 19160894Smsmith return(ENXIO); 19260894Smsmith } 19360894Smsmith 19460894Smsmith return(0); 19560894Smsmith} 19660894Smsmith 197123103Spsstatic int 198118508Spstwe_add_unit(struct twe_softc *sc, int unit) 19960894Smsmith{ 20060894Smsmith struct twe_drive *dr; 201123103Sps int table, error = 0; 20267555Smsmith u_int16_t dsize; 203118508Sps TWE_Param *drives = NULL, *param = NULL; 20467555Smsmith TWE_Unit_Descriptor *ud; 20560894Smsmith 206118508Sps if (unit < 0 || unit > TWE_MAX_UNITS) 207123103Sps return (EINVAL); 20860894Smsmith 20960894Smsmith /* 21067555Smsmith * The controller is in a safe state, so try to find drives attached to it. 21160894Smsmith */ 21267555Smsmith if ((drives = twe_get_param(sc, TWE_PARAM_UNITSUMMARY, TWE_PARAM_UNITSUMMARY_Status, 21367555Smsmith TWE_MAX_UNITS, NULL)) == NULL) { 21467555Smsmith twe_printf(sc, "can't detect attached units\n"); 215123103Sps return (EIO); 21660894Smsmith } 21760894Smsmith 218118508Sps dr = &sc->twe_drive[unit]; 219118508Sps /* check that the drive is online */ 220123103Sps if (!(drives->data[unit] & TWE_PARAM_UNITSTATUS_Online)) { 221123103Sps error = ENXIO; 222118508Sps goto out; 223123103Sps } 22460894Smsmith 225118508Sps table = TWE_PARAM_UNITINFO + unit; 22660894Smsmith 227118508Sps if (twe_get_param_4(sc, table, TWE_PARAM_UNITINFO_Capacity, &dr->td_size)) { 228118508Sps twe_printf(sc, "error fetching capacity for unit %d\n", unit); 229123103Sps error = EIO; 230118508Sps goto out; 231118508Sps } 232118508Sps if (twe_get_param_1(sc, table, TWE_PARAM_UNITINFO_Status, &dr->td_state)) { 233118508Sps twe_printf(sc, "error fetching state for unit %d\n", unit); 234123103Sps error = EIO; 235118508Sps goto out; 236118508Sps } 237118508Sps if (twe_get_param_2(sc, table, TWE_PARAM_UNITINFO_DescriptorSize, &dsize)) { 238118508Sps twe_printf(sc, "error fetching descriptor size for unit %d\n", unit); 239123103Sps error = EIO; 240118508Sps goto out; 241118508Sps } 242118508Sps if ((param = twe_get_param(sc, table, TWE_PARAM_UNITINFO_Descriptor, dsize - 3, NULL)) == NULL) { 243118508Sps twe_printf(sc, "error fetching descriptor for unit %d\n", unit); 244123103Sps error = EIO; 245118508Sps goto out; 246118508Sps } 247118508Sps ud = (TWE_Unit_Descriptor *)param->data; 248118508Sps dr->td_type = ud->configuration; 249118508Sps 250118508Sps /* build synthetic geometry as per controller internal rules */ 251118508Sps if (dr->td_size > 0x200000) { 252118508Sps dr->td_heads = 255; 253118508Sps dr->td_sectors = 63; 254118508Sps } else { 255118508Sps dr->td_heads = 64; 256118508Sps dr->td_sectors = 32; 257118508Sps } 258118508Sps dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors); 259123103Sps dr->td_twe_unit = unit; 260118508Sps 261123103Sps error = twe_attach_drive(sc, dr); 262118508Sps 263118508Spsout: 264118508Sps if (param != NULL) 26567555Smsmith free(param, M_DEVBUF); 266118508Sps if (drives != NULL) 267118508Sps free(drives, M_DEVBUF); 268123103Sps return (error); 269118508Sps} 27060894Smsmith 271123103Spsstatic int 272118508Spstwe_del_unit(struct twe_softc *sc, int unit) 273118508Sps{ 274123103Sps int error; 27560894Smsmith 276126099Scperciva if (unit < 0 || unit >= TWE_MAX_UNITS) 277123103Sps return (ENXIO); 27860894Smsmith 279123103Sps if (sc->twe_drive[unit].td_disk == NULL) 280123103Sps return (ENXIO); 281123103Sps 282123103Sps error = twe_detach_drive(sc, unit); 283123103Sps return (error); 284118508Sps} 285118508Sps 286118508Sps/******************************************************************************** 287118508Sps * Locate disk devices and attach children to them. 288118508Sps */ 289118508Spsvoid 290118508Spstwe_init(struct twe_softc *sc) 291118508Sps{ 292118508Sps int i; 293118508Sps 29460894Smsmith /* 295118508Sps * Scan for drives 296118508Sps */ 297118508Sps for (i = 0; i < TWE_MAX_UNITS; i++) 298118508Sps twe_add_unit(sc, i); 299118508Sps 300118508Sps /* 30160894Smsmith * Initialise connection with controller. 30260894Smsmith */ 30367555Smsmith twe_init_connection(sc, TWE_INIT_MESSAGE_CREDITS); 30460894Smsmith 30567555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 30667555Smsmith /* 30767555Smsmith * Tell the controller we support shutdown notification. 30867555Smsmith */ 30967555Smsmith twe_set_param_1(sc, TWE_PARAM_FEATURES, TWE_PARAM_FEATURES_DriverShutdown, 1); 31067555Smsmith#endif 31167555Smsmith 31260894Smsmith /* 31360894Smsmith * Mark controller up and ready to run. 31460894Smsmith */ 31560894Smsmith sc->twe_state &= ~TWE_STATE_SHUTDOWN; 31660894Smsmith 31760894Smsmith /* 31867555Smsmith * Finally enable interrupts. 31960894Smsmith */ 32060894Smsmith twe_enable_interrupts(sc); 32160894Smsmith} 32260894Smsmith 32360894Smsmith/******************************************************************************** 32467555Smsmith * Stop the controller 32560894Smsmith */ 32667555Smsmithvoid 32767555Smsmithtwe_deinit(struct twe_softc *sc) 32860894Smsmith{ 32960894Smsmith /* 33060894Smsmith * Mark the controller as shutting down, and disable any further interrupts. 33160894Smsmith */ 33260894Smsmith sc->twe_state |= TWE_STATE_SHUTDOWN; 33360894Smsmith twe_disable_interrupts(sc); 33460894Smsmith 33567555Smsmith#ifdef TWE_SHUTDOWN_NOTIFICATION 33667555Smsmith /* 33767555Smsmith * Disconnect from the controller 33860894Smsmith */ 33967555Smsmith twe_init_connection(sc, TWE_SHUTDOWN_MESSAGE_CREDITS); 34067555Smsmith#endif 34160894Smsmith} 34260894Smsmith 34360894Smsmith/******************************************************************************* 34460894Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy 34560894Smsmith * status. 34660894Smsmith */ 34767555Smsmithvoid 34867555Smsmithtwe_intr(struct twe_softc *sc) 34960894Smsmith{ 35060894Smsmith u_int32_t status_reg; 35160894Smsmith 35260894Smsmith debug_called(4); 35360894Smsmith 35460894Smsmith /* 35560894Smsmith * Collect current interrupt status. 35660894Smsmith */ 35760894Smsmith status_reg = TWE_STATUS(sc); 35860894Smsmith twe_check_bits(sc, status_reg); 35960894Smsmith 36060894Smsmith /* 36160894Smsmith * Dispatch based on interrupt status 36260894Smsmith */ 36360894Smsmith if (status_reg & TWE_STATUS_HOST_INTERRUPT) 36460894Smsmith twe_host_intr(sc); 36560894Smsmith if (status_reg & TWE_STATUS_ATTENTION_INTERRUPT) 36660894Smsmith twe_attention_intr(sc); 36760894Smsmith if (status_reg & TWE_STATUS_COMMAND_INTERRUPT) 36860894Smsmith twe_command_intr(sc); 36973104Smsmith if (status_reg & TWE_STATUS_RESPONSE_INTERRUPT) 37060894Smsmith twe_done(sc); 37160894Smsmith}; 37260894Smsmith 37369543Smsmith/******************************************************************************** 37469543Smsmith * Pull as much work off the softc's work queue as possible and give it to the 37569543Smsmith * controller. 37660894Smsmith */ 37769543Smsmithvoid 37869543Smsmithtwe_startio(struct twe_softc *sc) 37960894Smsmith{ 38069543Smsmith struct twe_request *tr; 38169543Smsmith TWE_Command *cmd; 38269543Smsmith twe_bio *bp; 38369543Smsmith int error; 38469543Smsmith 38567555Smsmith debug_called(4); 38660894Smsmith 387118816Sps if (sc->twe_state & TWE_STATE_FRZN) 388118816Sps return; 389118816Sps 39069543Smsmith /* spin until something prevents us from doing any work */ 39169543Smsmith for (;;) { 39267555Smsmith 39369543Smsmith /* try to get a command that's already ready to go */ 39469543Smsmith tr = twe_dequeue_ready(sc); 39569543Smsmith 39669543Smsmith /* build a command from an outstanding bio */ 39769543Smsmith if (tr == NULL) { 39869543Smsmith 39969543Smsmith /* see if there's work to be done */ 40069543Smsmith if ((bp = twe_dequeue_bio(sc)) == NULL) 40169543Smsmith break; 40269543Smsmith 40369543Smsmith /* get a command to handle the bio with */ 40469543Smsmith if (twe_get_request(sc, &tr)) { 40569543Smsmith twe_enqueue_bio(sc, bp); /* failed, put the bio back */ 40669543Smsmith break; 40769543Smsmith } 40869543Smsmith 40969543Smsmith /* connect the bio to the command */ 41069543Smsmith tr->tr_complete = twe_completeio; 41169543Smsmith tr->tr_private = bp; 41269543Smsmith tr->tr_data = TWE_BIO_DATA(bp); 41369543Smsmith tr->tr_length = TWE_BIO_LENGTH(bp); 414118816Sps cmd = TWE_FIND_COMMAND(tr); 41569543Smsmith if (TWE_BIO_IS_READ(bp)) { 41669543Smsmith tr->tr_flags |= TWE_CMD_DATAIN; 41769543Smsmith cmd->io.opcode = TWE_OP_READ; 41869543Smsmith } else { 41969543Smsmith tr->tr_flags |= TWE_CMD_DATAOUT; 42069543Smsmith cmd->io.opcode = TWE_OP_WRITE; 42169543Smsmith } 42269543Smsmith 42369543Smsmith /* build a suitable I/O command (assumes 512-byte rounded transfers) */ 42469543Smsmith cmd->io.size = 3; 42569543Smsmith cmd->io.unit = TWE_BIO_UNIT(bp); 42669543Smsmith cmd->io.block_count = (tr->tr_length + TWE_BLOCK_SIZE - 1) / TWE_BLOCK_SIZE; 42769543Smsmith cmd->io.lba = TWE_BIO_LBA(bp); 42869543Smsmith } 42969543Smsmith 43069543Smsmith /* did we find something to do? */ 43169543Smsmith if (tr == NULL) 43269543Smsmith break; 43369543Smsmith 434118816Sps /* map the command so the controller can work with it */ 435118816Sps error = twe_map_request(tr); 43669543Smsmith if (error != 0) { 43769543Smsmith if (error == EBUSY) { 43869543Smsmith twe_requeue_ready(tr); /* try it again later */ 43969543Smsmith break; /* don't try anything more for now */ 44069543Smsmith } 441118816Sps 44269543Smsmith /* we don't support any other return from twe_start */ 443118816Sps twe_panic(sc, "twe_map_request returned nonsense"); 44469543Smsmith } 44569543Smsmith } 44660894Smsmith} 44760894Smsmith 44860894Smsmith/******************************************************************************** 44969543Smsmith * Write blocks from memory to disk, for system crash dumps. 45069543Smsmith */ 45169543Smsmithint 45269543Smsmithtwe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) 45369543Smsmith{ 45469543Smsmith struct twe_request *tr; 45569543Smsmith TWE_Command *cmd; 45669543Smsmith int error; 45769543Smsmith 45869543Smsmith if (twe_get_request(sc, &tr)) 45969543Smsmith return(ENOMEM); 46069543Smsmith 46169543Smsmith tr->tr_data = data; 46269543Smsmith tr->tr_status = TWE_CMD_SETUP; 46369543Smsmith tr->tr_length = nblks * TWE_BLOCK_SIZE; 46469543Smsmith tr->tr_flags = TWE_CMD_DATAOUT; 46569543Smsmith 466118816Sps cmd = TWE_FIND_COMMAND(tr); 46769543Smsmith cmd->io.opcode = TWE_OP_WRITE; 46869543Smsmith cmd->io.size = 3; 46969543Smsmith cmd->io.unit = unit; 47069543Smsmith cmd->io.block_count = nblks; 47169543Smsmith cmd->io.lba = lba; 47269543Smsmith 47369543Smsmith error = twe_immediate_request(tr); 47469543Smsmith if (error == 0) 47569543Smsmith if (twe_report_request(tr)) 47669543Smsmith error = EIO; 47769543Smsmith twe_release_request(tr); 47869543Smsmith return(error); 47969543Smsmith} 48069543Smsmith 48169543Smsmith/******************************************************************************** 48267555Smsmith * Handle controller-specific control operations. 48360894Smsmith */ 48467555Smsmithint 485118816Spstwe_ioctl(struct twe_softc *sc, int ioctlcmd, void *addr) 48660894Smsmith{ 48767555Smsmith struct twe_usercommand *tu = (struct twe_usercommand *)addr; 48867555Smsmith struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; 489118508Sps struct twe_drivecommand *td = (struct twe_drivecommand *)addr; 49067555Smsmith union twe_statrequest *ts = (union twe_statrequest *)addr; 49167555Smsmith TWE_Param *param; 492118816Sps TWE_Command *cmd; 49367555Smsmith void *data; 494123103Sps u_int16_t *aen_code = (u_int16_t *)addr; 49567555Smsmith struct twe_request *tr; 49669543Smsmith u_int8_t srid; 49767555Smsmith int s, error; 49860894Smsmith 49967555Smsmith error = 0; 500118816Sps switch(ioctlcmd) { 50167555Smsmith /* handle a command from userspace */ 50267555Smsmith case TWEIO_COMMAND: 50367555Smsmith /* get a request */ 504118508Sps while (twe_get_request(sc, &tr)) 505119124Sps tsleep(sc, PPAUSE, "twioctl", hz); 50667555Smsmith 50769543Smsmith /* 50869543Smsmith * Save the command's request ID, copy the user-supplied command in, 50969543Smsmith * restore the request ID. 51069543Smsmith */ 511118816Sps cmd = TWE_FIND_COMMAND(tr); 512118816Sps srid = cmd->generic.request_id; 513118816Sps bcopy(&tu->tu_command, cmd, sizeof(TWE_Command)); 514118816Sps cmd->generic.request_id = srid; 51567555Smsmith 516118508Sps /* 517118508Sps * if there's a data buffer, allocate and copy it in. 518118508Sps * Must be in multipled of 512 bytes. 519118508Sps */ 520118508Sps tr->tr_length = (tu->tu_size + 511) & ~511; 52167555Smsmith if (tr->tr_length > 0) { 522111119Simp if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { 52367555Smsmith error = ENOMEM; 52467555Smsmith goto cmd_done; 52567555Smsmith } 526118508Sps if ((error = copyin(tu->tu_data, tr->tr_data, tu->tu_size)) != 0) 52767555Smsmith goto cmd_done; 52867555Smsmith tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 52967555Smsmith } 53067555Smsmith 53167555Smsmith /* run the command */ 53267555Smsmith twe_wait_request(tr); 53367555Smsmith 53467555Smsmith /* copy the command out again */ 535118816Sps bcopy(cmd, &tu->tu_command, sizeof(TWE_Command)); 53667555Smsmith 53767555Smsmith /* if there was a data buffer, copy it out */ 53867555Smsmith if (tr->tr_length > 0) 539118508Sps error = copyout(tr->tr_data, tu->tu_data, tu->tu_size); 54067555Smsmith 54167555Smsmith cmd_done: 54267555Smsmith /* free resources */ 54367555Smsmith if (tr->tr_data != NULL) 54467555Smsmith free(tr->tr_data, M_DEVBUF); 54567555Smsmith if (tr != NULL) 54667555Smsmith twe_release_request(tr); 54767555Smsmith 54867555Smsmith break; 54967555Smsmith 55067555Smsmith /* fetch statistics counter */ 55167555Smsmith case TWEIO_STATS: 55267555Smsmith switch (ts->ts_item) { 55367555Smsmith#ifdef TWE_PERFORMANCE_MONITOR 55467555Smsmith case TWEQ_FREE: 55567555Smsmith case TWEQ_BIO: 55667555Smsmith case TWEQ_READY: 55767555Smsmith case TWEQ_BUSY: 55867555Smsmith case TWEQ_COMPLETE: 55967555Smsmith bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); 56067555Smsmith break; 56167555Smsmith#endif 56267555Smsmith default: 56367555Smsmith error = ENOENT; 56467555Smsmith break; 56567555Smsmith } 56667555Smsmith break; 56767555Smsmith 56867555Smsmith /* poll for an AEN */ 56967555Smsmith case TWEIO_AEN_POLL: 570123103Sps *aen_code = twe_dequeue_aen(sc); 57167555Smsmith break; 57267555Smsmith 57367555Smsmith /* wait for another AEN to show up */ 57467555Smsmith case TWEIO_AEN_WAIT: 57567555Smsmith s = splbio(); 576123103Sps while ((*aen_code = twe_dequeue_aen(sc)) == TWE_AEN_QUEUE_EMPTY) { 57767555Smsmith error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); 57867555Smsmith if (error == EINTR) 57967555Smsmith break; 58067555Smsmith } 58167555Smsmith splx(s); 58267555Smsmith break; 58367555Smsmith 58467555Smsmith case TWEIO_GET_PARAM: 58567555Smsmith if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { 58667555Smsmith twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", 58767555Smsmith tp->tp_table_id, tp->tp_param_id, tp->tp_size); 58867555Smsmith error = EINVAL; 58967555Smsmith } else { 59067555Smsmith if (param->parameter_size_bytes > tp->tp_size) { 59167555Smsmith twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", 59267555Smsmith param->parameter_size_bytes, tp->tp_size); 59367555Smsmith error = EFAULT; 59467555Smsmith } else { 59567555Smsmith error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); 59667555Smsmith } 59767555Smsmith free(param, M_DEVBUF); 59867555Smsmith } 59967555Smsmith break; 60067555Smsmith 60167555Smsmith case TWEIO_SET_PARAM: 602111119Simp if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { 60367555Smsmith error = ENOMEM; 60467555Smsmith } else { 60567555Smsmith error = copyin(tp->tp_data, data, tp->tp_size); 60667555Smsmith if (error == 0) 60767555Smsmith error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); 60867555Smsmith free(data, M_DEVBUF); 60967555Smsmith } 61067555Smsmith break; 61167555Smsmith 61267555Smsmith case TWEIO_RESET: 61367555Smsmith twe_reset(sc); 61467555Smsmith break; 61567555Smsmith 616118508Sps case TWEIO_ADD_UNIT: 617123103Sps error = twe_add_unit(sc, td->td_unit); 618118508Sps break; 619118508Sps 620118508Sps case TWEIO_DEL_UNIT: 621123103Sps error = twe_del_unit(sc, td->td_unit); 622118508Sps break; 623118508Sps 62491790Smsmith /* XXX implement ATA PASSTHROUGH */ 62591790Smsmith 62667555Smsmith /* nothing we understand */ 62767555Smsmith default: 62867555Smsmith error = ENOTTY; 62967555Smsmith } 63067555Smsmith 63167555Smsmith return(error); 63260894Smsmith} 63360894Smsmith 63460894Smsmith/******************************************************************************** 63567555Smsmith * Enable the useful interrupts from the controller. 63660894Smsmith */ 63767555Smsmithvoid 63867555Smsmithtwe_enable_interrupts(struct twe_softc *sc) 63960894Smsmith{ 64067555Smsmith sc->twe_state |= TWE_STATE_INTEN; 64167555Smsmith TWE_CONTROL(sc, 64267555Smsmith TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | 64367555Smsmith TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | 64467555Smsmith TWE_CONTROL_ENABLE_INTERRUPTS); 64560894Smsmith} 64660894Smsmith 64760894Smsmith/******************************************************************************** 64867555Smsmith * Disable interrupts from the controller. 64967555Smsmith */ 65067555Smsmithvoid 65167555Smsmithtwe_disable_interrupts(struct twe_softc *sc) 65267555Smsmith{ 65367555Smsmith TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); 65467555Smsmith sc->twe_state &= ~TWE_STATE_INTEN; 65567555Smsmith} 65667555Smsmith 65767555Smsmith/******************************************************************************** 65860894Smsmith ******************************************************************************** 65960894Smsmith Command Submission 66060894Smsmith ******************************************************************************** 66160894Smsmith ********************************************************************************/ 66260894Smsmith 66367555Smsmith/******************************************************************************** 66467555Smsmith * Read integer parameter table entries. 66560894Smsmith */ 66667555Smsmithstatic int 66767555Smsmithtwe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) 66860894Smsmith{ 66967555Smsmith TWE_Param *param; 67060894Smsmith 67167555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) 67267555Smsmith return(ENOENT); 67367555Smsmith *result = *(u_int8_t *)param->data; 67467555Smsmith free(param, M_DEVBUF); 67567555Smsmith return(0); 67667555Smsmith} 67760894Smsmith 67867555Smsmithstatic int 67967555Smsmithtwe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) 68067555Smsmith{ 68167555Smsmith TWE_Param *param; 68267555Smsmith 68367555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) 68467555Smsmith return(ENOENT); 68567555Smsmith *result = *(u_int16_t *)param->data; 68667555Smsmith free(param, M_DEVBUF); 68760894Smsmith return(0); 68860894Smsmith} 68960894Smsmith 69067555Smsmithstatic int 69167555Smsmithtwe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) 69267555Smsmith{ 69367555Smsmith TWE_Param *param; 69467555Smsmith 69567555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) 69667555Smsmith return(ENOENT); 69767555Smsmith *result = *(u_int32_t *)param->data; 69867555Smsmith free(param, M_DEVBUF); 69967555Smsmith return(0); 70067555Smsmith} 70167555Smsmith 70260894Smsmith/******************************************************************************** 70360894Smsmith * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it 70460894Smsmith * will be called with the command when it's completed. If no callback is 70560894Smsmith * provided, we will wait for the command to complete and then return just the data. 70660894Smsmith * The caller is responsible for freeing the data when done with it. 70760894Smsmith */ 70860894Smsmithstatic void * 70967555Smsmithtwe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, 71067555Smsmith void (* func)(struct twe_request *tr)) 71160894Smsmith{ 71260894Smsmith struct twe_request *tr; 71360894Smsmith TWE_Command *cmd; 71460894Smsmith TWE_Param *param; 71560894Smsmith int error; 71660894Smsmith 71760894Smsmith debug_called(4); 71860894Smsmith 71960894Smsmith tr = NULL; 72060894Smsmith param = NULL; 72160894Smsmith 72260894Smsmith /* get a command */ 72367555Smsmith if (twe_get_request(sc, &tr)) 72460894Smsmith goto err; 72560894Smsmith 72660894Smsmith /* get a buffer */ 72760894Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 72860894Smsmith goto err; 72960894Smsmith tr->tr_data = param; 73060894Smsmith tr->tr_length = TWE_SECTOR_SIZE; 73160894Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 73260894Smsmith 73360894Smsmith /* build the command for the controller */ 734118816Sps cmd = TWE_FIND_COMMAND(tr); 73567555Smsmith cmd->param.opcode = TWE_OP_GET_PARAM; 73667555Smsmith cmd->param.size = 2; 73767555Smsmith cmd->param.unit = 0; 73867555Smsmith cmd->param.param_count = 1; 73960894Smsmith 74060894Smsmith /* fill in the outbound parameter data */ 74160894Smsmith param->table_id = table_id; 74267555Smsmith param->parameter_id = param_id; 74367555Smsmith param->parameter_size_bytes = param_size; 74460894Smsmith 74560894Smsmith /* submit the command and either wait or let the callback handle it */ 74660894Smsmith if (func == NULL) { 74760894Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 74860894Smsmith error = twe_immediate_request(tr); 74960894Smsmith if (error == 0) { 75069543Smsmith if (twe_report_request(tr)) 75160894Smsmith goto err; 752123103Sps } else { 753123103Sps goto err; 75460894Smsmith } 75567555Smsmith twe_release_request(tr); 75667555Smsmith return(param); 75760894Smsmith } else { 75860894Smsmith tr->tr_complete = func; 759118816Sps error = twe_map_request(tr); 76060894Smsmith if (error == 0) 76160894Smsmith return(func); 76260894Smsmith } 76360894Smsmith 76460894Smsmith /* something failed */ 76560894Smsmitherr: 76660894Smsmith debug(1, "failed"); 76760894Smsmith if (tr != NULL) 76860894Smsmith twe_release_request(tr); 76960894Smsmith if (param != NULL) 77060894Smsmith free(param, M_DEVBUF); 77160894Smsmith return(NULL); 77260894Smsmith} 77360894Smsmith 77460894Smsmith/******************************************************************************** 77567555Smsmith * Set integer parameter table entries. 77667555Smsmith */ 77791449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 77867555Smsmithstatic int 77967555Smsmithtwe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) 78067555Smsmith{ 78167555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 78267555Smsmith} 78391449Speter#endif 78467555Smsmith 78591449Speter#if 0 78667555Smsmithstatic int 78767555Smsmithtwe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) 78867555Smsmith{ 78967555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 79067555Smsmith} 79167555Smsmith 79267555Smsmithstatic int 79367555Smsmithtwe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) 79467555Smsmith{ 79567555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 79667555Smsmith} 79791449Speter#endif 79867555Smsmith 79967555Smsmith/******************************************************************************** 80067555Smsmith * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. 80167555Smsmith */ 80267555Smsmithstatic int 80367555Smsmithtwe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) 80467555Smsmith{ 80567555Smsmith struct twe_request *tr; 80667555Smsmith TWE_Command *cmd; 80767555Smsmith TWE_Param *param; 80867555Smsmith int error; 80967555Smsmith 81067555Smsmith debug_called(4); 81167555Smsmith 81267555Smsmith tr = NULL; 81367555Smsmith param = NULL; 81467555Smsmith error = ENOMEM; 81567555Smsmith 81667555Smsmith /* get a command */ 81767555Smsmith if (twe_get_request(sc, &tr)) 81867555Smsmith goto out; 81967555Smsmith 82067555Smsmith /* get a buffer */ 82167555Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 82267555Smsmith goto out; 82367555Smsmith tr->tr_data = param; 82467555Smsmith tr->tr_length = TWE_SECTOR_SIZE; 82567555Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 82667555Smsmith 82767555Smsmith /* build the command for the controller */ 828118816Sps cmd = TWE_FIND_COMMAND(tr); 82967555Smsmith cmd->param.opcode = TWE_OP_SET_PARAM; 83067555Smsmith cmd->param.size = 2; 83167555Smsmith cmd->param.unit = 0; 83267555Smsmith cmd->param.param_count = 1; 83367555Smsmith 83467555Smsmith /* fill in the outbound parameter data */ 83567555Smsmith param->table_id = table_id; 83667555Smsmith param->parameter_id = param_id; 83767555Smsmith param->parameter_size_bytes = param_size; 83867555Smsmith bcopy(data, param->data, param_size); 83967555Smsmith 84067555Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 84167555Smsmith error = twe_immediate_request(tr); 84267555Smsmith if (error == 0) { 84369543Smsmith if (twe_report_request(tr)) 84467555Smsmith error = EIO; 84567555Smsmith } 84667555Smsmith 84767555Smsmithout: 84867555Smsmith if (tr != NULL) 84967555Smsmith twe_release_request(tr); 85067555Smsmith if (param != NULL) 85167555Smsmith free(param, M_DEVBUF); 85267555Smsmith return(error); 85367555Smsmith} 85467555Smsmith 85567555Smsmith/******************************************************************************** 85660894Smsmith * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. 85760894Smsmith * 85860894Smsmith * Typically called with interrupts disabled. 85960894Smsmith */ 86060894Smsmithstatic int 86167555Smsmithtwe_init_connection(struct twe_softc *sc, int mode) 86260894Smsmith{ 86360894Smsmith struct twe_request *tr; 86460894Smsmith TWE_Command *cmd; 86560894Smsmith int error; 86660894Smsmith 86760894Smsmith debug_called(4); 86860894Smsmith 86960894Smsmith /* get a command */ 87067555Smsmith if (twe_get_request(sc, &tr)) 871102291Sarchie return(0); 87260894Smsmith 87360894Smsmith /* build the command */ 874118816Sps cmd = TWE_FIND_COMMAND(tr); 87567555Smsmith cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; 87667555Smsmith cmd->initconnection.size = 3; 87767555Smsmith cmd->initconnection.host_id = 0; 87867555Smsmith cmd->initconnection.message_credits = mode; 87967555Smsmith cmd->initconnection.response_queue_pointer = 0; 88060894Smsmith 88160894Smsmith /* submit the command */ 88260894Smsmith error = twe_immediate_request(tr); 88360894Smsmith /* XXX check command result? */ 88460894Smsmith twe_unmap_request(tr); 88560894Smsmith twe_release_request(tr); 88660894Smsmith 88767555Smsmith if (mode == TWE_INIT_MESSAGE_CREDITS) 88867555Smsmith sc->twe_host_id = cmd->initconnection.host_id; 88960894Smsmith return(error); 89060894Smsmith} 89160894Smsmith 89260894Smsmith/******************************************************************************** 89360894Smsmith * Start the command (tr) and sleep waiting for it to complete. 89460894Smsmith * 89560894Smsmith * Successfully completed commands are dequeued. 89660894Smsmith */ 89760894Smsmithstatic int 89860894Smsmithtwe_wait_request(struct twe_request *tr) 89960894Smsmith{ 90067555Smsmith int s; 90160894Smsmith 90260894Smsmith debug_called(4); 90360894Smsmith 90467555Smsmith tr->tr_flags |= TWE_CMD_SLEEPER; 90567555Smsmith tr->tr_status = TWE_CMD_BUSY; 90667555Smsmith twe_enqueue_ready(tr); 90767555Smsmith twe_startio(tr->tr_sc); 90860894Smsmith s = splbio(); 90967555Smsmith while (tr->tr_status == TWE_CMD_BUSY) 91067555Smsmith tsleep(tr, PRIBIO, "twewait", 0); 91160894Smsmith splx(s); 91267555Smsmith 91367555Smsmith return(0); 91460894Smsmith} 91560894Smsmith 91660894Smsmith/******************************************************************************** 91760894Smsmith * Start the command (tr) and busy-wait for it to complete. 91860894Smsmith * This should only be used when interrupts are actually disabled (although it 91960894Smsmith * will work if they are not). 92060894Smsmith */ 92160894Smsmithstatic int 92260894Smsmithtwe_immediate_request(struct twe_request *tr) 92360894Smsmith{ 92460894Smsmith 92560894Smsmith debug_called(4); 92660894Smsmith 927118816Sps tr->tr_flags |= TWE_CMD_IMMEDIATE; 928118816Sps tr->tr_status = TWE_CMD_BUSY; 929118816Sps twe_map_request(tr); 93060894Smsmith 93160894Smsmith while (tr->tr_status == TWE_CMD_BUSY){ 93260894Smsmith twe_done(tr->tr_sc); 93360894Smsmith } 93460894Smsmith return(tr->tr_status != TWE_CMD_COMPLETE); 93560894Smsmith} 93660894Smsmith 93760894Smsmith/******************************************************************************** 93860894Smsmith * Handle completion of an I/O command. 93960894Smsmith */ 94060894Smsmithstatic void 94160894Smsmithtwe_completeio(struct twe_request *tr) 94260894Smsmith{ 943123103Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 94460894Smsmith struct twe_softc *sc = tr->tr_sc; 94567555Smsmith twe_bio *bp = (twe_bio *)tr->tr_private; 94660894Smsmith 94760894Smsmith debug_called(4); 94860894Smsmith 94960894Smsmith if (tr->tr_status == TWE_CMD_COMPLETE) { 95067555Smsmith 951123103Sps if (cmd->generic.status) 952123103Sps if (twe_report_request(tr)) 953123103Sps TWE_BIO_SET_ERROR(bp, EIO); 95467555Smsmith 95569543Smsmith } else { 95669543Smsmith twe_panic(sc, "twe_completeio on incomplete command"); 95760894Smsmith } 95869543Smsmith tr->tr_private = NULL; 95969543Smsmith twed_intr(bp); 96060894Smsmith twe_release_request(tr); 96160894Smsmith} 96260894Smsmith 96360894Smsmith/******************************************************************************** 96467555Smsmith * Reset the controller and pull all the active commands back onto the ready 96567555Smsmith * queue. Used to restart a controller that's exhibiting bad behaviour. 96667555Smsmith */ 96767555Smsmithstatic void 96867555Smsmithtwe_reset(struct twe_softc *sc) 96967555Smsmith{ 97067555Smsmith struct twe_request *tr; 97167555Smsmith int i, s; 97267555Smsmith 97391790Smsmith /* 97491790Smsmith * Sleep for a short period to allow AENs to be signalled. 97591790Smsmith */ 976119124Sps tsleep(sc, PRIBIO, "twereset", hz); 97767555Smsmith 97867555Smsmith /* 97967555Smsmith * Disable interrupts from the controller, and mask any accidental entry 98067555Smsmith * into our interrupt handler. 98167555Smsmith */ 98291790Smsmith twe_printf(sc, "controller reset in progress...\n"); 98367555Smsmith twe_disable_interrupts(sc); 98467555Smsmith s = splbio(); 98567555Smsmith 98667555Smsmith /* 98767555Smsmith * Try to soft-reset the controller. 98867555Smsmith */ 98967555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 99067555Smsmith 99167555Smsmith if (i > 0) 99267555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 99367555Smsmith 99467555Smsmith if (!twe_soft_reset(sc)) 99567555Smsmith break; /* reset process complete */ 99667555Smsmith } 99767555Smsmith /* did we give up? */ 99867555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 99967555Smsmith twe_printf(sc, "can't reset controller, giving up\n"); 100067555Smsmith goto out; 100167555Smsmith } 100267555Smsmith 100367555Smsmith /* 100467555Smsmith * Move all of the commands that were busy back to the ready queue. 100567555Smsmith */ 100667555Smsmith i = 0; 100767555Smsmith while ((tr = twe_dequeue_busy(sc)) != NULL) { 100867555Smsmith twe_enqueue_ready(tr); 100967555Smsmith i++; 101067555Smsmith } 101167555Smsmith 101267555Smsmith /* 101367555Smsmith * Kick the controller to start things going again, then re-enable interrupts. 101467555Smsmith */ 101567555Smsmith twe_startio(sc); 101667555Smsmith twe_enable_interrupts(sc); 101767555Smsmith twe_printf(sc, "controller reset done, %d commands restarted\n", i); 101867555Smsmith 101967555Smsmithout: 102067555Smsmith splx(s); 102167555Smsmith twe_enable_interrupts(sc); 102267555Smsmith} 102367555Smsmith 102467555Smsmith/******************************************************************************** 102560894Smsmith ******************************************************************************** 102660894Smsmith Command I/O to Controller 102760894Smsmith ******************************************************************************** 102860894Smsmith ********************************************************************************/ 102960894Smsmith 103060894Smsmith/******************************************************************************** 103160894Smsmith * Try to deliver (tr) to the controller. 103260894Smsmith * 103360894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 103460894Smsmith */ 1035118816Spsint 103660894Smsmithtwe_start(struct twe_request *tr) 103760894Smsmith{ 103860894Smsmith struct twe_softc *sc = tr->tr_sc; 1039118816Sps TWE_Command *cmd; 104060894Smsmith int i, s, done; 104160894Smsmith u_int32_t status_reg; 104260894Smsmith 104360894Smsmith debug_called(4); 104460894Smsmith 104560894Smsmith /* mark the command as currently being processed */ 104660894Smsmith tr->tr_status = TWE_CMD_BUSY; 1047118816Sps cmd = TWE_FIND_COMMAND(tr); 104860894Smsmith 104967555Smsmith /* 105067555Smsmith * Spin briefly waiting for the controller to come ready 105167555Smsmith * 105267555Smsmith * XXX it might be more efficient to return EBUSY immediately 105367555Smsmith * and let the command be rescheduled. 105467555Smsmith */ 105560894Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 105660894Smsmith s = splbio(); 105760894Smsmith 105860894Smsmith /* check to see if we can post a command */ 105960894Smsmith status_reg = TWE_STATUS(sc); 106060894Smsmith twe_check_bits(sc, status_reg); 106160894Smsmith 106260894Smsmith if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { 1063118816Sps twe_enqueue_busy(tr); 1064118816Sps 1065118816Sps TWE_COMMAND_QUEUE(sc, TWE_FIND_COMMANDPHYS(tr)); 106660894Smsmith done = 1; 106760894Smsmith /* move command to work queue */ 106867555Smsmith#ifdef TWE_DEBUG 106960894Smsmith if (tr->tr_complete != NULL) { 1070118816Sps debug(3, "queued request %d with callback %p", cmd->generic.request_id, tr->tr_complete); 107167555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { 1072118816Sps debug(3, "queued request %d with wait channel %p", cmd->generic.request_id, tr); 107360894Smsmith } else { 1074118816Sps debug(3, "queued request %d for polling caller", cmd->generic.request_id); 107560894Smsmith } 107667555Smsmith#endif 107760894Smsmith } 107860894Smsmith splx(s); /* drop spl to allow completion interrupts */ 107960894Smsmith } 108060894Smsmith 108160894Smsmith /* command is enqueued */ 108260894Smsmith if (done) 108360894Smsmith return(0); 108460894Smsmith 108560894Smsmith /* 108660894Smsmith * We couldn't get the controller to take the command; try submitting it again later. 108760894Smsmith * This should only happen if something is wrong with the controller, or if we have 108860894Smsmith * overestimated the number of commands it can accept. (Should we actually reject 108960894Smsmith * the command at this point?) 109060894Smsmith */ 109160894Smsmith return(EBUSY); 109260894Smsmith} 109360894Smsmith 109460894Smsmith/******************************************************************************** 109560894Smsmith * Poll the controller (sc) for completed commands. 109660894Smsmith * 109760894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 109860894Smsmith */ 109960894Smsmithstatic void 110060894Smsmithtwe_done(struct twe_softc *sc) 110160894Smsmith{ 110260894Smsmith TWE_Response_Queue rq; 1103118816Sps TWE_Command *cmd; 110460894Smsmith struct twe_request *tr; 110560894Smsmith int s, found; 110660894Smsmith u_int32_t status_reg; 110760894Smsmith 110860894Smsmith debug_called(5); 110960894Smsmith 111060894Smsmith /* loop collecting completed commands */ 111160894Smsmith found = 0; 111260894Smsmith s = splbio(); 111360894Smsmith for (;;) { 111460894Smsmith status_reg = TWE_STATUS(sc); 111560894Smsmith twe_check_bits(sc, status_reg); /* XXX should this fail? */ 111660894Smsmith 111760894Smsmith if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { 111860894Smsmith found = 1; 111960894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 112067555Smsmith tr = sc->twe_lookup[rq.u.response_id]; /* find command */ 1121118816Sps cmd = TWE_FIND_COMMAND(tr); 112269543Smsmith if (tr->tr_status != TWE_CMD_BUSY) 112369543Smsmith twe_printf(sc, "completion event for nonbusy command\n"); 112469543Smsmith tr->tr_status = TWE_CMD_COMPLETE; 112569543Smsmith debug(3, "completed request id %d with status %d", 1126118816Sps cmd->generic.request_id, cmd->generic.status); 112769543Smsmith /* move to completed queue */ 112869543Smsmith twe_remove_busy(tr); 112969543Smsmith twe_enqueue_complete(tr); 113060894Smsmith } else { 113160894Smsmith break; /* no response ready */ 113260894Smsmith } 113360894Smsmith } 113460894Smsmith splx(s); 113560894Smsmith 113660894Smsmith /* if we've completed any commands, try posting some more */ 113760894Smsmith if (found) 113860894Smsmith twe_startio(sc); 113960894Smsmith 114060894Smsmith /* handle completion and timeouts */ 114167555Smsmith twe_complete(sc); /* XXX use deferred completion? */ 114260894Smsmith} 114360894Smsmith 114460894Smsmith/******************************************************************************** 114560894Smsmith * Perform post-completion processing for commands on (sc). 114660894Smsmith * 114760894Smsmith * This is split from twe_done as it can be safely deferred and run at a lower 114860894Smsmith * priority level should facilities for such a thing become available. 114960894Smsmith */ 115060894Smsmithstatic void 115160894Smsmithtwe_complete(struct twe_softc *sc) 115260894Smsmith{ 115367555Smsmith struct twe_request *tr; 115460894Smsmith 115560894Smsmith debug_called(5); 115660894Smsmith 115760894Smsmith /* 115867555Smsmith * Pull commands off the completed list, dispatch them appropriately 115960894Smsmith */ 116067555Smsmith while ((tr = twe_dequeue_complete(sc)) != NULL) { 116167555Smsmith /* unmap the command's data buffer */ 116267555Smsmith twe_unmap_request(tr); 116360894Smsmith 116467555Smsmith /* dispatch to suit command originator */ 116567555Smsmith if (tr->tr_complete != NULL) { /* completion callback */ 116667555Smsmith debug(2, "call completion handler %p", tr->tr_complete); 116767555Smsmith tr->tr_complete(tr); 116860894Smsmith 116967555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ 117067555Smsmith debug(2, "wake up command owner on %p", tr); 117167555Smsmith wakeup_one(tr); 117260894Smsmith 117367555Smsmith } else { /* caller is polling command */ 117467555Smsmith debug(2, "command left for owner"); 117560894Smsmith } 117667555Smsmith } 1177118816Sps 1178118816Sps sc->twe_state &= ~TWE_STATE_FRZN; 117960894Smsmith} 118060894Smsmith 118160894Smsmith/******************************************************************************** 118260894Smsmith * Wait for (status) to be set in the controller status register for up to 118360894Smsmith * (timeout) seconds. Returns 0 if found, nonzero if we time out. 118460894Smsmith * 118560894Smsmith * Note: this busy-waits, rather than sleeping, since we may be called with 118660894Smsmith * eg. clock interrupts masked. 118760894Smsmith */ 118860894Smsmithstatic int 118960894Smsmithtwe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) 119060894Smsmith{ 119160894Smsmith time_t expiry; 119260894Smsmith u_int32_t status_reg; 119360894Smsmith 119460894Smsmith debug_called(4); 119560894Smsmith 119660894Smsmith expiry = time_second + timeout; 119760894Smsmith 119860894Smsmith do { 119960894Smsmith status_reg = TWE_STATUS(sc); 120060894Smsmith if (status_reg & status) /* got the required bit(s)? */ 120160894Smsmith return(0); 120260894Smsmith DELAY(100000); 120360894Smsmith } while (time_second <= expiry); 120460894Smsmith 120560894Smsmith return(1); 120660894Smsmith} 120760894Smsmith 120860894Smsmith/******************************************************************************** 120960894Smsmith * Drain the response queue, which may contain responses to commands we know 121060894Smsmith * nothing about. 121160894Smsmith */ 121260894Smsmithstatic int 121360894Smsmithtwe_drain_response_queue(struct twe_softc *sc) 121460894Smsmith{ 121560894Smsmith TWE_Response_Queue rq; 121660894Smsmith u_int32_t status_reg; 121760894Smsmith 121860894Smsmith debug_called(4); 121960894Smsmith 122060894Smsmith for (;;) { /* XXX give up eventually? */ 122160894Smsmith status_reg = TWE_STATUS(sc); 122260894Smsmith if (twe_check_bits(sc, status_reg)) 122360894Smsmith return(1); 122460894Smsmith if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) 122560894Smsmith return(0); 122660894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 122760894Smsmith } 122860894Smsmith} 122960894Smsmith 123060894Smsmith/******************************************************************************** 123167555Smsmith * Soft-reset the controller 123267555Smsmith */ 123367555Smsmithstatic int 123467555Smsmithtwe_soft_reset(struct twe_softc *sc) 123567555Smsmith{ 123667555Smsmith u_int32_t status_reg; 123767555Smsmith 123867555Smsmith debug_called(2); 123967555Smsmith 124067555Smsmith TWE_SOFT_RESET(sc); 124167555Smsmith 124291790Smsmith if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) { 124367683Smsmith twe_printf(sc, "no attention interrupt\n"); 124467555Smsmith return(1); 124567555Smsmith } 124691790Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 124767555Smsmith if (twe_drain_aen_queue(sc)) { 124867555Smsmith twe_printf(sc, "can't drain AEN queue\n"); 124967555Smsmith return(1); 125067555Smsmith } 125167555Smsmith if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { 125267555Smsmith twe_printf(sc, "reset not reported\n"); 125367555Smsmith return(1); 125467555Smsmith } 125567555Smsmith status_reg = TWE_STATUS(sc); 125667555Smsmith if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { 125767555Smsmith twe_printf(sc, "controller errors detected\n"); 125867555Smsmith return(1); 125967555Smsmith } 126067555Smsmith if (twe_drain_response_queue(sc)) { 126167555Smsmith twe_printf(sc, "can't drain response queue\n"); 126267555Smsmith return(1); 126367555Smsmith } 126467555Smsmith return(0); 126567555Smsmith} 126667555Smsmith 126767555Smsmith/******************************************************************************** 126860894Smsmith ******************************************************************************** 126960894Smsmith Interrupt Handling 127060894Smsmith ******************************************************************************** 127160894Smsmith ********************************************************************************/ 127260894Smsmith 127360894Smsmith/******************************************************************************** 127460894Smsmith * Host interrupt. 127560894Smsmith * 127660894Smsmith * XXX what does this mean? 127760894Smsmith */ 127860894Smsmithstatic void 127960894Smsmithtwe_host_intr(struct twe_softc *sc) 128060894Smsmith{ 128160894Smsmith debug_called(4); 128260894Smsmith 128367555Smsmith twe_printf(sc, "host interrupt\n"); 128460894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); 128560894Smsmith} 128660894Smsmith 128760894Smsmith/******************************************************************************** 128860894Smsmith * Attention interrupt. 128960894Smsmith * 129060894Smsmith * Signalled when the controller has one or more AENs for us. 129160894Smsmith */ 129260894Smsmithstatic void 129360894Smsmithtwe_attention_intr(struct twe_softc *sc) 129460894Smsmith{ 129560894Smsmith debug_called(4); 129660894Smsmith 129760894Smsmith /* instigate a poll for AENs */ 129860894Smsmith if (twe_fetch_aen(sc)) { 129967555Smsmith twe_printf(sc, "error polling for signalled AEN\n"); 130060894Smsmith } else { 130160894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 130260894Smsmith } 130360894Smsmith} 130460894Smsmith 130560894Smsmith/******************************************************************************** 130660894Smsmith * Command interrupt. 130760894Smsmith * 130860894Smsmith * Signalled when the controller can handle more commands. 130960894Smsmith */ 131060894Smsmithstatic void 131160894Smsmithtwe_command_intr(struct twe_softc *sc) 131260894Smsmith{ 131360894Smsmith debug_called(4); 131460894Smsmith 131560894Smsmith /* 131660894Smsmith * We don't use this, rather we try to submit commands when we receive 131760894Smsmith * them, and when other commands have completed. Mask it so we don't get 131860894Smsmith * another one. 131960894Smsmith */ 132060894Smsmith TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); 132160894Smsmith} 132260894Smsmith 132360894Smsmith/******************************************************************************** 132460894Smsmith ******************************************************************************** 132560894Smsmith Asynchronous Event Handling 132660894Smsmith ******************************************************************************** 132760894Smsmith ********************************************************************************/ 132860894Smsmith 132960894Smsmith/******************************************************************************** 133060894Smsmith * Request an AEN from the controller. 133160894Smsmith */ 133260894Smsmithstatic int 133360894Smsmithtwe_fetch_aen(struct twe_softc *sc) 133460894Smsmith{ 133560894Smsmith 133660894Smsmith debug_called(4); 133760894Smsmith 133867555Smsmith if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) 133960894Smsmith return(EIO); 134060894Smsmith return(0); 134160894Smsmith} 134260894Smsmith 134360894Smsmith/******************************************************************************** 134460894Smsmith * Handle an AEN returned by the controller. 134560894Smsmith */ 134660894Smsmithstatic void 134760894Smsmithtwe_handle_aen(struct twe_request *tr) 134860894Smsmith{ 134960894Smsmith struct twe_softc *sc = tr->tr_sc; 135060894Smsmith TWE_Param *param; 135160894Smsmith u_int16_t aen; 135260894Smsmith 135360894Smsmith debug_called(4); 135460894Smsmith 135560894Smsmith /* XXX check for command success somehow? */ 135660894Smsmith 135760894Smsmith param = (TWE_Param *)tr->tr_data; 135860894Smsmith aen = *(u_int16_t *)(param->data); 135960894Smsmith 136060894Smsmith free(tr->tr_data, M_DEVBUF); 136160894Smsmith twe_release_request(tr); 136260894Smsmith twe_enqueue_aen(sc, aen); 136360894Smsmith 136460894Smsmith /* XXX poll for more AENs? */ 136560894Smsmith} 136660894Smsmith 136760894Smsmith/******************************************************************************** 136860894Smsmith * Pull AENs out of the controller and park them in the queue, in a context where 136960894Smsmith * interrupts aren't active. Return nonzero if we encounter any errors in the 137060894Smsmith * process of obtaining all the available AENs. 137160894Smsmith */ 137260894Smsmithstatic int 137360894Smsmithtwe_drain_aen_queue(struct twe_softc *sc) 137460894Smsmith{ 137560894Smsmith u_int16_t aen; 137660894Smsmith 137760894Smsmith for (;;) { 137867555Smsmith if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) 137960894Smsmith return(1); 138060894Smsmith if (aen == TWE_AEN_QUEUE_EMPTY) 138160894Smsmith return(0); 138260894Smsmith twe_enqueue_aen(sc, aen); 138360894Smsmith } 138460894Smsmith} 138560894Smsmith 138660894Smsmith/******************************************************************************** 138760894Smsmith * Push an AEN that we've received onto the queue. 138860894Smsmith * 138960894Smsmith * Note that we have to lock this against reentrance, since it may be called 139060894Smsmith * from both interrupt and non-interrupt context. 139160894Smsmith * 139260894Smsmith * If someone is waiting for the AEN we have, wake them up. 139360894Smsmith */ 139460894Smsmithstatic void 139560894Smsmithtwe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) 139660894Smsmith{ 139767555Smsmith char *msg; 139867555Smsmith int s, next, nextnext; 139960894Smsmith 140060894Smsmith debug_called(4); 140160894Smsmith 140267555Smsmith if ((msg = twe_format_aen(sc, aen)) != NULL) 140367555Smsmith twe_printf(sc, "AEN: <%s>\n", msg); 140460894Smsmith 140560894Smsmith s = splbio(); 140660894Smsmith /* enqueue the AEN */ 140760894Smsmith next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); 140867555Smsmith nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); 140967555Smsmith 141067555Smsmith /* check to see if this is the last free slot, and subvert the AEN if it is */ 141167555Smsmith if (nextnext == sc->twe_aen_tail) 141267555Smsmith aen = TWE_AEN_QUEUE_FULL; 141367555Smsmith 141467555Smsmith /* look to see if there's room for this AEN */ 141560894Smsmith if (next != sc->twe_aen_tail) { 141660894Smsmith sc->twe_aen_queue[sc->twe_aen_head] = aen; 141760894Smsmith sc->twe_aen_head = next; 141860894Smsmith } 141960894Smsmith 142067555Smsmith /* wake up anyone asleep on the queue */ 142167555Smsmith wakeup(&sc->twe_aen_queue); 142267555Smsmith 142360894Smsmith /* anyone looking for this AEN? */ 142460894Smsmith if (sc->twe_wait_aen == aen) { 142560894Smsmith sc->twe_wait_aen = -1; 142660894Smsmith wakeup(&sc->twe_wait_aen); 142760894Smsmith } 142860894Smsmith splx(s); 142960894Smsmith} 143060894Smsmith 143160894Smsmith/******************************************************************************** 143260894Smsmith * Pop an AEN off the queue, or return -1 if there are none left. 143360894Smsmith * 143460894Smsmith * We are more or less interrupt-safe, so don't block interrupts. 143560894Smsmith */ 1436123103Spsstatic u_int16_t 143760894Smsmithtwe_dequeue_aen(struct twe_softc *sc) 143860894Smsmith{ 1439123103Sps u_int16_t result; 144060894Smsmith 144160894Smsmith debug_called(4); 144260894Smsmith 144360894Smsmith if (sc->twe_aen_tail == sc->twe_aen_head) { 1444118508Sps result = TWE_AEN_QUEUE_EMPTY; 144560894Smsmith } else { 144660894Smsmith result = sc->twe_aen_queue[sc->twe_aen_tail]; 144760894Smsmith sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); 144860894Smsmith } 144960894Smsmith return(result); 145060894Smsmith} 145160894Smsmith 145260894Smsmith/******************************************************************************** 145360894Smsmith * Check to see if the requested AEN is in the queue. 145460894Smsmith * 145560894Smsmith * XXX we could probably avoid masking interrupts here 145660894Smsmith */ 145760894Smsmithstatic int 145860894Smsmithtwe_find_aen(struct twe_softc *sc, u_int16_t aen) 145960894Smsmith{ 146060894Smsmith int i, s, missing; 146160894Smsmith 146260894Smsmith missing = 1; 146360894Smsmith s = splbio(); 146460894Smsmith for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { 146560894Smsmith if (sc->twe_aen_queue[i] == aen) 146660894Smsmith missing = 0; 146760894Smsmith } 146867555Smsmith splx(s); 146960894Smsmith return(missing); 147060894Smsmith} 147160894Smsmith 147260894Smsmith 147360894Smsmith#if 0 /* currently unused */ 147460894Smsmith/******************************************************************************** 147560894Smsmith * Sleep waiting for at least (timeout) seconds until we see (aen) as 147660894Smsmith * requested. Returns nonzero on timeout or failure. 147760894Smsmith * 147860894Smsmith * XXX: this should not be used in cases where there may be more than one sleeper 147960894Smsmith * without a mechanism for registering multiple sleepers. 148060894Smsmith */ 148160894Smsmithstatic int 148260894Smsmithtwe_wait_aen(struct twe_softc *sc, int aen, int timeout) 148360894Smsmith{ 148460894Smsmith time_t expiry; 148560894Smsmith int found, s; 148660894Smsmith 148760894Smsmith debug_called(4); 148860894Smsmith 148960894Smsmith expiry = time_second + timeout; 149060894Smsmith found = 0; 149160894Smsmith 149260894Smsmith s = splbio(); 149360894Smsmith sc->twe_wait_aen = aen; 149460894Smsmith do { 149560894Smsmith twe_fetch_aen(sc); 149660894Smsmith tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); 149760894Smsmith if (sc->twe_wait_aen == -1) 149860894Smsmith found = 1; 149960894Smsmith } while ((time_second <= expiry) && !found); 150060894Smsmith splx(s); 150160894Smsmith return(!found); 150260894Smsmith} 150360894Smsmith#endif 150460894Smsmith 150560894Smsmith/******************************************************************************** 150660894Smsmith ******************************************************************************** 150760894Smsmith Command Buffer Management 150860894Smsmith ******************************************************************************** 150960894Smsmith ********************************************************************************/ 151060894Smsmith 151160894Smsmith/******************************************************************************** 151260894Smsmith * Get a new command buffer. 151360894Smsmith * 151467555Smsmith * This will return NULL if all command buffers are in use. 151560894Smsmith */ 151667555Smsmithstatic int 151767555Smsmithtwe_get_request(struct twe_softc *sc, struct twe_request **tr) 151860894Smsmith{ 1519118816Sps TWE_Command *cmd; 152060894Smsmith debug_called(4); 152160894Smsmith 152260894Smsmith /* try to reuse an old buffer */ 152367555Smsmith *tr = twe_dequeue_free(sc); 152460894Smsmith 152567555Smsmith /* initialise some fields to their defaults */ 152667555Smsmith if (*tr != NULL) { 1527118816Sps cmd = TWE_FIND_COMMAND(*tr); 152867555Smsmith (*tr)->tr_data = NULL; 152969543Smsmith (*tr)->tr_private = NULL; 153067555Smsmith (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ 153167555Smsmith (*tr)->tr_flags = 0; 153267555Smsmith (*tr)->tr_complete = NULL; 1533118816Sps cmd->generic.status = 0; /* before submission to controller */ 1534118816Sps cmd->generic.flags = 0; /* not used */ 153560894Smsmith } 153667555Smsmith return(*tr == NULL); 153760894Smsmith} 153860894Smsmith 153960894Smsmith/******************************************************************************** 154067555Smsmith * Release a command buffer for reuse. 154160894Smsmith * 154260894Smsmith */ 154360894Smsmithstatic void 154460894Smsmithtwe_release_request(struct twe_request *tr) 154560894Smsmith{ 154660894Smsmith debug_called(4); 154760894Smsmith 154869543Smsmith if (tr->tr_private != NULL) 154969543Smsmith twe_panic(tr->tr_sc, "tr_private != NULL"); 155067555Smsmith twe_enqueue_free(tr); 155160894Smsmith} 155260894Smsmith 155360894Smsmith/******************************************************************************** 155467555Smsmith ******************************************************************************** 155567555Smsmith Debugging 155667555Smsmith ******************************************************************************** 155767555Smsmith ********************************************************************************/ 155860894Smsmith 155960894Smsmith/******************************************************************************** 156067555Smsmith * Print some information about the controller 156160894Smsmith */ 156267555Smsmithvoid 156367555Smsmithtwe_describe_controller(struct twe_softc *sc) 156460894Smsmith{ 156567555Smsmith TWE_Param *p[6]; 156667555Smsmith u_int8_t ports; 156767555Smsmith u_int32_t size; 156860894Smsmith int i; 156960894Smsmith 157067555Smsmith debug_called(2); 157160894Smsmith 157267555Smsmith /* get the port count */ 157367555Smsmith twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); 157460894Smsmith 157567555Smsmith /* get version strings */ 1576123103Sps p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); 1577123103Sps p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); 1578123103Sps if (p[0] && p[1]) 1579123103Sps twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[0]->data, p[1]->data); 158060894Smsmith 1581123103Sps if (bootverbose) { 1582123103Sps p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); 1583123103Sps p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); 1584123103Sps p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); 1585123103Sps p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); 158660894Smsmith 1587123103Sps if (p[2] && p[3] && p[4] && p[5]) 1588123103Sps twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[2]->data, p[3]->data, 1589123103Sps p[4]->data, p[5]->data); 1590123103Sps if (p[2]) 1591123103Sps free(p[2], M_DEVBUF); 1592123103Sps if (p[3]) 1593123103Sps free(p[3], M_DEVBUF); 1594123103Sps if (p[4]) 1595123103Sps free(p[4], M_DEVBUF); 1596123103Sps if (p[5]) 1597123103Sps free(p[5], M_DEVBUF); 1598123103Sps } 1599123103Sps if (p[0]) 1600123103Sps free(p[0], M_DEVBUF); 1601123103Sps if (p[1]) 1602123103Sps free(p[1], M_DEVBUF); 1603123103Sps 160467555Smsmith /* print attached drives */ 160567555Smsmith if (bootverbose) { 160667555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); 160767555Smsmith for (i = 0; i < ports; i++) { 160867555Smsmith if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) 160967555Smsmith continue; 161067555Smsmith twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); 161167555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); 161267555Smsmith if (p[1] != NULL) { 161367555Smsmith twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); 161467555Smsmith free(p[1], M_DEVBUF); 161567555Smsmith } else { 161667555Smsmith twe_printf(sc, "port %d, drive status unavailable\n", i); 161767555Smsmith } 161860894Smsmith } 1619123103Sps if (p[0]) 1620123103Sps free(p[0], M_DEVBUF); 162160894Smsmith } 162260894Smsmith} 162360894Smsmith 162460894Smsmith/******************************************************************************** 1625123103Sps * Look up a text description of a numeric code and return a pointer to same. 1626123103Sps */ 1627123103Spschar * 1628123103Spstwe_describe_code(struct twe_code_lookup *table, u_int32_t code) 1629123103Sps{ 1630123103Sps int i; 1631123103Sps 1632123103Sps for (i = 0; table[i].string != NULL; i++) 1633123103Sps if (table[i].code == code) 1634123103Sps return(table[i].string); 1635123103Sps return(table[i+1].string); 1636123103Sps} 1637123103Sps 1638123103Sps/******************************************************************************** 163960894Smsmith * Complain if the status bits aren't what we're expecting. 164067555Smsmith * 164167555Smsmith * Rate-limit the complaints to at most one of each every five seconds, but 164267555Smsmith * always return the correct status. 164360894Smsmith */ 164460894Smsmithstatic int 164560894Smsmithtwe_check_bits(struct twe_softc *sc, u_int32_t status_reg) 164660894Smsmith{ 164767555Smsmith int result; 164867555Smsmith static time_t lastwarn[2] = {0, 0}; 164960894Smsmith 165067555Smsmith /* 165167555Smsmith * This can be a little problematic, as twe_panic may call twe_reset if 165267555Smsmith * TWE_DEBUG is not set, which will call us again as part of the soft reset. 165367555Smsmith */ 165467555Smsmith if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { 165567555Smsmith twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, 165667555Smsmith TWE_STATUS_BITS_DESCRIPTION); 165767555Smsmith twe_panic(sc, "fatal status bits"); 165867555Smsmith } 165967555Smsmith 166060894Smsmith result = 0; 166160894Smsmith if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { 166267555Smsmith if (time_second > (lastwarn[0] + 5)) { 166367555Smsmith twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, 166467555Smsmith TWE_STATUS_BITS_DESCRIPTION); 166567555Smsmith lastwarn[0] = time_second; 166667555Smsmith } 166760894Smsmith result = 1; 166860894Smsmith } 166960894Smsmith 167060894Smsmith if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { 167167555Smsmith if (time_second > (lastwarn[1] + 5)) { 167267555Smsmith twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, 167367555Smsmith TWE_STATUS_BITS_DESCRIPTION); 167467555Smsmith lastwarn[1] = time_second; 167567555Smsmith } 167660894Smsmith result = 1; 167791790Smsmith if (status_reg & TWE_STATUS_PCI_PARITY_ERROR) { 167891790Smsmith twe_printf(sc, "PCI parity error: Reseat card, move card or buggy device present."); 167991790Smsmith twe_clear_pci_parity_error(sc); 168091790Smsmith } 168191790Smsmith if (status_reg & TWE_STATUS_PCI_ABORT) { 1682118816Sps twe_printf(sc, "PCI abort, clearing.\n"); 168391790Smsmith twe_clear_pci_abort(sc); 168491790Smsmith } 168560894Smsmith } 168667555Smsmith 168760894Smsmith return(result); 168860894Smsmith} 168960894Smsmith 169060894Smsmith/******************************************************************************** 169167555Smsmith * Return a string describing (aen). 169267555Smsmith * 169367555Smsmith * The low 8 bits of the aen are the code, the high 8 bits give the unit number 169467555Smsmith * where an AEN is specific to a unit. 169567555Smsmith * 169667555Smsmith * Note that we could expand this routine to handle eg. up/downgrading the status 169767555Smsmith * of a drive if we had some idea of what the drive's initial status was. 169860894Smsmith */ 169960894Smsmith 170060894Smsmithstatic char * 170167555Smsmithtwe_format_aen(struct twe_softc *sc, u_int16_t aen) 170260894Smsmith{ 170367555Smsmith static char buf[80]; 170467555Smsmith device_t child; 170567555Smsmith char *code, *msg; 170660894Smsmith 170767555Smsmith code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); 170867555Smsmith msg = code + 2; 170960894Smsmith 171067555Smsmith switch (*code) { 171167555Smsmith case 'q': 171267555Smsmith if (!bootverbose) 171367555Smsmith return(NULL); 171467555Smsmith /* FALLTHROUGH */ 171591790Smsmith case 'a': 171667555Smsmith return(msg); 171767555Smsmith 171867555Smsmith case 'c': 171967555Smsmith if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { 172067555Smsmith sprintf(buf, "twed%d: %s", device_get_unit(child), msg); 172167555Smsmith } else { 172267555Smsmith sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), 172367555Smsmith msg, TWE_AEN_UNIT(aen)); 172467555Smsmith } 172567555Smsmith return(buf); 172691790Smsmith 172791790Smsmith case 'p': 172891790Smsmith sprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen), 172991790Smsmith msg); 173091790Smsmith return(buf); 173191790Smsmith 173267555Smsmith 173367555Smsmith case 'x': 173467555Smsmith default: 173567555Smsmith break; 173667555Smsmith } 173767555Smsmith sprintf(buf, "unknown AEN 0x%x", aen); 173860894Smsmith return(buf); 173960894Smsmith} 174060894Smsmith 174169543Smsmith/******************************************************************************** 174269543Smsmith * Print a diagnostic if the status of the command warrants it, and return 174369543Smsmith * either zero (command was ok) or nonzero (command failed). 174469543Smsmith */ 174567555Smsmithstatic int 174669543Smsmithtwe_report_request(struct twe_request *tr) 174767555Smsmith{ 174869543Smsmith struct twe_softc *sc = tr->tr_sc; 1749118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 175076340Smsmith int result = 0; 175160894Smsmith 175276340Smsmith /* 175376340Smsmith * Check the command status value and handle accordingly. 175476340Smsmith */ 175576340Smsmith if (cmd->generic.status == TWE_STATUS_RESET) { 175676340Smsmith /* 175776340Smsmith * The status code 0xff requests a controller reset. 175876340Smsmith */ 1759123103Sps twe_printf(sc, "command returned with controller reset request\n"); 176076340Smsmith twe_reset(sc); 176169543Smsmith result = 1; 176276340Smsmith } else if (cmd->generic.status > TWE_STATUS_FATAL) { 176369543Smsmith /* 176476340Smsmith * Fatal errors that don't require controller reset. 176591790Smsmith * 176691790Smsmith * We know a few special flags values. 176769543Smsmith */ 176891790Smsmith switch (cmd->generic.flags) { 176991790Smsmith case 0x1b: 177091790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 177191790Smsmith "drive timeout"); 177291790Smsmith break; 177391790Smsmith case 0x51: 177491790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 177591790Smsmith "unrecoverable drive error"); 177691790Smsmith break; 177791790Smsmith default: 177891790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 177991790Smsmith "controller error - %s (flags = 0x%x)\n", 178091790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 178191790Smsmith cmd->generic.flags); 178291790Smsmith result = 1; 178391790Smsmith } 178476340Smsmith } else if (cmd->generic.status > TWE_STATUS_WARNING) { 178576340Smsmith /* 178676340Smsmith * Warning level status. 178776340Smsmith */ 178891790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 178991790Smsmith "warning - %s (flags = 0x%x)\n", 179091790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 179191790Smsmith cmd->generic.flags); 179276340Smsmith } else if (cmd->generic.status > 0x40) { 179376340Smsmith /* 179476340Smsmith * Info level status. 179576340Smsmith */ 179691790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 179791790Smsmith "attention - %s (flags = 0x%x)\n", 179891790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 179991790Smsmith cmd->generic.flags); 180067555Smsmith } 180176340Smsmith 180269543Smsmith return(result); 180367555Smsmith} 180467555Smsmith 180569543Smsmith/******************************************************************************** 180669543Smsmith * Print some controller state to aid in debugging error/panic conditions 180769543Smsmith */ 180867555Smsmithvoid 180967555Smsmithtwe_print_controller(struct twe_softc *sc) 181060894Smsmith{ 181167555Smsmith u_int32_t status_reg; 181260894Smsmith 181367555Smsmith status_reg = TWE_STATUS(sc); 181467555Smsmith twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); 1815123103Sps twe_printf(sc, " current max min\n"); 1816123103Sps twe_printf(sc, "free %04d %04d %04d\n", 1817123103Sps sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max, sc->twe_qstat[TWEQ_FREE].q_min); 1818123103Sps 1819123103Sps twe_printf(sc, "ready %04d %04d %04d\n", 1820123103Sps sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max, sc->twe_qstat[TWEQ_READY].q_min); 1821123103Sps 1822123103Sps twe_printf(sc, "busy %04d %04d %04d\n", 1823123103Sps sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max, sc->twe_qstat[TWEQ_BUSY].q_min); 1824123103Sps 1825123103Sps twe_printf(sc, "complete %04d %04d %04d\n", 1826123103Sps sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max, sc->twe_qstat[TWEQ_COMPLETE].q_min); 1827123103Sps 1828123103Sps twe_printf(sc, "bioq %04d %04d %04d\n", 1829123103Sps sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max, sc->twe_qstat[TWEQ_BIO].q_min); 1830123103Sps 183167555Smsmith twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); 183267555Smsmith} 183360894Smsmith 183467555Smsmithstatic void 183567555Smsmithtwe_panic(struct twe_softc *sc, char *reason) 183667555Smsmith{ 183767555Smsmith twe_print_controller(sc); 183867555Smsmith#ifdef TWE_DEBUG 183967555Smsmith panic(reason); 184067555Smsmith#else 184167555Smsmith twe_reset(sc); 184267555Smsmith#endif 184360894Smsmith} 184460894Smsmith 1845118816Sps#ifdef TWE_DEBUG 184660894Smsmith/******************************************************************************** 184760894Smsmith * Print a request/command in human-readable format. 184860894Smsmith */ 184960894Smsmithstatic void 185060894Smsmithtwe_print_request(struct twe_request *tr) 185160894Smsmith{ 185267555Smsmith struct twe_softc *sc = tr->tr_sc; 1853118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 185460894Smsmith int i; 185560894Smsmith 185667555Smsmith twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", 185767555Smsmith cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, 185867555Smsmith cmd->generic.unit, cmd->generic.host_id); 185967555Smsmith twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", 186067555Smsmith cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); 186167555Smsmith 186267555Smsmith switch(cmd->generic.opcode) { /* XXX add more opcodes? */ 186367555Smsmith case TWE_OP_READ: 186467555Smsmith case TWE_OP_WRITE: 186567555Smsmith twe_printf(sc, " lba %d\n", cmd->io.lba); 186667555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) 186767555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 186867555Smsmith i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); 186960894Smsmith break; 187060894Smsmith 187167555Smsmith case TWE_OP_GET_PARAM: 187267555Smsmith case TWE_OP_SET_PARAM: 187367555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) 187467555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 187567555Smsmith i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); 187660894Smsmith break; 187760894Smsmith 187867555Smsmith case TWE_OP_INIT_CONNECTION: 187967555Smsmith twe_printf(sc, " response queue pointer 0x%x\n", 188067555Smsmith cmd->initconnection.response_queue_pointer); 188167555Smsmith break; 188267555Smsmith 188360894Smsmith default: 188467555Smsmith break; 188560894Smsmith } 188667555Smsmith twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", 1887118816Sps tr, TWE_FIND_COMMANDPHYS(tr), tr->tr_data, tr->tr_dataphys, tr->tr_length); 188867555Smsmith twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", 188967555Smsmith tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); 189060894Smsmith} 189160894Smsmith 189260894Smsmith#endif 1893