twe.c revision 197409
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 197409 2009-09-22 16:28:07Z rdivacky $ 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); 62141492Sscottlstatic int twe_immediate_request(struct twe_request *tr, int usetmp); 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 387130358Svkashyap if (sc->twe_state & (TWE_STATE_CTLR_BUSY | 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 399129144Svkashyap /* get a command to handle the bio with */ 400129144Svkashyap if (twe_get_request(sc, &tr)) 40169543Smsmith break; 40269543Smsmith 403129144Svkashyap /* see if there's work to be done */ 404129144Svkashyap if ((bp = twe_dequeue_bio(sc)) == NULL) { 405129144Svkashyap twe_release_request(tr); 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 434127415Svkashyap /* try to map and submit the command to controller */ 435118816Sps error = twe_map_request(tr); 436127415Svkashyap 43769543Smsmith if (error != 0) { 438130358Svkashyap if (error == EBUSY) 439130358Svkashyap break; 440127415Svkashyap tr->tr_status = TWE_CMD_ERROR; 441127415Svkashyap if (tr->tr_private != NULL) { 442127415Svkashyap bp = (twe_bio *)(tr->tr_private); 443127415Svkashyap TWE_BIO_SET_ERROR(bp, error); 444127415Svkashyap tr->tr_private = NULL; 445127415Svkashyap twed_intr(bp); 446127415Svkashyap twe_release_request(tr); 447127415Svkashyap } else if (tr->tr_flags & TWE_CMD_SLEEPER) 448127415Svkashyap wakeup_one(tr); /* wakeup the sleeping owner */ 44969543Smsmith } 45069543Smsmith } 45160894Smsmith} 45260894Smsmith 45360894Smsmith/******************************************************************************** 45469543Smsmith * Write blocks from memory to disk, for system crash dumps. 45569543Smsmith */ 45669543Smsmithint 45769543Smsmithtwe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) 45869543Smsmith{ 45969543Smsmith struct twe_request *tr; 46069543Smsmith TWE_Command *cmd; 46169543Smsmith int error; 46269543Smsmith 46369543Smsmith if (twe_get_request(sc, &tr)) 46469543Smsmith return(ENOMEM); 46569543Smsmith 46669543Smsmith tr->tr_data = data; 46769543Smsmith tr->tr_status = TWE_CMD_SETUP; 46869543Smsmith tr->tr_length = nblks * TWE_BLOCK_SIZE; 46969543Smsmith tr->tr_flags = TWE_CMD_DATAOUT; 47069543Smsmith 471118816Sps cmd = TWE_FIND_COMMAND(tr); 47269543Smsmith cmd->io.opcode = TWE_OP_WRITE; 47369543Smsmith cmd->io.size = 3; 47469543Smsmith cmd->io.unit = unit; 47569543Smsmith cmd->io.block_count = nblks; 47669543Smsmith cmd->io.lba = lba; 47769543Smsmith 478141492Sscottl error = twe_immediate_request(tr, 0); 47969543Smsmith if (error == 0) 48069543Smsmith if (twe_report_request(tr)) 48169543Smsmith error = EIO; 48269543Smsmith twe_release_request(tr); 48369543Smsmith return(error); 48469543Smsmith} 48569543Smsmith 48669543Smsmith/******************************************************************************** 48767555Smsmith * Handle controller-specific control operations. 48860894Smsmith */ 48967555Smsmithint 490197409Srdivackytwe_ioctl(struct twe_softc *sc, u_long ioctlcmd, void *addr) 49160894Smsmith{ 49267555Smsmith struct twe_usercommand *tu = (struct twe_usercommand *)addr; 49367555Smsmith struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; 494118508Sps struct twe_drivecommand *td = (struct twe_drivecommand *)addr; 49567555Smsmith union twe_statrequest *ts = (union twe_statrequest *)addr; 49667555Smsmith TWE_Param *param; 497118816Sps TWE_Command *cmd; 49867555Smsmith void *data; 499123103Sps u_int16_t *aen_code = (u_int16_t *)addr; 50067555Smsmith struct twe_request *tr; 50169543Smsmith u_int8_t srid; 50267555Smsmith int s, error; 50360894Smsmith 50467555Smsmith error = 0; 505118816Sps switch(ioctlcmd) { 50667555Smsmith /* handle a command from userspace */ 50767555Smsmith case TWEIO_COMMAND: 50867555Smsmith /* get a request */ 509118508Sps while (twe_get_request(sc, &tr)) 510119124Sps tsleep(sc, PPAUSE, "twioctl", hz); 51167555Smsmith 51269543Smsmith /* 51369543Smsmith * Save the command's request ID, copy the user-supplied command in, 51469543Smsmith * restore the request ID. 51569543Smsmith */ 516118816Sps cmd = TWE_FIND_COMMAND(tr); 517118816Sps srid = cmd->generic.request_id; 518118816Sps bcopy(&tu->tu_command, cmd, sizeof(TWE_Command)); 519118816Sps cmd->generic.request_id = srid; 52067555Smsmith 521118508Sps /* 522118508Sps * if there's a data buffer, allocate and copy it in. 523118508Sps * Must be in multipled of 512 bytes. 524118508Sps */ 525118508Sps tr->tr_length = (tu->tu_size + 511) & ~511; 52667555Smsmith if (tr->tr_length > 0) { 527111119Simp if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { 52867555Smsmith error = ENOMEM; 52967555Smsmith goto cmd_done; 53067555Smsmith } 531118508Sps if ((error = copyin(tu->tu_data, tr->tr_data, tu->tu_size)) != 0) 53267555Smsmith goto cmd_done; 53367555Smsmith tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 53467555Smsmith } 53567555Smsmith 53667555Smsmith /* run the command */ 537127415Svkashyap error = twe_wait_request(tr); 538127415Svkashyap if (error) 539127415Svkashyap goto cmd_done; 54067555Smsmith 54167555Smsmith /* copy the command out again */ 542118816Sps bcopy(cmd, &tu->tu_command, sizeof(TWE_Command)); 54367555Smsmith 54467555Smsmith /* if there was a data buffer, copy it out */ 54567555Smsmith if (tr->tr_length > 0) 546118508Sps error = copyout(tr->tr_data, tu->tu_data, tu->tu_size); 54767555Smsmith 54867555Smsmith cmd_done: 54967555Smsmith /* free resources */ 55067555Smsmith if (tr->tr_data != NULL) 55167555Smsmith free(tr->tr_data, M_DEVBUF); 55267555Smsmith if (tr != NULL) 55367555Smsmith twe_release_request(tr); 55467555Smsmith 55567555Smsmith break; 55667555Smsmith 55767555Smsmith /* fetch statistics counter */ 55867555Smsmith case TWEIO_STATS: 55967555Smsmith switch (ts->ts_item) { 56067555Smsmith#ifdef TWE_PERFORMANCE_MONITOR 56167555Smsmith case TWEQ_FREE: 56267555Smsmith case TWEQ_BIO: 56367555Smsmith case TWEQ_READY: 56467555Smsmith case TWEQ_BUSY: 56567555Smsmith case TWEQ_COMPLETE: 56667555Smsmith bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); 56767555Smsmith break; 56867555Smsmith#endif 56967555Smsmith default: 57067555Smsmith error = ENOENT; 57167555Smsmith break; 57267555Smsmith } 57367555Smsmith break; 57467555Smsmith 57567555Smsmith /* poll for an AEN */ 57667555Smsmith case TWEIO_AEN_POLL: 577123103Sps *aen_code = twe_dequeue_aen(sc); 57867555Smsmith break; 57967555Smsmith 58067555Smsmith /* wait for another AEN to show up */ 58167555Smsmith case TWEIO_AEN_WAIT: 58267555Smsmith s = splbio(); 583123103Sps while ((*aen_code = twe_dequeue_aen(sc)) == TWE_AEN_QUEUE_EMPTY) { 58467555Smsmith error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); 58567555Smsmith if (error == EINTR) 58667555Smsmith break; 58767555Smsmith } 58867555Smsmith splx(s); 58967555Smsmith break; 59067555Smsmith 59167555Smsmith case TWEIO_GET_PARAM: 59267555Smsmith if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { 59367555Smsmith twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", 59467555Smsmith tp->tp_table_id, tp->tp_param_id, tp->tp_size); 59567555Smsmith error = EINVAL; 59667555Smsmith } else { 59767555Smsmith if (param->parameter_size_bytes > tp->tp_size) { 59867555Smsmith twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", 59967555Smsmith param->parameter_size_bytes, tp->tp_size); 60067555Smsmith error = EFAULT; 60167555Smsmith } else { 60267555Smsmith error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); 60367555Smsmith } 60467555Smsmith free(param, M_DEVBUF); 60567555Smsmith } 60667555Smsmith break; 60767555Smsmith 60867555Smsmith case TWEIO_SET_PARAM: 609111119Simp if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { 61067555Smsmith error = ENOMEM; 61167555Smsmith } else { 61267555Smsmith error = copyin(tp->tp_data, data, tp->tp_size); 61367555Smsmith if (error == 0) 61467555Smsmith error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); 61567555Smsmith free(data, M_DEVBUF); 61667555Smsmith } 61767555Smsmith break; 61867555Smsmith 61967555Smsmith case TWEIO_RESET: 62067555Smsmith twe_reset(sc); 62167555Smsmith break; 62267555Smsmith 623118508Sps case TWEIO_ADD_UNIT: 624123103Sps error = twe_add_unit(sc, td->td_unit); 625118508Sps break; 626118508Sps 627118508Sps case TWEIO_DEL_UNIT: 628123103Sps error = twe_del_unit(sc, td->td_unit); 629118508Sps break; 630118508Sps 63191790Smsmith /* XXX implement ATA PASSTHROUGH */ 63291790Smsmith 63367555Smsmith /* nothing we understand */ 63467555Smsmith default: 63567555Smsmith error = ENOTTY; 63667555Smsmith } 63767555Smsmith 63867555Smsmith return(error); 63960894Smsmith} 64060894Smsmith 64160894Smsmith/******************************************************************************** 64267555Smsmith * Enable the useful interrupts from the controller. 64360894Smsmith */ 64467555Smsmithvoid 64567555Smsmithtwe_enable_interrupts(struct twe_softc *sc) 64660894Smsmith{ 64767555Smsmith sc->twe_state |= TWE_STATE_INTEN; 64867555Smsmith TWE_CONTROL(sc, 64967555Smsmith TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | 65067555Smsmith TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | 65167555Smsmith TWE_CONTROL_ENABLE_INTERRUPTS); 65260894Smsmith} 65360894Smsmith 65460894Smsmith/******************************************************************************** 65567555Smsmith * Disable interrupts from the controller. 65667555Smsmith */ 65767555Smsmithvoid 65867555Smsmithtwe_disable_interrupts(struct twe_softc *sc) 65967555Smsmith{ 66067555Smsmith TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); 66167555Smsmith sc->twe_state &= ~TWE_STATE_INTEN; 66267555Smsmith} 66367555Smsmith 66467555Smsmith/******************************************************************************** 66560894Smsmith ******************************************************************************** 66660894Smsmith Command Submission 66760894Smsmith ******************************************************************************** 66860894Smsmith ********************************************************************************/ 66960894Smsmith 67067555Smsmith/******************************************************************************** 67167555Smsmith * Read integer parameter table entries. 67260894Smsmith */ 67367555Smsmithstatic int 67467555Smsmithtwe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) 67560894Smsmith{ 67667555Smsmith TWE_Param *param; 67760894Smsmith 67867555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) 67967555Smsmith return(ENOENT); 68067555Smsmith *result = *(u_int8_t *)param->data; 68167555Smsmith free(param, M_DEVBUF); 68267555Smsmith return(0); 68367555Smsmith} 68460894Smsmith 68567555Smsmithstatic int 68667555Smsmithtwe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) 68767555Smsmith{ 68867555Smsmith TWE_Param *param; 68967555Smsmith 69067555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) 69167555Smsmith return(ENOENT); 69267555Smsmith *result = *(u_int16_t *)param->data; 69367555Smsmith free(param, M_DEVBUF); 69460894Smsmith return(0); 69560894Smsmith} 69660894Smsmith 69767555Smsmithstatic int 69867555Smsmithtwe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) 69967555Smsmith{ 70067555Smsmith TWE_Param *param; 70167555Smsmith 70267555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) 70367555Smsmith return(ENOENT); 70467555Smsmith *result = *(u_int32_t *)param->data; 70567555Smsmith free(param, M_DEVBUF); 70667555Smsmith return(0); 70767555Smsmith} 70867555Smsmith 70960894Smsmith/******************************************************************************** 71060894Smsmith * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it 71160894Smsmith * will be called with the command when it's completed. If no callback is 71260894Smsmith * provided, we will wait for the command to complete and then return just the data. 71360894Smsmith * The caller is responsible for freeing the data when done with it. 71460894Smsmith */ 71560894Smsmithstatic void * 71667555Smsmithtwe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, 71767555Smsmith void (* func)(struct twe_request *tr)) 71860894Smsmith{ 71960894Smsmith struct twe_request *tr; 72060894Smsmith TWE_Command *cmd; 72160894Smsmith TWE_Param *param; 72260894Smsmith int error; 72360894Smsmith 72460894Smsmith debug_called(4); 72560894Smsmith 72660894Smsmith tr = NULL; 72760894Smsmith param = NULL; 72860894Smsmith 72960894Smsmith /* get a command */ 73067555Smsmith if (twe_get_request(sc, &tr)) 73160894Smsmith goto err; 73260894Smsmith 73360894Smsmith /* get a buffer */ 73460894Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 73560894Smsmith goto err; 73660894Smsmith tr->tr_data = param; 73760894Smsmith tr->tr_length = TWE_SECTOR_SIZE; 73860894Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 73960894Smsmith 74060894Smsmith /* build the command for the controller */ 741118816Sps cmd = TWE_FIND_COMMAND(tr); 74267555Smsmith cmd->param.opcode = TWE_OP_GET_PARAM; 74367555Smsmith cmd->param.size = 2; 74467555Smsmith cmd->param.unit = 0; 74567555Smsmith cmd->param.param_count = 1; 74660894Smsmith 74760894Smsmith /* fill in the outbound parameter data */ 74860894Smsmith param->table_id = table_id; 74967555Smsmith param->parameter_id = param_id; 75067555Smsmith param->parameter_size_bytes = param_size; 75160894Smsmith 75260894Smsmith /* submit the command and either wait or let the callback handle it */ 75360894Smsmith if (func == NULL) { 75460894Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 755141492Sscottl error = twe_immediate_request(tr, 1 /* usetmp */); 75660894Smsmith if (error == 0) { 75769543Smsmith if (twe_report_request(tr)) 75860894Smsmith goto err; 759123103Sps } else { 760123103Sps goto err; 76160894Smsmith } 76267555Smsmith twe_release_request(tr); 76367555Smsmith return(param); 76460894Smsmith } else { 76560894Smsmith tr->tr_complete = func; 766118816Sps error = twe_map_request(tr); 767130358Svkashyap if ((error == 0) || (error == EBUSY)) 76860894Smsmith return(func); 76960894Smsmith } 77060894Smsmith 77160894Smsmith /* something failed */ 77260894Smsmitherr: 77360894Smsmith debug(1, "failed"); 77460894Smsmith if (tr != NULL) 77560894Smsmith twe_release_request(tr); 77660894Smsmith if (param != NULL) 77760894Smsmith free(param, M_DEVBUF); 77860894Smsmith return(NULL); 77960894Smsmith} 78060894Smsmith 78160894Smsmith/******************************************************************************** 78267555Smsmith * Set integer parameter table entries. 78367555Smsmith */ 78491449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 78567555Smsmithstatic int 78667555Smsmithtwe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) 78767555Smsmith{ 78867555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 78967555Smsmith} 79091449Speter#endif 79167555Smsmith 79291449Speter#if 0 79367555Smsmithstatic int 79467555Smsmithtwe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) 79567555Smsmith{ 79667555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 79767555Smsmith} 79867555Smsmith 79967555Smsmithstatic int 80067555Smsmithtwe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) 80167555Smsmith{ 80267555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 80367555Smsmith} 80491449Speter#endif 80567555Smsmith 80667555Smsmith/******************************************************************************** 80767555Smsmith * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. 80867555Smsmith */ 80967555Smsmithstatic int 81067555Smsmithtwe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) 81167555Smsmith{ 81267555Smsmith struct twe_request *tr; 81367555Smsmith TWE_Command *cmd; 81467555Smsmith TWE_Param *param; 81567555Smsmith int error; 81667555Smsmith 81767555Smsmith debug_called(4); 81867555Smsmith 81967555Smsmith tr = NULL; 82067555Smsmith param = NULL; 82167555Smsmith error = ENOMEM; 82267555Smsmith 82367555Smsmith /* get a command */ 82467555Smsmith if (twe_get_request(sc, &tr)) 82567555Smsmith goto out; 82667555Smsmith 82767555Smsmith /* get a buffer */ 82867555Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 82967555Smsmith goto out; 83067555Smsmith tr->tr_data = param; 83167555Smsmith tr->tr_length = TWE_SECTOR_SIZE; 83267555Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 83367555Smsmith 83467555Smsmith /* build the command for the controller */ 835118816Sps cmd = TWE_FIND_COMMAND(tr); 83667555Smsmith cmd->param.opcode = TWE_OP_SET_PARAM; 83767555Smsmith cmd->param.size = 2; 83867555Smsmith cmd->param.unit = 0; 83967555Smsmith cmd->param.param_count = 1; 84067555Smsmith 84167555Smsmith /* fill in the outbound parameter data */ 84267555Smsmith param->table_id = table_id; 84367555Smsmith param->parameter_id = param_id; 84467555Smsmith param->parameter_size_bytes = param_size; 84567555Smsmith bcopy(data, param->data, param_size); 84667555Smsmith 84767555Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 848141492Sscottl error = twe_immediate_request(tr, 1 /* usetmp */); 84967555Smsmith if (error == 0) { 85069543Smsmith if (twe_report_request(tr)) 85167555Smsmith error = EIO; 85267555Smsmith } 85367555Smsmith 85467555Smsmithout: 85567555Smsmith if (tr != NULL) 85667555Smsmith twe_release_request(tr); 85767555Smsmith if (param != NULL) 85867555Smsmith free(param, M_DEVBUF); 85967555Smsmith return(error); 86067555Smsmith} 86167555Smsmith 86267555Smsmith/******************************************************************************** 86360894Smsmith * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. 86460894Smsmith * 86560894Smsmith * Typically called with interrupts disabled. 86660894Smsmith */ 86760894Smsmithstatic int 86867555Smsmithtwe_init_connection(struct twe_softc *sc, int mode) 86960894Smsmith{ 87060894Smsmith struct twe_request *tr; 87160894Smsmith TWE_Command *cmd; 87260894Smsmith int error; 87360894Smsmith 87460894Smsmith debug_called(4); 87560894Smsmith 87660894Smsmith /* get a command */ 87767555Smsmith if (twe_get_request(sc, &tr)) 878102291Sarchie return(0); 87960894Smsmith 88060894Smsmith /* build the command */ 881118816Sps cmd = TWE_FIND_COMMAND(tr); 88267555Smsmith cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; 88367555Smsmith cmd->initconnection.size = 3; 88467555Smsmith cmd->initconnection.host_id = 0; 88567555Smsmith cmd->initconnection.message_credits = mode; 88667555Smsmith cmd->initconnection.response_queue_pointer = 0; 88760894Smsmith 88860894Smsmith /* submit the command */ 889141492Sscottl error = twe_immediate_request(tr, 0 /* usetmp */); 89060894Smsmith twe_release_request(tr); 89160894Smsmith 89267555Smsmith if (mode == TWE_INIT_MESSAGE_CREDITS) 89367555Smsmith sc->twe_host_id = cmd->initconnection.host_id; 89460894Smsmith return(error); 89560894Smsmith} 89660894Smsmith 89760894Smsmith/******************************************************************************** 89860894Smsmith * Start the command (tr) and sleep waiting for it to complete. 89960894Smsmith * 90060894Smsmith * Successfully completed commands are dequeued. 90160894Smsmith */ 90260894Smsmithstatic int 90360894Smsmithtwe_wait_request(struct twe_request *tr) 90460894Smsmith{ 90567555Smsmith int s; 90660894Smsmith 90760894Smsmith debug_called(4); 90860894Smsmith 90967555Smsmith tr->tr_flags |= TWE_CMD_SLEEPER; 91067555Smsmith tr->tr_status = TWE_CMD_BUSY; 91167555Smsmith twe_enqueue_ready(tr); 91267555Smsmith twe_startio(tr->tr_sc); 91360894Smsmith s = splbio(); 91467555Smsmith while (tr->tr_status == TWE_CMD_BUSY) 91567555Smsmith tsleep(tr, PRIBIO, "twewait", 0); 91660894Smsmith splx(s); 91767555Smsmith 918127415Svkashyap return(tr->tr_status != TWE_CMD_COMPLETE); 91960894Smsmith} 92060894Smsmith 92160894Smsmith/******************************************************************************** 92260894Smsmith * Start the command (tr) and busy-wait for it to complete. 92360894Smsmith * This should only be used when interrupts are actually disabled (although it 92460894Smsmith * will work if they are not). 92560894Smsmith */ 92660894Smsmithstatic int 927141492Sscottltwe_immediate_request(struct twe_request *tr, int usetmp) 92860894Smsmith{ 929141492Sscottl struct twe_softc *sc; 930127415Svkashyap int error; 931141492Sscottl int count = 0; 93260894Smsmith 93360894Smsmith debug_called(4); 93460894Smsmith 935141492Sscottl sc = tr->tr_sc; 936141492Sscottl 937141492Sscottl if (usetmp && (tr->tr_data != NULL)) { 938141492Sscottl tr->tr_flags |= TWE_CMD_IMMEDIATE; 939141492Sscottl if (tr->tr_length > MAXBSIZE) 940141492Sscottl return (EINVAL); 941141492Sscottl bcopy(tr->tr_data, sc->twe_immediate, tr->tr_length); 942141492Sscottl } 943118816Sps tr->tr_status = TWE_CMD_BUSY; 944127415Svkashyap if ((error = twe_map_request(tr)) != 0) 945130358Svkashyap if (error != EBUSY) 946130358Svkashyap return(error); 947141492Sscottl 948141492Sscottl /* Wait up to 5 seconds for the command to complete */ 949141492Sscottl while ((count++ < 5000) && (tr->tr_status == TWE_CMD_BUSY)){ 950141492Sscottl DELAY(1000); 951141492Sscottl twe_done(sc); 95260894Smsmith } 953141492Sscottl if (usetmp && (tr->tr_data != NULL)) 954141492Sscottl bcopy(sc->twe_immediate, tr->tr_data, tr->tr_length); 955141492Sscottl 95660894Smsmith return(tr->tr_status != TWE_CMD_COMPLETE); 95760894Smsmith} 95860894Smsmith 95960894Smsmith/******************************************************************************** 96060894Smsmith * Handle completion of an I/O command. 96160894Smsmith */ 96260894Smsmithstatic void 96360894Smsmithtwe_completeio(struct twe_request *tr) 96460894Smsmith{ 965123103Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 96660894Smsmith struct twe_softc *sc = tr->tr_sc; 96767555Smsmith twe_bio *bp = (twe_bio *)tr->tr_private; 96860894Smsmith 96960894Smsmith debug_called(4); 97060894Smsmith 97160894Smsmith if (tr->tr_status == TWE_CMD_COMPLETE) { 97267555Smsmith 973123103Sps if (cmd->generic.status) 974123103Sps if (twe_report_request(tr)) 975123103Sps TWE_BIO_SET_ERROR(bp, EIO); 97667555Smsmith 97769543Smsmith } else { 97869543Smsmith twe_panic(sc, "twe_completeio on incomplete command"); 97960894Smsmith } 98069543Smsmith tr->tr_private = NULL; 98169543Smsmith twed_intr(bp); 98260894Smsmith twe_release_request(tr); 98360894Smsmith} 98460894Smsmith 98560894Smsmith/******************************************************************************** 98667555Smsmith * Reset the controller and pull all the active commands back onto the ready 98767555Smsmith * queue. Used to restart a controller that's exhibiting bad behaviour. 98867555Smsmith */ 98967555Smsmithstatic void 99067555Smsmithtwe_reset(struct twe_softc *sc) 99167555Smsmith{ 99267555Smsmith struct twe_request *tr; 99367555Smsmith int i, s; 99467555Smsmith 99591790Smsmith /* 99691790Smsmith * Sleep for a short period to allow AENs to be signalled. 99791790Smsmith */ 998119124Sps tsleep(sc, PRIBIO, "twereset", hz); 99967555Smsmith 100067555Smsmith /* 100167555Smsmith * Disable interrupts from the controller, and mask any accidental entry 100267555Smsmith * into our interrupt handler. 100367555Smsmith */ 100491790Smsmith twe_printf(sc, "controller reset in progress...\n"); 100567555Smsmith twe_disable_interrupts(sc); 100667555Smsmith s = splbio(); 100767555Smsmith 100867555Smsmith /* 100967555Smsmith * Try to soft-reset the controller. 101067555Smsmith */ 101167555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 101267555Smsmith 101367555Smsmith if (i > 0) 101467555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 101567555Smsmith 101667555Smsmith if (!twe_soft_reset(sc)) 101767555Smsmith break; /* reset process complete */ 101867555Smsmith } 101967555Smsmith /* did we give up? */ 102067555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 102167555Smsmith twe_printf(sc, "can't reset controller, giving up\n"); 102267555Smsmith goto out; 102367555Smsmith } 102467555Smsmith 102567555Smsmith /* 102667555Smsmith * Move all of the commands that were busy back to the ready queue. 102767555Smsmith */ 102867555Smsmith i = 0; 102967555Smsmith while ((tr = twe_dequeue_busy(sc)) != NULL) { 103067555Smsmith twe_enqueue_ready(tr); 103167555Smsmith i++; 103267555Smsmith } 103367555Smsmith 103467555Smsmith /* 103567555Smsmith * Kick the controller to start things going again, then re-enable interrupts. 103667555Smsmith */ 103767555Smsmith twe_startio(sc); 103867555Smsmith twe_enable_interrupts(sc); 103967555Smsmith twe_printf(sc, "controller reset done, %d commands restarted\n", i); 104067555Smsmith 104167555Smsmithout: 104267555Smsmith splx(s); 104367555Smsmith twe_enable_interrupts(sc); 104467555Smsmith} 104567555Smsmith 104667555Smsmith/******************************************************************************** 104760894Smsmith ******************************************************************************** 104860894Smsmith Command I/O to Controller 104960894Smsmith ******************************************************************************** 105060894Smsmith ********************************************************************************/ 105160894Smsmith 105260894Smsmith/******************************************************************************** 105360894Smsmith * Try to deliver (tr) to the controller. 105460894Smsmith * 105560894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 105660894Smsmith */ 1057118816Spsint 105860894Smsmithtwe_start(struct twe_request *tr) 105960894Smsmith{ 106060894Smsmith struct twe_softc *sc = tr->tr_sc; 1061118816Sps TWE_Command *cmd; 106260894Smsmith int i, s, done; 106360894Smsmith u_int32_t status_reg; 106460894Smsmith 106560894Smsmith debug_called(4); 106660894Smsmith 106760894Smsmith /* mark the command as currently being processed */ 106860894Smsmith tr->tr_status = TWE_CMD_BUSY; 1069118816Sps cmd = TWE_FIND_COMMAND(tr); 107060894Smsmith 107167555Smsmith /* 107267555Smsmith * Spin briefly waiting for the controller to come ready 107367555Smsmith * 107467555Smsmith * XXX it might be more efficient to return EBUSY immediately 107567555Smsmith * and let the command be rescheduled. 107667555Smsmith */ 107760894Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 107860894Smsmith s = splbio(); 107960894Smsmith 108060894Smsmith /* check to see if we can post a command */ 108160894Smsmith status_reg = TWE_STATUS(sc); 108260894Smsmith twe_check_bits(sc, status_reg); 108360894Smsmith 108460894Smsmith if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { 1085118816Sps twe_enqueue_busy(tr); 1086118816Sps 1087118816Sps TWE_COMMAND_QUEUE(sc, TWE_FIND_COMMANDPHYS(tr)); 108860894Smsmith done = 1; 108960894Smsmith /* move command to work queue */ 109067555Smsmith#ifdef TWE_DEBUG 109160894Smsmith if (tr->tr_complete != NULL) { 1092118816Sps debug(3, "queued request %d with callback %p", cmd->generic.request_id, tr->tr_complete); 109367555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { 1094118816Sps debug(3, "queued request %d with wait channel %p", cmd->generic.request_id, tr); 109560894Smsmith } else { 1096118816Sps debug(3, "queued request %d for polling caller", cmd->generic.request_id); 109760894Smsmith } 109867555Smsmith#endif 109960894Smsmith } 110060894Smsmith splx(s); /* drop spl to allow completion interrupts */ 110160894Smsmith } 110260894Smsmith 110360894Smsmith /* command is enqueued */ 110460894Smsmith if (done) 110560894Smsmith return(0); 110660894Smsmith 110760894Smsmith /* 110860894Smsmith * We couldn't get the controller to take the command; try submitting it again later. 110960894Smsmith * This should only happen if something is wrong with the controller, or if we have 111060894Smsmith * overestimated the number of commands it can accept. (Should we actually reject 111160894Smsmith * the command at this point?) 111260894Smsmith */ 111360894Smsmith return(EBUSY); 111460894Smsmith} 111560894Smsmith 111660894Smsmith/******************************************************************************** 111760894Smsmith * Poll the controller (sc) for completed commands. 111860894Smsmith * 111960894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 112060894Smsmith */ 112160894Smsmithstatic void 112260894Smsmithtwe_done(struct twe_softc *sc) 112360894Smsmith{ 112460894Smsmith TWE_Response_Queue rq; 1125118816Sps TWE_Command *cmd; 112660894Smsmith struct twe_request *tr; 112760894Smsmith int s, found; 112860894Smsmith u_int32_t status_reg; 112960894Smsmith 113060894Smsmith debug_called(5); 113160894Smsmith 113260894Smsmith /* loop collecting completed commands */ 113360894Smsmith found = 0; 113460894Smsmith s = splbio(); 113560894Smsmith for (;;) { 113660894Smsmith status_reg = TWE_STATUS(sc); 113760894Smsmith twe_check_bits(sc, status_reg); /* XXX should this fail? */ 113860894Smsmith 113960894Smsmith if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { 114060894Smsmith found = 1; 114160894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 114267555Smsmith tr = sc->twe_lookup[rq.u.response_id]; /* find command */ 1143118816Sps cmd = TWE_FIND_COMMAND(tr); 114469543Smsmith if (tr->tr_status != TWE_CMD_BUSY) 114569543Smsmith twe_printf(sc, "completion event for nonbusy command\n"); 114669543Smsmith tr->tr_status = TWE_CMD_COMPLETE; 114769543Smsmith debug(3, "completed request id %d with status %d", 1148118816Sps cmd->generic.request_id, cmd->generic.status); 114969543Smsmith /* move to completed queue */ 115069543Smsmith twe_remove_busy(tr); 115169543Smsmith twe_enqueue_complete(tr); 1152130358Svkashyap sc->twe_state &= ~TWE_STATE_CTLR_BUSY; 115360894Smsmith } else { 115460894Smsmith break; /* no response ready */ 115560894Smsmith } 115660894Smsmith } 115760894Smsmith splx(s); 115860894Smsmith 115960894Smsmith /* if we've completed any commands, try posting some more */ 116060894Smsmith if (found) 116160894Smsmith twe_startio(sc); 116260894Smsmith 116360894Smsmith /* handle completion and timeouts */ 116467555Smsmith twe_complete(sc); /* XXX use deferred completion? */ 116560894Smsmith} 116660894Smsmith 116760894Smsmith/******************************************************************************** 116860894Smsmith * Perform post-completion processing for commands on (sc). 116960894Smsmith * 117060894Smsmith * This is split from twe_done as it can be safely deferred and run at a lower 117160894Smsmith * priority level should facilities for such a thing become available. 117260894Smsmith */ 117360894Smsmithstatic void 117460894Smsmithtwe_complete(struct twe_softc *sc) 117560894Smsmith{ 117667555Smsmith struct twe_request *tr; 117760894Smsmith 117860894Smsmith debug_called(5); 117960894Smsmith 118060894Smsmith /* 118167555Smsmith * Pull commands off the completed list, dispatch them appropriately 118260894Smsmith */ 118367555Smsmith while ((tr = twe_dequeue_complete(sc)) != NULL) { 118467555Smsmith /* unmap the command's data buffer */ 118567555Smsmith twe_unmap_request(tr); 118660894Smsmith 118767555Smsmith /* dispatch to suit command originator */ 118867555Smsmith if (tr->tr_complete != NULL) { /* completion callback */ 118967555Smsmith debug(2, "call completion handler %p", tr->tr_complete); 119067555Smsmith tr->tr_complete(tr); 119160894Smsmith 119267555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ 119367555Smsmith debug(2, "wake up command owner on %p", tr); 119467555Smsmith wakeup_one(tr); 119560894Smsmith 119667555Smsmith } else { /* caller is polling command */ 119767555Smsmith debug(2, "command left for owner"); 119860894Smsmith } 119967555Smsmith } 120060894Smsmith} 120160894Smsmith 120260894Smsmith/******************************************************************************** 120360894Smsmith * Wait for (status) to be set in the controller status register for up to 120460894Smsmith * (timeout) seconds. Returns 0 if found, nonzero if we time out. 120560894Smsmith * 120660894Smsmith * Note: this busy-waits, rather than sleeping, since we may be called with 120760894Smsmith * eg. clock interrupts masked. 120860894Smsmith */ 120960894Smsmithstatic int 121060894Smsmithtwe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) 121160894Smsmith{ 121260894Smsmith time_t expiry; 121360894Smsmith u_int32_t status_reg; 121460894Smsmith 121560894Smsmith debug_called(4); 121660894Smsmith 121760894Smsmith expiry = time_second + timeout; 121860894Smsmith 121960894Smsmith do { 122060894Smsmith status_reg = TWE_STATUS(sc); 122160894Smsmith if (status_reg & status) /* got the required bit(s)? */ 122260894Smsmith return(0); 122360894Smsmith DELAY(100000); 122460894Smsmith } while (time_second <= expiry); 122560894Smsmith 122660894Smsmith return(1); 122760894Smsmith} 122860894Smsmith 122960894Smsmith/******************************************************************************** 123060894Smsmith * Drain the response queue, which may contain responses to commands we know 123160894Smsmith * nothing about. 123260894Smsmith */ 123360894Smsmithstatic int 123460894Smsmithtwe_drain_response_queue(struct twe_softc *sc) 123560894Smsmith{ 123660894Smsmith TWE_Response_Queue rq; 123760894Smsmith u_int32_t status_reg; 123860894Smsmith 123960894Smsmith debug_called(4); 124060894Smsmith 124160894Smsmith for (;;) { /* XXX give up eventually? */ 124260894Smsmith status_reg = TWE_STATUS(sc); 124360894Smsmith if (twe_check_bits(sc, status_reg)) 124460894Smsmith return(1); 124560894Smsmith if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) 124660894Smsmith return(0); 124760894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 124860894Smsmith } 124960894Smsmith} 125060894Smsmith 125160894Smsmith/******************************************************************************** 125267555Smsmith * Soft-reset the controller 125367555Smsmith */ 125467555Smsmithstatic int 125567555Smsmithtwe_soft_reset(struct twe_softc *sc) 125667555Smsmith{ 125767555Smsmith u_int32_t status_reg; 125867555Smsmith 125967555Smsmith debug_called(2); 126067555Smsmith 126167555Smsmith TWE_SOFT_RESET(sc); 126267555Smsmith 126391790Smsmith if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) { 126467683Smsmith twe_printf(sc, "no attention interrupt\n"); 126567555Smsmith return(1); 126667555Smsmith } 126791790Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 126867555Smsmith if (twe_drain_aen_queue(sc)) { 126967555Smsmith twe_printf(sc, "can't drain AEN queue\n"); 127067555Smsmith return(1); 127167555Smsmith } 127267555Smsmith if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { 127367555Smsmith twe_printf(sc, "reset not reported\n"); 127467555Smsmith return(1); 127567555Smsmith } 127667555Smsmith status_reg = TWE_STATUS(sc); 127767555Smsmith if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { 127867555Smsmith twe_printf(sc, "controller errors detected\n"); 127967555Smsmith return(1); 128067555Smsmith } 128167555Smsmith if (twe_drain_response_queue(sc)) { 128267555Smsmith twe_printf(sc, "can't drain response queue\n"); 128367555Smsmith return(1); 128467555Smsmith } 128567555Smsmith return(0); 128667555Smsmith} 128767555Smsmith 128867555Smsmith/******************************************************************************** 128960894Smsmith ******************************************************************************** 129060894Smsmith Interrupt Handling 129160894Smsmith ******************************************************************************** 129260894Smsmith ********************************************************************************/ 129360894Smsmith 129460894Smsmith/******************************************************************************** 129560894Smsmith * Host interrupt. 129660894Smsmith * 129760894Smsmith * XXX what does this mean? 129860894Smsmith */ 129960894Smsmithstatic void 130060894Smsmithtwe_host_intr(struct twe_softc *sc) 130160894Smsmith{ 130260894Smsmith debug_called(4); 130360894Smsmith 130467555Smsmith twe_printf(sc, "host interrupt\n"); 130560894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); 130660894Smsmith} 130760894Smsmith 130860894Smsmith/******************************************************************************** 130960894Smsmith * Attention interrupt. 131060894Smsmith * 131160894Smsmith * Signalled when the controller has one or more AENs for us. 131260894Smsmith */ 131360894Smsmithstatic void 131460894Smsmithtwe_attention_intr(struct twe_softc *sc) 131560894Smsmith{ 131660894Smsmith debug_called(4); 131760894Smsmith 131860894Smsmith /* instigate a poll for AENs */ 131960894Smsmith if (twe_fetch_aen(sc)) { 132067555Smsmith twe_printf(sc, "error polling for signalled AEN\n"); 132160894Smsmith } else { 132260894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 132360894Smsmith } 132460894Smsmith} 132560894Smsmith 132660894Smsmith/******************************************************************************** 132760894Smsmith * Command interrupt. 132860894Smsmith * 132960894Smsmith * Signalled when the controller can handle more commands. 133060894Smsmith */ 133160894Smsmithstatic void 133260894Smsmithtwe_command_intr(struct twe_softc *sc) 133360894Smsmith{ 133460894Smsmith debug_called(4); 133560894Smsmith 133660894Smsmith /* 133760894Smsmith * We don't use this, rather we try to submit commands when we receive 133860894Smsmith * them, and when other commands have completed. Mask it so we don't get 133960894Smsmith * another one. 134060894Smsmith */ 134160894Smsmith TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); 134260894Smsmith} 134360894Smsmith 134460894Smsmith/******************************************************************************** 134560894Smsmith ******************************************************************************** 134660894Smsmith Asynchronous Event Handling 134760894Smsmith ******************************************************************************** 134860894Smsmith ********************************************************************************/ 134960894Smsmith 135060894Smsmith/******************************************************************************** 135160894Smsmith * Request an AEN from the controller. 135260894Smsmith */ 135360894Smsmithstatic int 135460894Smsmithtwe_fetch_aen(struct twe_softc *sc) 135560894Smsmith{ 135660894Smsmith 135760894Smsmith debug_called(4); 135860894Smsmith 135967555Smsmith if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) 136060894Smsmith return(EIO); 136160894Smsmith return(0); 136260894Smsmith} 136360894Smsmith 136460894Smsmith/******************************************************************************** 136560894Smsmith * Handle an AEN returned by the controller. 136660894Smsmith */ 136760894Smsmithstatic void 136860894Smsmithtwe_handle_aen(struct twe_request *tr) 136960894Smsmith{ 137060894Smsmith struct twe_softc *sc = tr->tr_sc; 137160894Smsmith TWE_Param *param; 137260894Smsmith u_int16_t aen; 137360894Smsmith 137460894Smsmith debug_called(4); 137560894Smsmith 137660894Smsmith /* XXX check for command success somehow? */ 137760894Smsmith 137860894Smsmith param = (TWE_Param *)tr->tr_data; 137960894Smsmith aen = *(u_int16_t *)(param->data); 138060894Smsmith 138160894Smsmith free(tr->tr_data, M_DEVBUF); 138260894Smsmith twe_release_request(tr); 138360894Smsmith twe_enqueue_aen(sc, aen); 138460894Smsmith 138560894Smsmith /* XXX poll for more AENs? */ 138660894Smsmith} 138760894Smsmith 138860894Smsmith/******************************************************************************** 138960894Smsmith * Pull AENs out of the controller and park them in the queue, in a context where 139060894Smsmith * interrupts aren't active. Return nonzero if we encounter any errors in the 139160894Smsmith * process of obtaining all the available AENs. 139260894Smsmith */ 139360894Smsmithstatic int 139460894Smsmithtwe_drain_aen_queue(struct twe_softc *sc) 139560894Smsmith{ 139660894Smsmith u_int16_t aen; 139760894Smsmith 139860894Smsmith for (;;) { 139967555Smsmith if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) 140060894Smsmith return(1); 140160894Smsmith if (aen == TWE_AEN_QUEUE_EMPTY) 140260894Smsmith return(0); 140360894Smsmith twe_enqueue_aen(sc, aen); 140460894Smsmith } 140560894Smsmith} 140660894Smsmith 140760894Smsmith/******************************************************************************** 140860894Smsmith * Push an AEN that we've received onto the queue. 140960894Smsmith * 141060894Smsmith * Note that we have to lock this against reentrance, since it may be called 141160894Smsmith * from both interrupt and non-interrupt context. 141260894Smsmith * 141360894Smsmith * If someone is waiting for the AEN we have, wake them up. 141460894Smsmith */ 141560894Smsmithstatic void 141660894Smsmithtwe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) 141760894Smsmith{ 141867555Smsmith char *msg; 141967555Smsmith int s, next, nextnext; 142060894Smsmith 142160894Smsmith debug_called(4); 142260894Smsmith 142367555Smsmith if ((msg = twe_format_aen(sc, aen)) != NULL) 142467555Smsmith twe_printf(sc, "AEN: <%s>\n", msg); 142560894Smsmith 142660894Smsmith s = splbio(); 142760894Smsmith /* enqueue the AEN */ 142860894Smsmith next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); 142967555Smsmith nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); 143067555Smsmith 143167555Smsmith /* check to see if this is the last free slot, and subvert the AEN if it is */ 143267555Smsmith if (nextnext == sc->twe_aen_tail) 143367555Smsmith aen = TWE_AEN_QUEUE_FULL; 143467555Smsmith 143567555Smsmith /* look to see if there's room for this AEN */ 143660894Smsmith if (next != sc->twe_aen_tail) { 143760894Smsmith sc->twe_aen_queue[sc->twe_aen_head] = aen; 143860894Smsmith sc->twe_aen_head = next; 143960894Smsmith } 144060894Smsmith 144167555Smsmith /* wake up anyone asleep on the queue */ 144267555Smsmith wakeup(&sc->twe_aen_queue); 144367555Smsmith 144460894Smsmith /* anyone looking for this AEN? */ 144560894Smsmith if (sc->twe_wait_aen == aen) { 144660894Smsmith sc->twe_wait_aen = -1; 144760894Smsmith wakeup(&sc->twe_wait_aen); 144860894Smsmith } 144960894Smsmith splx(s); 145060894Smsmith} 145160894Smsmith 145260894Smsmith/******************************************************************************** 145360894Smsmith * Pop an AEN off the queue, or return -1 if there are none left. 145460894Smsmith * 145560894Smsmith * We are more or less interrupt-safe, so don't block interrupts. 145660894Smsmith */ 1457123103Spsstatic u_int16_t 145860894Smsmithtwe_dequeue_aen(struct twe_softc *sc) 145960894Smsmith{ 1460123103Sps u_int16_t result; 146160894Smsmith 146260894Smsmith debug_called(4); 146360894Smsmith 146460894Smsmith if (sc->twe_aen_tail == sc->twe_aen_head) { 1465118508Sps result = TWE_AEN_QUEUE_EMPTY; 146660894Smsmith } else { 146760894Smsmith result = sc->twe_aen_queue[sc->twe_aen_tail]; 146860894Smsmith sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); 146960894Smsmith } 147060894Smsmith return(result); 147160894Smsmith} 147260894Smsmith 147360894Smsmith/******************************************************************************** 147460894Smsmith * Check to see if the requested AEN is in the queue. 147560894Smsmith * 147660894Smsmith * XXX we could probably avoid masking interrupts here 147760894Smsmith */ 147860894Smsmithstatic int 147960894Smsmithtwe_find_aen(struct twe_softc *sc, u_int16_t aen) 148060894Smsmith{ 148160894Smsmith int i, s, missing; 148260894Smsmith 148360894Smsmith missing = 1; 148460894Smsmith s = splbio(); 148560894Smsmith for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { 148660894Smsmith if (sc->twe_aen_queue[i] == aen) 148760894Smsmith missing = 0; 148860894Smsmith } 148967555Smsmith splx(s); 149060894Smsmith return(missing); 149160894Smsmith} 149260894Smsmith 149360894Smsmith 149460894Smsmith#if 0 /* currently unused */ 149560894Smsmith/******************************************************************************** 149660894Smsmith * Sleep waiting for at least (timeout) seconds until we see (aen) as 149760894Smsmith * requested. Returns nonzero on timeout or failure. 149860894Smsmith * 149960894Smsmith * XXX: this should not be used in cases where there may be more than one sleeper 150060894Smsmith * without a mechanism for registering multiple sleepers. 150160894Smsmith */ 150260894Smsmithstatic int 150360894Smsmithtwe_wait_aen(struct twe_softc *sc, int aen, int timeout) 150460894Smsmith{ 150560894Smsmith time_t expiry; 150660894Smsmith int found, s; 150760894Smsmith 150860894Smsmith debug_called(4); 150960894Smsmith 151060894Smsmith expiry = time_second + timeout; 151160894Smsmith found = 0; 151260894Smsmith 151360894Smsmith s = splbio(); 151460894Smsmith sc->twe_wait_aen = aen; 151560894Smsmith do { 151660894Smsmith twe_fetch_aen(sc); 151760894Smsmith tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); 151860894Smsmith if (sc->twe_wait_aen == -1) 151960894Smsmith found = 1; 152060894Smsmith } while ((time_second <= expiry) && !found); 152160894Smsmith splx(s); 152260894Smsmith return(!found); 152360894Smsmith} 152460894Smsmith#endif 152560894Smsmith 152660894Smsmith/******************************************************************************** 152760894Smsmith ******************************************************************************** 152860894Smsmith Command Buffer Management 152960894Smsmith ******************************************************************************** 153060894Smsmith ********************************************************************************/ 153160894Smsmith 153260894Smsmith/******************************************************************************** 153360894Smsmith * Get a new command buffer. 153460894Smsmith * 153567555Smsmith * This will return NULL if all command buffers are in use. 153660894Smsmith */ 153767555Smsmithstatic int 153867555Smsmithtwe_get_request(struct twe_softc *sc, struct twe_request **tr) 153960894Smsmith{ 1540118816Sps TWE_Command *cmd; 154160894Smsmith debug_called(4); 154260894Smsmith 154360894Smsmith /* try to reuse an old buffer */ 154467555Smsmith *tr = twe_dequeue_free(sc); 154560894Smsmith 154667555Smsmith /* initialise some fields to their defaults */ 154767555Smsmith if (*tr != NULL) { 1548118816Sps cmd = TWE_FIND_COMMAND(*tr); 154967555Smsmith (*tr)->tr_data = NULL; 155069543Smsmith (*tr)->tr_private = NULL; 155167555Smsmith (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ 155267555Smsmith (*tr)->tr_flags = 0; 155367555Smsmith (*tr)->tr_complete = NULL; 1554118816Sps cmd->generic.status = 0; /* before submission to controller */ 1555118816Sps cmd->generic.flags = 0; /* not used */ 155660894Smsmith } 155767555Smsmith return(*tr == NULL); 155860894Smsmith} 155960894Smsmith 156060894Smsmith/******************************************************************************** 156167555Smsmith * Release a command buffer for reuse. 156260894Smsmith * 156360894Smsmith */ 156460894Smsmithstatic void 156560894Smsmithtwe_release_request(struct twe_request *tr) 156660894Smsmith{ 156760894Smsmith debug_called(4); 156860894Smsmith 156969543Smsmith if (tr->tr_private != NULL) 157069543Smsmith twe_panic(tr->tr_sc, "tr_private != NULL"); 157167555Smsmith twe_enqueue_free(tr); 157260894Smsmith} 157360894Smsmith 157460894Smsmith/******************************************************************************** 157567555Smsmith ******************************************************************************** 157667555Smsmith Debugging 157767555Smsmith ******************************************************************************** 157867555Smsmith ********************************************************************************/ 157960894Smsmith 158060894Smsmith/******************************************************************************** 158167555Smsmith * Print some information about the controller 158260894Smsmith */ 158367555Smsmithvoid 158467555Smsmithtwe_describe_controller(struct twe_softc *sc) 158560894Smsmith{ 158667555Smsmith TWE_Param *p[6]; 158767555Smsmith u_int8_t ports; 158867555Smsmith u_int32_t size; 158960894Smsmith int i; 159060894Smsmith 159167555Smsmith debug_called(2); 159260894Smsmith 159367555Smsmith /* get the port count */ 159467555Smsmith twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); 159560894Smsmith 159667555Smsmith /* get version strings */ 1597123103Sps p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); 1598123103Sps p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); 1599123103Sps if (p[0] && p[1]) 1600123103Sps twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[0]->data, p[1]->data); 160160894Smsmith 1602123103Sps if (bootverbose) { 1603123103Sps p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); 1604123103Sps p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); 1605123103Sps p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); 1606123103Sps p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); 160760894Smsmith 1608123103Sps if (p[2] && p[3] && p[4] && p[5]) 1609123103Sps twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[2]->data, p[3]->data, 1610123103Sps p[4]->data, p[5]->data); 1611123103Sps if (p[2]) 1612123103Sps free(p[2], M_DEVBUF); 1613123103Sps if (p[3]) 1614123103Sps free(p[3], M_DEVBUF); 1615123103Sps if (p[4]) 1616123103Sps free(p[4], M_DEVBUF); 1617123103Sps if (p[5]) 1618123103Sps free(p[5], M_DEVBUF); 1619123103Sps } 1620123103Sps if (p[0]) 1621123103Sps free(p[0], M_DEVBUF); 1622123103Sps if (p[1]) 1623123103Sps free(p[1], M_DEVBUF); 1624123103Sps 162567555Smsmith /* print attached drives */ 162667555Smsmith if (bootverbose) { 162767555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); 162867555Smsmith for (i = 0; i < ports; i++) { 162967555Smsmith if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) 163067555Smsmith continue; 163167555Smsmith twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); 163267555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); 163367555Smsmith if (p[1] != NULL) { 163467555Smsmith twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); 163567555Smsmith free(p[1], M_DEVBUF); 163667555Smsmith } else { 163767555Smsmith twe_printf(sc, "port %d, drive status unavailable\n", i); 163867555Smsmith } 163960894Smsmith } 1640123103Sps if (p[0]) 1641123103Sps free(p[0], M_DEVBUF); 164260894Smsmith } 164360894Smsmith} 164460894Smsmith 164560894Smsmith/******************************************************************************** 1646123103Sps * Look up a text description of a numeric code and return a pointer to same. 1647123103Sps */ 1648123103Spschar * 1649123103Spstwe_describe_code(struct twe_code_lookup *table, u_int32_t code) 1650123103Sps{ 1651123103Sps int i; 1652123103Sps 1653123103Sps for (i = 0; table[i].string != NULL; i++) 1654123103Sps if (table[i].code == code) 1655123103Sps return(table[i].string); 1656123103Sps return(table[i+1].string); 1657123103Sps} 1658123103Sps 1659123103Sps/******************************************************************************** 166060894Smsmith * Complain if the status bits aren't what we're expecting. 166167555Smsmith * 166267555Smsmith * Rate-limit the complaints to at most one of each every five seconds, but 166367555Smsmith * always return the correct status. 166460894Smsmith */ 166560894Smsmithstatic int 166660894Smsmithtwe_check_bits(struct twe_softc *sc, u_int32_t status_reg) 166760894Smsmith{ 166867555Smsmith int result; 166967555Smsmith static time_t lastwarn[2] = {0, 0}; 167060894Smsmith 167167555Smsmith /* 167267555Smsmith * This can be a little problematic, as twe_panic may call twe_reset if 167367555Smsmith * TWE_DEBUG is not set, which will call us again as part of the soft reset. 167467555Smsmith */ 167567555Smsmith if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { 167667555Smsmith twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, 167767555Smsmith TWE_STATUS_BITS_DESCRIPTION); 167867555Smsmith twe_panic(sc, "fatal status bits"); 167967555Smsmith } 168067555Smsmith 168160894Smsmith result = 0; 168260894Smsmith if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { 168367555Smsmith if (time_second > (lastwarn[0] + 5)) { 168467555Smsmith twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, 168567555Smsmith TWE_STATUS_BITS_DESCRIPTION); 168667555Smsmith lastwarn[0] = time_second; 168767555Smsmith } 168860894Smsmith result = 1; 168960894Smsmith } 169060894Smsmith 169160894Smsmith if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { 169267555Smsmith if (time_second > (lastwarn[1] + 5)) { 169367555Smsmith twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, 169467555Smsmith TWE_STATUS_BITS_DESCRIPTION); 169567555Smsmith lastwarn[1] = time_second; 169667555Smsmith } 169760894Smsmith result = 1; 169891790Smsmith if (status_reg & TWE_STATUS_PCI_PARITY_ERROR) { 1699176200Sbrueffer twe_printf(sc, "PCI parity error: Reseat card, move card or buggy device present.\n"); 170091790Smsmith twe_clear_pci_parity_error(sc); 170191790Smsmith } 170291790Smsmith if (status_reg & TWE_STATUS_PCI_ABORT) { 1703118816Sps twe_printf(sc, "PCI abort, clearing.\n"); 170491790Smsmith twe_clear_pci_abort(sc); 170591790Smsmith } 170660894Smsmith } 170767555Smsmith 170860894Smsmith return(result); 170960894Smsmith} 171060894Smsmith 171160894Smsmith/******************************************************************************** 171267555Smsmith * Return a string describing (aen). 171367555Smsmith * 171467555Smsmith * The low 8 bits of the aen are the code, the high 8 bits give the unit number 171567555Smsmith * where an AEN is specific to a unit. 171667555Smsmith * 171767555Smsmith * Note that we could expand this routine to handle eg. up/downgrading the status 171867555Smsmith * of a drive if we had some idea of what the drive's initial status was. 171960894Smsmith */ 172060894Smsmith 172160894Smsmithstatic char * 172267555Smsmithtwe_format_aen(struct twe_softc *sc, u_int16_t aen) 172360894Smsmith{ 172467555Smsmith static char buf[80]; 172567555Smsmith device_t child; 172667555Smsmith char *code, *msg; 172760894Smsmith 172867555Smsmith code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); 172967555Smsmith msg = code + 2; 173060894Smsmith 173167555Smsmith switch (*code) { 173267555Smsmith case 'q': 173367555Smsmith if (!bootverbose) 173467555Smsmith return(NULL); 173567555Smsmith /* FALLTHROUGH */ 173691790Smsmith case 'a': 173767555Smsmith return(msg); 173867555Smsmith 173967555Smsmith case 'c': 174067555Smsmith if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { 174167555Smsmith sprintf(buf, "twed%d: %s", device_get_unit(child), msg); 174267555Smsmith } else { 174367555Smsmith sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), 174467555Smsmith msg, TWE_AEN_UNIT(aen)); 174567555Smsmith } 174667555Smsmith return(buf); 174791790Smsmith 174891790Smsmith case 'p': 174991790Smsmith sprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen), 175091790Smsmith msg); 175191790Smsmith return(buf); 175291790Smsmith 175367555Smsmith 175467555Smsmith case 'x': 175567555Smsmith default: 175667555Smsmith break; 175767555Smsmith } 175867555Smsmith sprintf(buf, "unknown AEN 0x%x", aen); 175960894Smsmith return(buf); 176060894Smsmith} 176160894Smsmith 176269543Smsmith/******************************************************************************** 176369543Smsmith * Print a diagnostic if the status of the command warrants it, and return 176469543Smsmith * either zero (command was ok) or nonzero (command failed). 176569543Smsmith */ 176667555Smsmithstatic int 176769543Smsmithtwe_report_request(struct twe_request *tr) 176867555Smsmith{ 176969543Smsmith struct twe_softc *sc = tr->tr_sc; 1770118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 177176340Smsmith int result = 0; 177260894Smsmith 177376340Smsmith /* 177476340Smsmith * Check the command status value and handle accordingly. 177576340Smsmith */ 177676340Smsmith if (cmd->generic.status == TWE_STATUS_RESET) { 177776340Smsmith /* 177876340Smsmith * The status code 0xff requests a controller reset. 177976340Smsmith */ 1780123103Sps twe_printf(sc, "command returned with controller reset request\n"); 178176340Smsmith twe_reset(sc); 178269543Smsmith result = 1; 178376340Smsmith } else if (cmd->generic.status > TWE_STATUS_FATAL) { 178469543Smsmith /* 178576340Smsmith * Fatal errors that don't require controller reset. 178691790Smsmith * 178791790Smsmith * We know a few special flags values. 178869543Smsmith */ 178991790Smsmith switch (cmd->generic.flags) { 179091790Smsmith case 0x1b: 179191790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 179291790Smsmith "drive timeout"); 179391790Smsmith break; 179491790Smsmith case 0x51: 179591790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 179691790Smsmith "unrecoverable drive error"); 179791790Smsmith break; 179891790Smsmith default: 179991790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 180091790Smsmith "controller error - %s (flags = 0x%x)\n", 180191790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 180291790Smsmith cmd->generic.flags); 180391790Smsmith result = 1; 180491790Smsmith } 180576340Smsmith } else if (cmd->generic.status > TWE_STATUS_WARNING) { 180676340Smsmith /* 180776340Smsmith * Warning level status. 180876340Smsmith */ 180991790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 181091790Smsmith "warning - %s (flags = 0x%x)\n", 181191790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 181291790Smsmith cmd->generic.flags); 181376340Smsmith } else if (cmd->generic.status > 0x40) { 181476340Smsmith /* 181576340Smsmith * Info level status. 181676340Smsmith */ 181791790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 181891790Smsmith "attention - %s (flags = 0x%x)\n", 181991790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 182091790Smsmith cmd->generic.flags); 182167555Smsmith } 182276340Smsmith 182369543Smsmith return(result); 182467555Smsmith} 182567555Smsmith 182669543Smsmith/******************************************************************************** 182769543Smsmith * Print some controller state to aid in debugging error/panic conditions 182869543Smsmith */ 182967555Smsmithvoid 183067555Smsmithtwe_print_controller(struct twe_softc *sc) 183160894Smsmith{ 183267555Smsmith u_int32_t status_reg; 183360894Smsmith 183467555Smsmith status_reg = TWE_STATUS(sc); 183567555Smsmith twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); 1836123103Sps twe_printf(sc, " current max min\n"); 1837123103Sps twe_printf(sc, "free %04d %04d %04d\n", 1838123103Sps sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max, sc->twe_qstat[TWEQ_FREE].q_min); 1839123103Sps 1840123103Sps twe_printf(sc, "ready %04d %04d %04d\n", 1841123103Sps sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max, sc->twe_qstat[TWEQ_READY].q_min); 1842123103Sps 1843123103Sps twe_printf(sc, "busy %04d %04d %04d\n", 1844123103Sps sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max, sc->twe_qstat[TWEQ_BUSY].q_min); 1845123103Sps 1846123103Sps twe_printf(sc, "complete %04d %04d %04d\n", 1847123103Sps sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max, sc->twe_qstat[TWEQ_COMPLETE].q_min); 1848123103Sps 1849123103Sps twe_printf(sc, "bioq %04d %04d %04d\n", 1850123103Sps sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max, sc->twe_qstat[TWEQ_BIO].q_min); 1851123103Sps 185267555Smsmith twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); 185367555Smsmith} 185460894Smsmith 185567555Smsmithstatic void 185667555Smsmithtwe_panic(struct twe_softc *sc, char *reason) 185767555Smsmith{ 185867555Smsmith twe_print_controller(sc); 185967555Smsmith#ifdef TWE_DEBUG 186067555Smsmith panic(reason); 186167555Smsmith#else 186267555Smsmith twe_reset(sc); 186367555Smsmith#endif 186460894Smsmith} 186560894Smsmith 1866127415Svkashyap#if 0 186760894Smsmith/******************************************************************************** 186860894Smsmith * Print a request/command in human-readable format. 186960894Smsmith */ 187060894Smsmithstatic void 187160894Smsmithtwe_print_request(struct twe_request *tr) 187260894Smsmith{ 187367555Smsmith struct twe_softc *sc = tr->tr_sc; 1874118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 187560894Smsmith int i; 187660894Smsmith 187767555Smsmith twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", 187867555Smsmith cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, 187967555Smsmith cmd->generic.unit, cmd->generic.host_id); 188067555Smsmith twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", 188167555Smsmith cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); 188267555Smsmith 188367555Smsmith switch(cmd->generic.opcode) { /* XXX add more opcodes? */ 188467555Smsmith case TWE_OP_READ: 188567555Smsmith case TWE_OP_WRITE: 188667555Smsmith twe_printf(sc, " lba %d\n", cmd->io.lba); 188767555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) 188867555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 188967555Smsmith i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); 189060894Smsmith break; 189160894Smsmith 189267555Smsmith case TWE_OP_GET_PARAM: 189367555Smsmith case TWE_OP_SET_PARAM: 189467555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) 189567555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 189667555Smsmith i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); 189760894Smsmith break; 189860894Smsmith 189967555Smsmith case TWE_OP_INIT_CONNECTION: 190067555Smsmith twe_printf(sc, " response queue pointer 0x%x\n", 190167555Smsmith cmd->initconnection.response_queue_pointer); 190267555Smsmith break; 190367555Smsmith 190460894Smsmith default: 190567555Smsmith break; 190660894Smsmith } 190767555Smsmith twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", 1908118816Sps tr, TWE_FIND_COMMANDPHYS(tr), tr->tr_data, tr->tr_dataphys, tr->tr_length); 190967555Smsmith twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", 191067555Smsmith tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); 191160894Smsmith} 191260894Smsmith 191360894Smsmith#endif 1914