twe.c revision 127415
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 127415 2004-03-25 19:30:35Z vkashyap $ 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 434127415Svkashyap /* try to map and submit the command to controller */ 435118816Sps error = twe_map_request(tr); 436127415Svkashyap 43769543Smsmith if (error != 0) { 438127415Svkashyap tr->tr_status = TWE_CMD_ERROR; 439127415Svkashyap if (tr->tr_private != NULL) { 440127415Svkashyap bp = (twe_bio *)(tr->tr_private); 441127415Svkashyap TWE_BIO_SET_ERROR(bp, error); 442127415Svkashyap tr->tr_private = NULL; 443127415Svkashyap twed_intr(bp); 444127415Svkashyap twe_release_request(tr); 445127415Svkashyap } else if (tr->tr_flags & TWE_CMD_SLEEPER) 446127415Svkashyap wakeup_one(tr); /* wakeup the sleeping owner */ 44769543Smsmith } 44869543Smsmith } 44960894Smsmith} 45060894Smsmith 45160894Smsmith/******************************************************************************** 45269543Smsmith * Write blocks from memory to disk, for system crash dumps. 45369543Smsmith */ 45469543Smsmithint 45569543Smsmithtwe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) 45669543Smsmith{ 45769543Smsmith struct twe_request *tr; 45869543Smsmith TWE_Command *cmd; 45969543Smsmith int error; 46069543Smsmith 46169543Smsmith if (twe_get_request(sc, &tr)) 46269543Smsmith return(ENOMEM); 46369543Smsmith 46469543Smsmith tr->tr_data = data; 46569543Smsmith tr->tr_status = TWE_CMD_SETUP; 46669543Smsmith tr->tr_length = nblks * TWE_BLOCK_SIZE; 46769543Smsmith tr->tr_flags = TWE_CMD_DATAOUT; 46869543Smsmith 469118816Sps cmd = TWE_FIND_COMMAND(tr); 47069543Smsmith cmd->io.opcode = TWE_OP_WRITE; 47169543Smsmith cmd->io.size = 3; 47269543Smsmith cmd->io.unit = unit; 47369543Smsmith cmd->io.block_count = nblks; 47469543Smsmith cmd->io.lba = lba; 47569543Smsmith 47669543Smsmith error = twe_immediate_request(tr); 47769543Smsmith if (error == 0) 47869543Smsmith if (twe_report_request(tr)) 47969543Smsmith error = EIO; 48069543Smsmith twe_release_request(tr); 48169543Smsmith return(error); 48269543Smsmith} 48369543Smsmith 48469543Smsmith/******************************************************************************** 48567555Smsmith * Handle controller-specific control operations. 48660894Smsmith */ 48767555Smsmithint 488118816Spstwe_ioctl(struct twe_softc *sc, int ioctlcmd, void *addr) 48960894Smsmith{ 49067555Smsmith struct twe_usercommand *tu = (struct twe_usercommand *)addr; 49167555Smsmith struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; 492118508Sps struct twe_drivecommand *td = (struct twe_drivecommand *)addr; 49367555Smsmith union twe_statrequest *ts = (union twe_statrequest *)addr; 49467555Smsmith TWE_Param *param; 495118816Sps TWE_Command *cmd; 49667555Smsmith void *data; 497123103Sps u_int16_t *aen_code = (u_int16_t *)addr; 49867555Smsmith struct twe_request *tr; 49969543Smsmith u_int8_t srid; 50067555Smsmith int s, error; 50160894Smsmith 50267555Smsmith error = 0; 503118816Sps switch(ioctlcmd) { 50467555Smsmith /* handle a command from userspace */ 50567555Smsmith case TWEIO_COMMAND: 50667555Smsmith /* get a request */ 507118508Sps while (twe_get_request(sc, &tr)) 508119124Sps tsleep(sc, PPAUSE, "twioctl", hz); 50967555Smsmith 51069543Smsmith /* 51169543Smsmith * Save the command's request ID, copy the user-supplied command in, 51269543Smsmith * restore the request ID. 51369543Smsmith */ 514118816Sps cmd = TWE_FIND_COMMAND(tr); 515118816Sps srid = cmd->generic.request_id; 516118816Sps bcopy(&tu->tu_command, cmd, sizeof(TWE_Command)); 517118816Sps cmd->generic.request_id = srid; 51867555Smsmith 519118508Sps /* 520118508Sps * if there's a data buffer, allocate and copy it in. 521118508Sps * Must be in multipled of 512 bytes. 522118508Sps */ 523118508Sps tr->tr_length = (tu->tu_size + 511) & ~511; 52467555Smsmith if (tr->tr_length > 0) { 525111119Simp if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { 52667555Smsmith error = ENOMEM; 52767555Smsmith goto cmd_done; 52867555Smsmith } 529118508Sps if ((error = copyin(tu->tu_data, tr->tr_data, tu->tu_size)) != 0) 53067555Smsmith goto cmd_done; 53167555Smsmith tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 53267555Smsmith } 53367555Smsmith 53467555Smsmith /* run the command */ 535127415Svkashyap error = twe_wait_request(tr); 536127415Svkashyap if (error) 537127415Svkashyap goto cmd_done; 53867555Smsmith 53967555Smsmith /* copy the command out again */ 540118816Sps bcopy(cmd, &tu->tu_command, sizeof(TWE_Command)); 54167555Smsmith 54267555Smsmith /* if there was a data buffer, copy it out */ 54367555Smsmith if (tr->tr_length > 0) 544118508Sps error = copyout(tr->tr_data, tu->tu_data, tu->tu_size); 54567555Smsmith 54667555Smsmith cmd_done: 54767555Smsmith /* free resources */ 54867555Smsmith if (tr->tr_data != NULL) 54967555Smsmith free(tr->tr_data, M_DEVBUF); 55067555Smsmith if (tr != NULL) 55167555Smsmith twe_release_request(tr); 55267555Smsmith 55367555Smsmith break; 55467555Smsmith 55567555Smsmith /* fetch statistics counter */ 55667555Smsmith case TWEIO_STATS: 55767555Smsmith switch (ts->ts_item) { 55867555Smsmith#ifdef TWE_PERFORMANCE_MONITOR 55967555Smsmith case TWEQ_FREE: 56067555Smsmith case TWEQ_BIO: 56167555Smsmith case TWEQ_READY: 56267555Smsmith case TWEQ_BUSY: 56367555Smsmith case TWEQ_COMPLETE: 56467555Smsmith bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); 56567555Smsmith break; 56667555Smsmith#endif 56767555Smsmith default: 56867555Smsmith error = ENOENT; 56967555Smsmith break; 57067555Smsmith } 57167555Smsmith break; 57267555Smsmith 57367555Smsmith /* poll for an AEN */ 57467555Smsmith case TWEIO_AEN_POLL: 575123103Sps *aen_code = twe_dequeue_aen(sc); 57667555Smsmith break; 57767555Smsmith 57867555Smsmith /* wait for another AEN to show up */ 57967555Smsmith case TWEIO_AEN_WAIT: 58067555Smsmith s = splbio(); 581123103Sps while ((*aen_code = twe_dequeue_aen(sc)) == TWE_AEN_QUEUE_EMPTY) { 58267555Smsmith error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); 58367555Smsmith if (error == EINTR) 58467555Smsmith break; 58567555Smsmith } 58667555Smsmith splx(s); 58767555Smsmith break; 58867555Smsmith 58967555Smsmith case TWEIO_GET_PARAM: 59067555Smsmith if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { 59167555Smsmith twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", 59267555Smsmith tp->tp_table_id, tp->tp_param_id, tp->tp_size); 59367555Smsmith error = EINVAL; 59467555Smsmith } else { 59567555Smsmith if (param->parameter_size_bytes > tp->tp_size) { 59667555Smsmith twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", 59767555Smsmith param->parameter_size_bytes, tp->tp_size); 59867555Smsmith error = EFAULT; 59967555Smsmith } else { 60067555Smsmith error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); 60167555Smsmith } 60267555Smsmith free(param, M_DEVBUF); 60367555Smsmith } 60467555Smsmith break; 60567555Smsmith 60667555Smsmith case TWEIO_SET_PARAM: 607111119Simp if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { 60867555Smsmith error = ENOMEM; 60967555Smsmith } else { 61067555Smsmith error = copyin(tp->tp_data, data, tp->tp_size); 61167555Smsmith if (error == 0) 61267555Smsmith error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); 61367555Smsmith free(data, M_DEVBUF); 61467555Smsmith } 61567555Smsmith break; 61667555Smsmith 61767555Smsmith case TWEIO_RESET: 61867555Smsmith twe_reset(sc); 61967555Smsmith break; 62067555Smsmith 621118508Sps case TWEIO_ADD_UNIT: 622123103Sps error = twe_add_unit(sc, td->td_unit); 623118508Sps break; 624118508Sps 625118508Sps case TWEIO_DEL_UNIT: 626123103Sps error = twe_del_unit(sc, td->td_unit); 627118508Sps break; 628118508Sps 62991790Smsmith /* XXX implement ATA PASSTHROUGH */ 63091790Smsmith 63167555Smsmith /* nothing we understand */ 63267555Smsmith default: 63367555Smsmith error = ENOTTY; 63467555Smsmith } 63567555Smsmith 63667555Smsmith return(error); 63760894Smsmith} 63860894Smsmith 63960894Smsmith/******************************************************************************** 64067555Smsmith * Enable the useful interrupts from the controller. 64160894Smsmith */ 64267555Smsmithvoid 64367555Smsmithtwe_enable_interrupts(struct twe_softc *sc) 64460894Smsmith{ 64567555Smsmith sc->twe_state |= TWE_STATE_INTEN; 64667555Smsmith TWE_CONTROL(sc, 64767555Smsmith TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | 64867555Smsmith TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | 64967555Smsmith TWE_CONTROL_ENABLE_INTERRUPTS); 65060894Smsmith} 65160894Smsmith 65260894Smsmith/******************************************************************************** 65367555Smsmith * Disable interrupts from the controller. 65467555Smsmith */ 65567555Smsmithvoid 65667555Smsmithtwe_disable_interrupts(struct twe_softc *sc) 65767555Smsmith{ 65867555Smsmith TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); 65967555Smsmith sc->twe_state &= ~TWE_STATE_INTEN; 66067555Smsmith} 66167555Smsmith 66267555Smsmith/******************************************************************************** 66360894Smsmith ******************************************************************************** 66460894Smsmith Command Submission 66560894Smsmith ******************************************************************************** 66660894Smsmith ********************************************************************************/ 66760894Smsmith 66867555Smsmith/******************************************************************************** 66967555Smsmith * Read integer parameter table entries. 67060894Smsmith */ 67167555Smsmithstatic int 67267555Smsmithtwe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) 67360894Smsmith{ 67467555Smsmith TWE_Param *param; 67560894Smsmith 67667555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) 67767555Smsmith return(ENOENT); 67867555Smsmith *result = *(u_int8_t *)param->data; 67967555Smsmith free(param, M_DEVBUF); 68067555Smsmith return(0); 68167555Smsmith} 68260894Smsmith 68367555Smsmithstatic int 68467555Smsmithtwe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) 68567555Smsmith{ 68667555Smsmith TWE_Param *param; 68767555Smsmith 68867555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) 68967555Smsmith return(ENOENT); 69067555Smsmith *result = *(u_int16_t *)param->data; 69167555Smsmith free(param, M_DEVBUF); 69260894Smsmith return(0); 69360894Smsmith} 69460894Smsmith 69567555Smsmithstatic int 69667555Smsmithtwe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) 69767555Smsmith{ 69867555Smsmith TWE_Param *param; 69967555Smsmith 70067555Smsmith if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) 70167555Smsmith return(ENOENT); 70267555Smsmith *result = *(u_int32_t *)param->data; 70367555Smsmith free(param, M_DEVBUF); 70467555Smsmith return(0); 70567555Smsmith} 70667555Smsmith 70760894Smsmith/******************************************************************************** 70860894Smsmith * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it 70960894Smsmith * will be called with the command when it's completed. If no callback is 71060894Smsmith * provided, we will wait for the command to complete and then return just the data. 71160894Smsmith * The caller is responsible for freeing the data when done with it. 71260894Smsmith */ 71360894Smsmithstatic void * 71467555Smsmithtwe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, 71567555Smsmith void (* func)(struct twe_request *tr)) 71660894Smsmith{ 71760894Smsmith struct twe_request *tr; 71860894Smsmith TWE_Command *cmd; 71960894Smsmith TWE_Param *param; 72060894Smsmith int error; 72160894Smsmith 72260894Smsmith debug_called(4); 72360894Smsmith 72460894Smsmith tr = NULL; 72560894Smsmith param = NULL; 72660894Smsmith 72760894Smsmith /* get a command */ 72867555Smsmith if (twe_get_request(sc, &tr)) 72960894Smsmith goto err; 73060894Smsmith 73160894Smsmith /* get a buffer */ 73260894Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 73360894Smsmith goto err; 73460894Smsmith tr->tr_data = param; 73560894Smsmith tr->tr_length = TWE_SECTOR_SIZE; 73660894Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 73760894Smsmith 73860894Smsmith /* build the command for the controller */ 739118816Sps cmd = TWE_FIND_COMMAND(tr); 74067555Smsmith cmd->param.opcode = TWE_OP_GET_PARAM; 74167555Smsmith cmd->param.size = 2; 74267555Smsmith cmd->param.unit = 0; 74367555Smsmith cmd->param.param_count = 1; 74460894Smsmith 74560894Smsmith /* fill in the outbound parameter data */ 74660894Smsmith param->table_id = table_id; 74767555Smsmith param->parameter_id = param_id; 74867555Smsmith param->parameter_size_bytes = param_size; 74960894Smsmith 75060894Smsmith /* submit the command and either wait or let the callback handle it */ 75160894Smsmith if (func == NULL) { 75260894Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 75360894Smsmith error = twe_immediate_request(tr); 75460894Smsmith if (error == 0) { 75569543Smsmith if (twe_report_request(tr)) 75660894Smsmith goto err; 757123103Sps } else { 758123103Sps goto err; 75960894Smsmith } 76067555Smsmith twe_release_request(tr); 76167555Smsmith return(param); 76260894Smsmith } else { 76360894Smsmith tr->tr_complete = func; 764118816Sps error = twe_map_request(tr); 76560894Smsmith if (error == 0) 76660894Smsmith return(func); 76760894Smsmith } 76860894Smsmith 76960894Smsmith /* something failed */ 77060894Smsmitherr: 77160894Smsmith debug(1, "failed"); 77260894Smsmith if (tr != NULL) 77360894Smsmith twe_release_request(tr); 77460894Smsmith if (param != NULL) 77560894Smsmith free(param, M_DEVBUF); 77660894Smsmith return(NULL); 77760894Smsmith} 77860894Smsmith 77960894Smsmith/******************************************************************************** 78067555Smsmith * Set integer parameter table entries. 78167555Smsmith */ 78291449Speter#ifdef TWE_SHUTDOWN_NOTIFICATION 78367555Smsmithstatic int 78467555Smsmithtwe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) 78567555Smsmith{ 78667555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 78767555Smsmith} 78891449Speter#endif 78967555Smsmith 79091449Speter#if 0 79167555Smsmithstatic int 79267555Smsmithtwe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) 79367555Smsmith{ 79467555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 79567555Smsmith} 79667555Smsmith 79767555Smsmithstatic int 79867555Smsmithtwe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) 79967555Smsmith{ 80067555Smsmith return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); 80167555Smsmith} 80291449Speter#endif 80367555Smsmith 80467555Smsmith/******************************************************************************** 80567555Smsmith * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. 80667555Smsmith */ 80767555Smsmithstatic int 80867555Smsmithtwe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) 80967555Smsmith{ 81067555Smsmith struct twe_request *tr; 81167555Smsmith TWE_Command *cmd; 81267555Smsmith TWE_Param *param; 81367555Smsmith int error; 81467555Smsmith 81567555Smsmith debug_called(4); 81667555Smsmith 81767555Smsmith tr = NULL; 81867555Smsmith param = NULL; 81967555Smsmith error = ENOMEM; 82067555Smsmith 82167555Smsmith /* get a command */ 82267555Smsmith if (twe_get_request(sc, &tr)) 82367555Smsmith goto out; 82467555Smsmith 82567555Smsmith /* get a buffer */ 82667555Smsmith if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) 82767555Smsmith goto out; 82867555Smsmith tr->tr_data = param; 82967555Smsmith tr->tr_length = TWE_SECTOR_SIZE; 83067555Smsmith tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; 83167555Smsmith 83267555Smsmith /* build the command for the controller */ 833118816Sps cmd = TWE_FIND_COMMAND(tr); 83467555Smsmith cmd->param.opcode = TWE_OP_SET_PARAM; 83567555Smsmith cmd->param.size = 2; 83667555Smsmith cmd->param.unit = 0; 83767555Smsmith cmd->param.param_count = 1; 83867555Smsmith 83967555Smsmith /* fill in the outbound parameter data */ 84067555Smsmith param->table_id = table_id; 84167555Smsmith param->parameter_id = param_id; 84267555Smsmith param->parameter_size_bytes = param_size; 84367555Smsmith bcopy(data, param->data, param_size); 84467555Smsmith 84567555Smsmith /* XXX could use twe_wait_request here if interrupts were enabled? */ 84667555Smsmith error = twe_immediate_request(tr); 84767555Smsmith if (error == 0) { 84869543Smsmith if (twe_report_request(tr)) 84967555Smsmith error = EIO; 85067555Smsmith } 85167555Smsmith 85267555Smsmithout: 85367555Smsmith if (tr != NULL) 85467555Smsmith twe_release_request(tr); 85567555Smsmith if (param != NULL) 85667555Smsmith free(param, M_DEVBUF); 85767555Smsmith return(error); 85867555Smsmith} 85967555Smsmith 86067555Smsmith/******************************************************************************** 86160894Smsmith * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. 86260894Smsmith * 86360894Smsmith * Typically called with interrupts disabled. 86460894Smsmith */ 86560894Smsmithstatic int 86667555Smsmithtwe_init_connection(struct twe_softc *sc, int mode) 86760894Smsmith{ 86860894Smsmith struct twe_request *tr; 86960894Smsmith TWE_Command *cmd; 87060894Smsmith int error; 87160894Smsmith 87260894Smsmith debug_called(4); 87360894Smsmith 87460894Smsmith /* get a command */ 87567555Smsmith if (twe_get_request(sc, &tr)) 876102291Sarchie return(0); 87760894Smsmith 87860894Smsmith /* build the command */ 879118816Sps cmd = TWE_FIND_COMMAND(tr); 88067555Smsmith cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; 88167555Smsmith cmd->initconnection.size = 3; 88267555Smsmith cmd->initconnection.host_id = 0; 88367555Smsmith cmd->initconnection.message_credits = mode; 88467555Smsmith cmd->initconnection.response_queue_pointer = 0; 88560894Smsmith 88660894Smsmith /* submit the command */ 88760894Smsmith error = twe_immediate_request(tr); 88860894Smsmith twe_release_request(tr); 88960894Smsmith 89067555Smsmith if (mode == TWE_INIT_MESSAGE_CREDITS) 89167555Smsmith sc->twe_host_id = cmd->initconnection.host_id; 89260894Smsmith return(error); 89360894Smsmith} 89460894Smsmith 89560894Smsmith/******************************************************************************** 89660894Smsmith * Start the command (tr) and sleep waiting for it to complete. 89760894Smsmith * 89860894Smsmith * Successfully completed commands are dequeued. 89960894Smsmith */ 90060894Smsmithstatic int 90160894Smsmithtwe_wait_request(struct twe_request *tr) 90260894Smsmith{ 90367555Smsmith int s; 90460894Smsmith 90560894Smsmith debug_called(4); 90660894Smsmith 90767555Smsmith tr->tr_flags |= TWE_CMD_SLEEPER; 90867555Smsmith tr->tr_status = TWE_CMD_BUSY; 90967555Smsmith twe_enqueue_ready(tr); 91067555Smsmith twe_startio(tr->tr_sc); 91160894Smsmith s = splbio(); 91267555Smsmith while (tr->tr_status == TWE_CMD_BUSY) 91367555Smsmith tsleep(tr, PRIBIO, "twewait", 0); 91460894Smsmith splx(s); 91567555Smsmith 916127415Svkashyap return(tr->tr_status != TWE_CMD_COMPLETE); 91760894Smsmith} 91860894Smsmith 91960894Smsmith/******************************************************************************** 92060894Smsmith * Start the command (tr) and busy-wait for it to complete. 92160894Smsmith * This should only be used when interrupts are actually disabled (although it 92260894Smsmith * will work if they are not). 92360894Smsmith */ 92460894Smsmithstatic int 92560894Smsmithtwe_immediate_request(struct twe_request *tr) 92660894Smsmith{ 927127415Svkashyap int error; 92860894Smsmith 92960894Smsmith debug_called(4); 93060894Smsmith 931118816Sps tr->tr_flags |= TWE_CMD_IMMEDIATE; 932118816Sps tr->tr_status = TWE_CMD_BUSY; 933127415Svkashyap if ((error = twe_map_request(tr)) != 0) 934127415Svkashyap return(error); 93560894Smsmith while (tr->tr_status == TWE_CMD_BUSY){ 93660894Smsmith twe_done(tr->tr_sc); 93760894Smsmith } 93860894Smsmith return(tr->tr_status != TWE_CMD_COMPLETE); 93960894Smsmith} 94060894Smsmith 94160894Smsmith/******************************************************************************** 94260894Smsmith * Handle completion of an I/O command. 94360894Smsmith */ 94460894Smsmithstatic void 94560894Smsmithtwe_completeio(struct twe_request *tr) 94660894Smsmith{ 947123103Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 94860894Smsmith struct twe_softc *sc = tr->tr_sc; 94967555Smsmith twe_bio *bp = (twe_bio *)tr->tr_private; 95060894Smsmith 95160894Smsmith debug_called(4); 95260894Smsmith 95360894Smsmith if (tr->tr_status == TWE_CMD_COMPLETE) { 95467555Smsmith 955123103Sps if (cmd->generic.status) 956123103Sps if (twe_report_request(tr)) 957123103Sps TWE_BIO_SET_ERROR(bp, EIO); 95867555Smsmith 95969543Smsmith } else { 96069543Smsmith twe_panic(sc, "twe_completeio on incomplete command"); 96160894Smsmith } 96269543Smsmith tr->tr_private = NULL; 96369543Smsmith twed_intr(bp); 96460894Smsmith twe_release_request(tr); 96560894Smsmith} 96660894Smsmith 96760894Smsmith/******************************************************************************** 96867555Smsmith * Reset the controller and pull all the active commands back onto the ready 96967555Smsmith * queue. Used to restart a controller that's exhibiting bad behaviour. 97067555Smsmith */ 97167555Smsmithstatic void 97267555Smsmithtwe_reset(struct twe_softc *sc) 97367555Smsmith{ 97467555Smsmith struct twe_request *tr; 97567555Smsmith int i, s; 97667555Smsmith 97791790Smsmith /* 97891790Smsmith * Sleep for a short period to allow AENs to be signalled. 97991790Smsmith */ 980119124Sps tsleep(sc, PRIBIO, "twereset", hz); 98167555Smsmith 98267555Smsmith /* 98367555Smsmith * Disable interrupts from the controller, and mask any accidental entry 98467555Smsmith * into our interrupt handler. 98567555Smsmith */ 98691790Smsmith twe_printf(sc, "controller reset in progress...\n"); 98767555Smsmith twe_disable_interrupts(sc); 98867555Smsmith s = splbio(); 98967555Smsmith 99067555Smsmith /* 99167555Smsmith * Try to soft-reset the controller. 99267555Smsmith */ 99367555Smsmith for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { 99467555Smsmith 99567555Smsmith if (i > 0) 99667555Smsmith twe_printf(sc, "reset %d failed, trying again\n", i); 99767555Smsmith 99867555Smsmith if (!twe_soft_reset(sc)) 99967555Smsmith break; /* reset process complete */ 100067555Smsmith } 100167555Smsmith /* did we give up? */ 100267555Smsmith if (i >= TWE_MAX_RESET_TRIES) { 100367555Smsmith twe_printf(sc, "can't reset controller, giving up\n"); 100467555Smsmith goto out; 100567555Smsmith } 100667555Smsmith 100767555Smsmith /* 100867555Smsmith * Move all of the commands that were busy back to the ready queue. 100967555Smsmith */ 101067555Smsmith i = 0; 101167555Smsmith while ((tr = twe_dequeue_busy(sc)) != NULL) { 101267555Smsmith twe_enqueue_ready(tr); 101367555Smsmith i++; 101467555Smsmith } 101567555Smsmith 101667555Smsmith /* 101767555Smsmith * Kick the controller to start things going again, then re-enable interrupts. 101867555Smsmith */ 101967555Smsmith twe_startio(sc); 102067555Smsmith twe_enable_interrupts(sc); 102167555Smsmith twe_printf(sc, "controller reset done, %d commands restarted\n", i); 102267555Smsmith 102367555Smsmithout: 102467555Smsmith splx(s); 102567555Smsmith twe_enable_interrupts(sc); 102667555Smsmith} 102767555Smsmith 102867555Smsmith/******************************************************************************** 102960894Smsmith ******************************************************************************** 103060894Smsmith Command I/O to Controller 103160894Smsmith ******************************************************************************** 103260894Smsmith ********************************************************************************/ 103360894Smsmith 103460894Smsmith/******************************************************************************** 103560894Smsmith * Try to deliver (tr) to the controller. 103660894Smsmith * 103760894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 103860894Smsmith */ 1039118816Spsint 104060894Smsmithtwe_start(struct twe_request *tr) 104160894Smsmith{ 104260894Smsmith struct twe_softc *sc = tr->tr_sc; 1043118816Sps TWE_Command *cmd; 104460894Smsmith int i, s, done; 104560894Smsmith u_int32_t status_reg; 104660894Smsmith 104760894Smsmith debug_called(4); 104860894Smsmith 104960894Smsmith /* mark the command as currently being processed */ 105060894Smsmith tr->tr_status = TWE_CMD_BUSY; 1051118816Sps cmd = TWE_FIND_COMMAND(tr); 105260894Smsmith 105367555Smsmith /* 105467555Smsmith * Spin briefly waiting for the controller to come ready 105567555Smsmith * 105667555Smsmith * XXX it might be more efficient to return EBUSY immediately 105767555Smsmith * and let the command be rescheduled. 105867555Smsmith */ 105960894Smsmith for (i = 100000, done = 0; (i > 0) && !done; i--) { 106060894Smsmith s = splbio(); 106160894Smsmith 106260894Smsmith /* check to see if we can post a command */ 106360894Smsmith status_reg = TWE_STATUS(sc); 106460894Smsmith twe_check_bits(sc, status_reg); 106560894Smsmith 106660894Smsmith if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { 1067118816Sps twe_enqueue_busy(tr); 1068118816Sps 1069118816Sps TWE_COMMAND_QUEUE(sc, TWE_FIND_COMMANDPHYS(tr)); 107060894Smsmith done = 1; 107160894Smsmith /* move command to work queue */ 107267555Smsmith#ifdef TWE_DEBUG 107360894Smsmith if (tr->tr_complete != NULL) { 1074118816Sps debug(3, "queued request %d with callback %p", cmd->generic.request_id, tr->tr_complete); 107567555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { 1076118816Sps debug(3, "queued request %d with wait channel %p", cmd->generic.request_id, tr); 107760894Smsmith } else { 1078118816Sps debug(3, "queued request %d for polling caller", cmd->generic.request_id); 107960894Smsmith } 108067555Smsmith#endif 108160894Smsmith } 108260894Smsmith splx(s); /* drop spl to allow completion interrupts */ 108360894Smsmith } 108460894Smsmith 108560894Smsmith /* command is enqueued */ 108660894Smsmith if (done) 108760894Smsmith return(0); 108860894Smsmith 108960894Smsmith /* 109060894Smsmith * We couldn't get the controller to take the command; try submitting it again later. 109160894Smsmith * This should only happen if something is wrong with the controller, or if we have 109260894Smsmith * overestimated the number of commands it can accept. (Should we actually reject 109360894Smsmith * the command at this point?) 109460894Smsmith */ 109560894Smsmith return(EBUSY); 109660894Smsmith} 109760894Smsmith 109860894Smsmith/******************************************************************************** 109960894Smsmith * Poll the controller (sc) for completed commands. 110060894Smsmith * 110160894Smsmith * Can be called at any interrupt level, with or without interrupts enabled. 110260894Smsmith */ 110360894Smsmithstatic void 110460894Smsmithtwe_done(struct twe_softc *sc) 110560894Smsmith{ 110660894Smsmith TWE_Response_Queue rq; 1107118816Sps TWE_Command *cmd; 110860894Smsmith struct twe_request *tr; 110960894Smsmith int s, found; 111060894Smsmith u_int32_t status_reg; 111160894Smsmith 111260894Smsmith debug_called(5); 111360894Smsmith 111460894Smsmith /* loop collecting completed commands */ 111560894Smsmith found = 0; 111660894Smsmith s = splbio(); 111760894Smsmith for (;;) { 111860894Smsmith status_reg = TWE_STATUS(sc); 111960894Smsmith twe_check_bits(sc, status_reg); /* XXX should this fail? */ 112060894Smsmith 112160894Smsmith if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { 112260894Smsmith found = 1; 112360894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 112467555Smsmith tr = sc->twe_lookup[rq.u.response_id]; /* find command */ 1125118816Sps cmd = TWE_FIND_COMMAND(tr); 112669543Smsmith if (tr->tr_status != TWE_CMD_BUSY) 112769543Smsmith twe_printf(sc, "completion event for nonbusy command\n"); 112869543Smsmith tr->tr_status = TWE_CMD_COMPLETE; 112969543Smsmith debug(3, "completed request id %d with status %d", 1130118816Sps cmd->generic.request_id, cmd->generic.status); 113169543Smsmith /* move to completed queue */ 113269543Smsmith twe_remove_busy(tr); 113369543Smsmith twe_enqueue_complete(tr); 113460894Smsmith } else { 113560894Smsmith break; /* no response ready */ 113660894Smsmith } 113760894Smsmith } 113860894Smsmith splx(s); 113960894Smsmith 114060894Smsmith /* if we've completed any commands, try posting some more */ 114160894Smsmith if (found) 114260894Smsmith twe_startio(sc); 114360894Smsmith 114460894Smsmith /* handle completion and timeouts */ 114567555Smsmith twe_complete(sc); /* XXX use deferred completion? */ 114660894Smsmith} 114760894Smsmith 114860894Smsmith/******************************************************************************** 114960894Smsmith * Perform post-completion processing for commands on (sc). 115060894Smsmith * 115160894Smsmith * This is split from twe_done as it can be safely deferred and run at a lower 115260894Smsmith * priority level should facilities for such a thing become available. 115360894Smsmith */ 115460894Smsmithstatic void 115560894Smsmithtwe_complete(struct twe_softc *sc) 115660894Smsmith{ 115767555Smsmith struct twe_request *tr; 115860894Smsmith 115960894Smsmith debug_called(5); 116060894Smsmith 116160894Smsmith /* 116267555Smsmith * Pull commands off the completed list, dispatch them appropriately 116360894Smsmith */ 116467555Smsmith while ((tr = twe_dequeue_complete(sc)) != NULL) { 116567555Smsmith /* unmap the command's data buffer */ 116667555Smsmith twe_unmap_request(tr); 116760894Smsmith 116867555Smsmith /* dispatch to suit command originator */ 116967555Smsmith if (tr->tr_complete != NULL) { /* completion callback */ 117067555Smsmith debug(2, "call completion handler %p", tr->tr_complete); 117167555Smsmith tr->tr_complete(tr); 117260894Smsmith 117367555Smsmith } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ 117467555Smsmith debug(2, "wake up command owner on %p", tr); 117567555Smsmith wakeup_one(tr); 117660894Smsmith 117767555Smsmith } else { /* caller is polling command */ 117867555Smsmith debug(2, "command left for owner"); 117960894Smsmith } 118067555Smsmith } 118160894Smsmith} 118260894Smsmith 118360894Smsmith/******************************************************************************** 118460894Smsmith * Wait for (status) to be set in the controller status register for up to 118560894Smsmith * (timeout) seconds. Returns 0 if found, nonzero if we time out. 118660894Smsmith * 118760894Smsmith * Note: this busy-waits, rather than sleeping, since we may be called with 118860894Smsmith * eg. clock interrupts masked. 118960894Smsmith */ 119060894Smsmithstatic int 119160894Smsmithtwe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) 119260894Smsmith{ 119360894Smsmith time_t expiry; 119460894Smsmith u_int32_t status_reg; 119560894Smsmith 119660894Smsmith debug_called(4); 119760894Smsmith 119860894Smsmith expiry = time_second + timeout; 119960894Smsmith 120060894Smsmith do { 120160894Smsmith status_reg = TWE_STATUS(sc); 120260894Smsmith if (status_reg & status) /* got the required bit(s)? */ 120360894Smsmith return(0); 120460894Smsmith DELAY(100000); 120560894Smsmith } while (time_second <= expiry); 120660894Smsmith 120760894Smsmith return(1); 120860894Smsmith} 120960894Smsmith 121060894Smsmith/******************************************************************************** 121160894Smsmith * Drain the response queue, which may contain responses to commands we know 121260894Smsmith * nothing about. 121360894Smsmith */ 121460894Smsmithstatic int 121560894Smsmithtwe_drain_response_queue(struct twe_softc *sc) 121660894Smsmith{ 121760894Smsmith TWE_Response_Queue rq; 121860894Smsmith u_int32_t status_reg; 121960894Smsmith 122060894Smsmith debug_called(4); 122160894Smsmith 122260894Smsmith for (;;) { /* XXX give up eventually? */ 122360894Smsmith status_reg = TWE_STATUS(sc); 122460894Smsmith if (twe_check_bits(sc, status_reg)) 122560894Smsmith return(1); 122660894Smsmith if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) 122760894Smsmith return(0); 122860894Smsmith rq = TWE_RESPONSE_QUEUE(sc); 122960894Smsmith } 123060894Smsmith} 123160894Smsmith 123260894Smsmith/******************************************************************************** 123367555Smsmith * Soft-reset the controller 123467555Smsmith */ 123567555Smsmithstatic int 123667555Smsmithtwe_soft_reset(struct twe_softc *sc) 123767555Smsmith{ 123867555Smsmith u_int32_t status_reg; 123967555Smsmith 124067555Smsmith debug_called(2); 124167555Smsmith 124267555Smsmith TWE_SOFT_RESET(sc); 124367555Smsmith 124491790Smsmith if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) { 124567683Smsmith twe_printf(sc, "no attention interrupt\n"); 124667555Smsmith return(1); 124767555Smsmith } 124891790Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 124967555Smsmith if (twe_drain_aen_queue(sc)) { 125067555Smsmith twe_printf(sc, "can't drain AEN queue\n"); 125167555Smsmith return(1); 125267555Smsmith } 125367555Smsmith if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { 125467555Smsmith twe_printf(sc, "reset not reported\n"); 125567555Smsmith return(1); 125667555Smsmith } 125767555Smsmith status_reg = TWE_STATUS(sc); 125867555Smsmith if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { 125967555Smsmith twe_printf(sc, "controller errors detected\n"); 126067555Smsmith return(1); 126167555Smsmith } 126267555Smsmith if (twe_drain_response_queue(sc)) { 126367555Smsmith twe_printf(sc, "can't drain response queue\n"); 126467555Smsmith return(1); 126567555Smsmith } 126667555Smsmith return(0); 126767555Smsmith} 126867555Smsmith 126967555Smsmith/******************************************************************************** 127060894Smsmith ******************************************************************************** 127160894Smsmith Interrupt Handling 127260894Smsmith ******************************************************************************** 127360894Smsmith ********************************************************************************/ 127460894Smsmith 127560894Smsmith/******************************************************************************** 127660894Smsmith * Host interrupt. 127760894Smsmith * 127860894Smsmith * XXX what does this mean? 127960894Smsmith */ 128060894Smsmithstatic void 128160894Smsmithtwe_host_intr(struct twe_softc *sc) 128260894Smsmith{ 128360894Smsmith debug_called(4); 128460894Smsmith 128567555Smsmith twe_printf(sc, "host interrupt\n"); 128660894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); 128760894Smsmith} 128860894Smsmith 128960894Smsmith/******************************************************************************** 129060894Smsmith * Attention interrupt. 129160894Smsmith * 129260894Smsmith * Signalled when the controller has one or more AENs for us. 129360894Smsmith */ 129460894Smsmithstatic void 129560894Smsmithtwe_attention_intr(struct twe_softc *sc) 129660894Smsmith{ 129760894Smsmith debug_called(4); 129860894Smsmith 129960894Smsmith /* instigate a poll for AENs */ 130060894Smsmith if (twe_fetch_aen(sc)) { 130167555Smsmith twe_printf(sc, "error polling for signalled AEN\n"); 130260894Smsmith } else { 130360894Smsmith TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); 130460894Smsmith } 130560894Smsmith} 130660894Smsmith 130760894Smsmith/******************************************************************************** 130860894Smsmith * Command interrupt. 130960894Smsmith * 131060894Smsmith * Signalled when the controller can handle more commands. 131160894Smsmith */ 131260894Smsmithstatic void 131360894Smsmithtwe_command_intr(struct twe_softc *sc) 131460894Smsmith{ 131560894Smsmith debug_called(4); 131660894Smsmith 131760894Smsmith /* 131860894Smsmith * We don't use this, rather we try to submit commands when we receive 131960894Smsmith * them, and when other commands have completed. Mask it so we don't get 132060894Smsmith * another one. 132160894Smsmith */ 132260894Smsmith TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); 132360894Smsmith} 132460894Smsmith 132560894Smsmith/******************************************************************************** 132660894Smsmith ******************************************************************************** 132760894Smsmith Asynchronous Event Handling 132860894Smsmith ******************************************************************************** 132960894Smsmith ********************************************************************************/ 133060894Smsmith 133160894Smsmith/******************************************************************************** 133260894Smsmith * Request an AEN from the controller. 133360894Smsmith */ 133460894Smsmithstatic int 133560894Smsmithtwe_fetch_aen(struct twe_softc *sc) 133660894Smsmith{ 133760894Smsmith 133860894Smsmith debug_called(4); 133960894Smsmith 134067555Smsmith if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) 134160894Smsmith return(EIO); 134260894Smsmith return(0); 134360894Smsmith} 134460894Smsmith 134560894Smsmith/******************************************************************************** 134660894Smsmith * Handle an AEN returned by the controller. 134760894Smsmith */ 134860894Smsmithstatic void 134960894Smsmithtwe_handle_aen(struct twe_request *tr) 135060894Smsmith{ 135160894Smsmith struct twe_softc *sc = tr->tr_sc; 135260894Smsmith TWE_Param *param; 135360894Smsmith u_int16_t aen; 135460894Smsmith 135560894Smsmith debug_called(4); 135660894Smsmith 135760894Smsmith /* XXX check for command success somehow? */ 135860894Smsmith 135960894Smsmith param = (TWE_Param *)tr->tr_data; 136060894Smsmith aen = *(u_int16_t *)(param->data); 136160894Smsmith 136260894Smsmith free(tr->tr_data, M_DEVBUF); 136360894Smsmith twe_release_request(tr); 136460894Smsmith twe_enqueue_aen(sc, aen); 136560894Smsmith 136660894Smsmith /* XXX poll for more AENs? */ 136760894Smsmith} 136860894Smsmith 136960894Smsmith/******************************************************************************** 137060894Smsmith * Pull AENs out of the controller and park them in the queue, in a context where 137160894Smsmith * interrupts aren't active. Return nonzero if we encounter any errors in the 137260894Smsmith * process of obtaining all the available AENs. 137360894Smsmith */ 137460894Smsmithstatic int 137560894Smsmithtwe_drain_aen_queue(struct twe_softc *sc) 137660894Smsmith{ 137760894Smsmith u_int16_t aen; 137860894Smsmith 137960894Smsmith for (;;) { 138067555Smsmith if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) 138160894Smsmith return(1); 138260894Smsmith if (aen == TWE_AEN_QUEUE_EMPTY) 138360894Smsmith return(0); 138460894Smsmith twe_enqueue_aen(sc, aen); 138560894Smsmith } 138660894Smsmith} 138760894Smsmith 138860894Smsmith/******************************************************************************** 138960894Smsmith * Push an AEN that we've received onto the queue. 139060894Smsmith * 139160894Smsmith * Note that we have to lock this against reentrance, since it may be called 139260894Smsmith * from both interrupt and non-interrupt context. 139360894Smsmith * 139460894Smsmith * If someone is waiting for the AEN we have, wake them up. 139560894Smsmith */ 139660894Smsmithstatic void 139760894Smsmithtwe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) 139860894Smsmith{ 139967555Smsmith char *msg; 140067555Smsmith int s, next, nextnext; 140160894Smsmith 140260894Smsmith debug_called(4); 140360894Smsmith 140467555Smsmith if ((msg = twe_format_aen(sc, aen)) != NULL) 140567555Smsmith twe_printf(sc, "AEN: <%s>\n", msg); 140660894Smsmith 140760894Smsmith s = splbio(); 140860894Smsmith /* enqueue the AEN */ 140960894Smsmith next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); 141067555Smsmith nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); 141167555Smsmith 141267555Smsmith /* check to see if this is the last free slot, and subvert the AEN if it is */ 141367555Smsmith if (nextnext == sc->twe_aen_tail) 141467555Smsmith aen = TWE_AEN_QUEUE_FULL; 141567555Smsmith 141667555Smsmith /* look to see if there's room for this AEN */ 141760894Smsmith if (next != sc->twe_aen_tail) { 141860894Smsmith sc->twe_aen_queue[sc->twe_aen_head] = aen; 141960894Smsmith sc->twe_aen_head = next; 142060894Smsmith } 142160894Smsmith 142267555Smsmith /* wake up anyone asleep on the queue */ 142367555Smsmith wakeup(&sc->twe_aen_queue); 142467555Smsmith 142560894Smsmith /* anyone looking for this AEN? */ 142660894Smsmith if (sc->twe_wait_aen == aen) { 142760894Smsmith sc->twe_wait_aen = -1; 142860894Smsmith wakeup(&sc->twe_wait_aen); 142960894Smsmith } 143060894Smsmith splx(s); 143160894Smsmith} 143260894Smsmith 143360894Smsmith/******************************************************************************** 143460894Smsmith * Pop an AEN off the queue, or return -1 if there are none left. 143560894Smsmith * 143660894Smsmith * We are more or less interrupt-safe, so don't block interrupts. 143760894Smsmith */ 1438123103Spsstatic u_int16_t 143960894Smsmithtwe_dequeue_aen(struct twe_softc *sc) 144060894Smsmith{ 1441123103Sps u_int16_t result; 144260894Smsmith 144360894Smsmith debug_called(4); 144460894Smsmith 144560894Smsmith if (sc->twe_aen_tail == sc->twe_aen_head) { 1446118508Sps result = TWE_AEN_QUEUE_EMPTY; 144760894Smsmith } else { 144860894Smsmith result = sc->twe_aen_queue[sc->twe_aen_tail]; 144960894Smsmith sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); 145060894Smsmith } 145160894Smsmith return(result); 145260894Smsmith} 145360894Smsmith 145460894Smsmith/******************************************************************************** 145560894Smsmith * Check to see if the requested AEN is in the queue. 145660894Smsmith * 145760894Smsmith * XXX we could probably avoid masking interrupts here 145860894Smsmith */ 145960894Smsmithstatic int 146060894Smsmithtwe_find_aen(struct twe_softc *sc, u_int16_t aen) 146160894Smsmith{ 146260894Smsmith int i, s, missing; 146360894Smsmith 146460894Smsmith missing = 1; 146560894Smsmith s = splbio(); 146660894Smsmith for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { 146760894Smsmith if (sc->twe_aen_queue[i] == aen) 146860894Smsmith missing = 0; 146960894Smsmith } 147067555Smsmith splx(s); 147160894Smsmith return(missing); 147260894Smsmith} 147360894Smsmith 147460894Smsmith 147560894Smsmith#if 0 /* currently unused */ 147660894Smsmith/******************************************************************************** 147760894Smsmith * Sleep waiting for at least (timeout) seconds until we see (aen) as 147860894Smsmith * requested. Returns nonzero on timeout or failure. 147960894Smsmith * 148060894Smsmith * XXX: this should not be used in cases where there may be more than one sleeper 148160894Smsmith * without a mechanism for registering multiple sleepers. 148260894Smsmith */ 148360894Smsmithstatic int 148460894Smsmithtwe_wait_aen(struct twe_softc *sc, int aen, int timeout) 148560894Smsmith{ 148660894Smsmith time_t expiry; 148760894Smsmith int found, s; 148860894Smsmith 148960894Smsmith debug_called(4); 149060894Smsmith 149160894Smsmith expiry = time_second + timeout; 149260894Smsmith found = 0; 149360894Smsmith 149460894Smsmith s = splbio(); 149560894Smsmith sc->twe_wait_aen = aen; 149660894Smsmith do { 149760894Smsmith twe_fetch_aen(sc); 149860894Smsmith tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); 149960894Smsmith if (sc->twe_wait_aen == -1) 150060894Smsmith found = 1; 150160894Smsmith } while ((time_second <= expiry) && !found); 150260894Smsmith splx(s); 150360894Smsmith return(!found); 150460894Smsmith} 150560894Smsmith#endif 150660894Smsmith 150760894Smsmith/******************************************************************************** 150860894Smsmith ******************************************************************************** 150960894Smsmith Command Buffer Management 151060894Smsmith ******************************************************************************** 151160894Smsmith ********************************************************************************/ 151260894Smsmith 151360894Smsmith/******************************************************************************** 151460894Smsmith * Get a new command buffer. 151560894Smsmith * 151667555Smsmith * This will return NULL if all command buffers are in use. 151760894Smsmith */ 151867555Smsmithstatic int 151967555Smsmithtwe_get_request(struct twe_softc *sc, struct twe_request **tr) 152060894Smsmith{ 1521118816Sps TWE_Command *cmd; 152260894Smsmith debug_called(4); 152360894Smsmith 152460894Smsmith /* try to reuse an old buffer */ 152567555Smsmith *tr = twe_dequeue_free(sc); 152660894Smsmith 152767555Smsmith /* initialise some fields to their defaults */ 152867555Smsmith if (*tr != NULL) { 1529118816Sps cmd = TWE_FIND_COMMAND(*tr); 153067555Smsmith (*tr)->tr_data = NULL; 153169543Smsmith (*tr)->tr_private = NULL; 153267555Smsmith (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ 153367555Smsmith (*tr)->tr_flags = 0; 153467555Smsmith (*tr)->tr_complete = NULL; 1535118816Sps cmd->generic.status = 0; /* before submission to controller */ 1536118816Sps cmd->generic.flags = 0; /* not used */ 153760894Smsmith } 153867555Smsmith return(*tr == NULL); 153960894Smsmith} 154060894Smsmith 154160894Smsmith/******************************************************************************** 154267555Smsmith * Release a command buffer for reuse. 154360894Smsmith * 154460894Smsmith */ 154560894Smsmithstatic void 154660894Smsmithtwe_release_request(struct twe_request *tr) 154760894Smsmith{ 154860894Smsmith debug_called(4); 154960894Smsmith 155069543Smsmith if (tr->tr_private != NULL) 155169543Smsmith twe_panic(tr->tr_sc, "tr_private != NULL"); 155267555Smsmith twe_enqueue_free(tr); 155360894Smsmith} 155460894Smsmith 155560894Smsmith/******************************************************************************** 155667555Smsmith ******************************************************************************** 155767555Smsmith Debugging 155867555Smsmith ******************************************************************************** 155967555Smsmith ********************************************************************************/ 156060894Smsmith 156160894Smsmith/******************************************************************************** 156267555Smsmith * Print some information about the controller 156360894Smsmith */ 156467555Smsmithvoid 156567555Smsmithtwe_describe_controller(struct twe_softc *sc) 156660894Smsmith{ 156767555Smsmith TWE_Param *p[6]; 156867555Smsmith u_int8_t ports; 156967555Smsmith u_int32_t size; 157060894Smsmith int i; 157160894Smsmith 157267555Smsmith debug_called(2); 157360894Smsmith 157467555Smsmith /* get the port count */ 157567555Smsmith twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); 157660894Smsmith 157767555Smsmith /* get version strings */ 1578123103Sps p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); 1579123103Sps p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); 1580123103Sps if (p[0] && p[1]) 1581123103Sps twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[0]->data, p[1]->data); 158260894Smsmith 1583123103Sps if (bootverbose) { 1584123103Sps p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); 1585123103Sps p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); 1586123103Sps p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); 1587123103Sps p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); 158860894Smsmith 1589123103Sps if (p[2] && p[3] && p[4] && p[5]) 1590123103Sps twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[2]->data, p[3]->data, 1591123103Sps p[4]->data, p[5]->data); 1592123103Sps if (p[2]) 1593123103Sps free(p[2], M_DEVBUF); 1594123103Sps if (p[3]) 1595123103Sps free(p[3], M_DEVBUF); 1596123103Sps if (p[4]) 1597123103Sps free(p[4], M_DEVBUF); 1598123103Sps if (p[5]) 1599123103Sps free(p[5], M_DEVBUF); 1600123103Sps } 1601123103Sps if (p[0]) 1602123103Sps free(p[0], M_DEVBUF); 1603123103Sps if (p[1]) 1604123103Sps free(p[1], M_DEVBUF); 1605123103Sps 160667555Smsmith /* print attached drives */ 160767555Smsmith if (bootverbose) { 160867555Smsmith p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); 160967555Smsmith for (i = 0; i < ports; i++) { 161067555Smsmith if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) 161167555Smsmith continue; 161267555Smsmith twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); 161367555Smsmith p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); 161467555Smsmith if (p[1] != NULL) { 161567555Smsmith twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); 161667555Smsmith free(p[1], M_DEVBUF); 161767555Smsmith } else { 161867555Smsmith twe_printf(sc, "port %d, drive status unavailable\n", i); 161967555Smsmith } 162060894Smsmith } 1621123103Sps if (p[0]) 1622123103Sps free(p[0], M_DEVBUF); 162360894Smsmith } 162460894Smsmith} 162560894Smsmith 162660894Smsmith/******************************************************************************** 1627123103Sps * Look up a text description of a numeric code and return a pointer to same. 1628123103Sps */ 1629123103Spschar * 1630123103Spstwe_describe_code(struct twe_code_lookup *table, u_int32_t code) 1631123103Sps{ 1632123103Sps int i; 1633123103Sps 1634123103Sps for (i = 0; table[i].string != NULL; i++) 1635123103Sps if (table[i].code == code) 1636123103Sps return(table[i].string); 1637123103Sps return(table[i+1].string); 1638123103Sps} 1639123103Sps 1640123103Sps/******************************************************************************** 164160894Smsmith * Complain if the status bits aren't what we're expecting. 164267555Smsmith * 164367555Smsmith * Rate-limit the complaints to at most one of each every five seconds, but 164467555Smsmith * always return the correct status. 164560894Smsmith */ 164660894Smsmithstatic int 164760894Smsmithtwe_check_bits(struct twe_softc *sc, u_int32_t status_reg) 164860894Smsmith{ 164967555Smsmith int result; 165067555Smsmith static time_t lastwarn[2] = {0, 0}; 165160894Smsmith 165267555Smsmith /* 165367555Smsmith * This can be a little problematic, as twe_panic may call twe_reset if 165467555Smsmith * TWE_DEBUG is not set, which will call us again as part of the soft reset. 165567555Smsmith */ 165667555Smsmith if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { 165767555Smsmith twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, 165867555Smsmith TWE_STATUS_BITS_DESCRIPTION); 165967555Smsmith twe_panic(sc, "fatal status bits"); 166067555Smsmith } 166167555Smsmith 166260894Smsmith result = 0; 166360894Smsmith if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { 166467555Smsmith if (time_second > (lastwarn[0] + 5)) { 166567555Smsmith twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, 166667555Smsmith TWE_STATUS_BITS_DESCRIPTION); 166767555Smsmith lastwarn[0] = time_second; 166867555Smsmith } 166960894Smsmith result = 1; 167060894Smsmith } 167160894Smsmith 167260894Smsmith if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { 167367555Smsmith if (time_second > (lastwarn[1] + 5)) { 167467555Smsmith twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, 167567555Smsmith TWE_STATUS_BITS_DESCRIPTION); 167667555Smsmith lastwarn[1] = time_second; 167767555Smsmith } 167860894Smsmith result = 1; 167991790Smsmith if (status_reg & TWE_STATUS_PCI_PARITY_ERROR) { 168091790Smsmith twe_printf(sc, "PCI parity error: Reseat card, move card or buggy device present."); 168191790Smsmith twe_clear_pci_parity_error(sc); 168291790Smsmith } 168391790Smsmith if (status_reg & TWE_STATUS_PCI_ABORT) { 1684118816Sps twe_printf(sc, "PCI abort, clearing.\n"); 168591790Smsmith twe_clear_pci_abort(sc); 168691790Smsmith } 168760894Smsmith } 168867555Smsmith 168960894Smsmith return(result); 169060894Smsmith} 169160894Smsmith 169260894Smsmith/******************************************************************************** 169367555Smsmith * Return a string describing (aen). 169467555Smsmith * 169567555Smsmith * The low 8 bits of the aen are the code, the high 8 bits give the unit number 169667555Smsmith * where an AEN is specific to a unit. 169767555Smsmith * 169867555Smsmith * Note that we could expand this routine to handle eg. up/downgrading the status 169967555Smsmith * of a drive if we had some idea of what the drive's initial status was. 170060894Smsmith */ 170160894Smsmith 170260894Smsmithstatic char * 170367555Smsmithtwe_format_aen(struct twe_softc *sc, u_int16_t aen) 170460894Smsmith{ 170567555Smsmith static char buf[80]; 170667555Smsmith device_t child; 170767555Smsmith char *code, *msg; 170860894Smsmith 170967555Smsmith code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); 171067555Smsmith msg = code + 2; 171160894Smsmith 171267555Smsmith switch (*code) { 171367555Smsmith case 'q': 171467555Smsmith if (!bootverbose) 171567555Smsmith return(NULL); 171667555Smsmith /* FALLTHROUGH */ 171791790Smsmith case 'a': 171867555Smsmith return(msg); 171967555Smsmith 172067555Smsmith case 'c': 172167555Smsmith if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { 172267555Smsmith sprintf(buf, "twed%d: %s", device_get_unit(child), msg); 172367555Smsmith } else { 172467555Smsmith sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), 172567555Smsmith msg, TWE_AEN_UNIT(aen)); 172667555Smsmith } 172767555Smsmith return(buf); 172891790Smsmith 172991790Smsmith case 'p': 173091790Smsmith sprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen), 173191790Smsmith msg); 173291790Smsmith return(buf); 173391790Smsmith 173467555Smsmith 173567555Smsmith case 'x': 173667555Smsmith default: 173767555Smsmith break; 173867555Smsmith } 173967555Smsmith sprintf(buf, "unknown AEN 0x%x", aen); 174060894Smsmith return(buf); 174160894Smsmith} 174260894Smsmith 174369543Smsmith/******************************************************************************** 174469543Smsmith * Print a diagnostic if the status of the command warrants it, and return 174569543Smsmith * either zero (command was ok) or nonzero (command failed). 174669543Smsmith */ 174767555Smsmithstatic int 174869543Smsmithtwe_report_request(struct twe_request *tr) 174967555Smsmith{ 175069543Smsmith struct twe_softc *sc = tr->tr_sc; 1751118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 175276340Smsmith int result = 0; 175360894Smsmith 175476340Smsmith /* 175576340Smsmith * Check the command status value and handle accordingly. 175676340Smsmith */ 175776340Smsmith if (cmd->generic.status == TWE_STATUS_RESET) { 175876340Smsmith /* 175976340Smsmith * The status code 0xff requests a controller reset. 176076340Smsmith */ 1761123103Sps twe_printf(sc, "command returned with controller reset request\n"); 176276340Smsmith twe_reset(sc); 176369543Smsmith result = 1; 176476340Smsmith } else if (cmd->generic.status > TWE_STATUS_FATAL) { 176569543Smsmith /* 176676340Smsmith * Fatal errors that don't require controller reset. 176791790Smsmith * 176891790Smsmith * We know a few special flags values. 176969543Smsmith */ 177091790Smsmith switch (cmd->generic.flags) { 177191790Smsmith case 0x1b: 177291790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 177391790Smsmith "drive timeout"); 177491790Smsmith break; 177591790Smsmith case 0x51: 177691790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 177791790Smsmith "unrecoverable drive error"); 177891790Smsmith break; 177991790Smsmith default: 178091790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 178191790Smsmith "controller error - %s (flags = 0x%x)\n", 178291790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 178391790Smsmith cmd->generic.flags); 178491790Smsmith result = 1; 178591790Smsmith } 178676340Smsmith } else if (cmd->generic.status > TWE_STATUS_WARNING) { 178776340Smsmith /* 178876340Smsmith * Warning level status. 178976340Smsmith */ 179091790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 179191790Smsmith "warning - %s (flags = 0x%x)\n", 179291790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 179391790Smsmith cmd->generic.flags); 179476340Smsmith } else if (cmd->generic.status > 0x40) { 179576340Smsmith /* 179676340Smsmith * Info level status. 179776340Smsmith */ 179891790Smsmith device_printf(sc->twe_drive[cmd->generic.unit].td_disk, 179991790Smsmith "attention - %s (flags = 0x%x)\n", 180091790Smsmith twe_describe_code(twe_table_status, cmd->generic.status), 180191790Smsmith cmd->generic.flags); 180267555Smsmith } 180376340Smsmith 180469543Smsmith return(result); 180567555Smsmith} 180667555Smsmith 180769543Smsmith/******************************************************************************** 180869543Smsmith * Print some controller state to aid in debugging error/panic conditions 180969543Smsmith */ 181067555Smsmithvoid 181167555Smsmithtwe_print_controller(struct twe_softc *sc) 181260894Smsmith{ 181367555Smsmith u_int32_t status_reg; 181460894Smsmith 181567555Smsmith status_reg = TWE_STATUS(sc); 181667555Smsmith twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); 1817123103Sps twe_printf(sc, " current max min\n"); 1818123103Sps twe_printf(sc, "free %04d %04d %04d\n", 1819123103Sps sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max, sc->twe_qstat[TWEQ_FREE].q_min); 1820123103Sps 1821123103Sps twe_printf(sc, "ready %04d %04d %04d\n", 1822123103Sps sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max, sc->twe_qstat[TWEQ_READY].q_min); 1823123103Sps 1824123103Sps twe_printf(sc, "busy %04d %04d %04d\n", 1825123103Sps sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max, sc->twe_qstat[TWEQ_BUSY].q_min); 1826123103Sps 1827123103Sps twe_printf(sc, "complete %04d %04d %04d\n", 1828123103Sps sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max, sc->twe_qstat[TWEQ_COMPLETE].q_min); 1829123103Sps 1830123103Sps twe_printf(sc, "bioq %04d %04d %04d\n", 1831123103Sps sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max, sc->twe_qstat[TWEQ_BIO].q_min); 1832123103Sps 183367555Smsmith twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); 183467555Smsmith} 183560894Smsmith 183667555Smsmithstatic void 183767555Smsmithtwe_panic(struct twe_softc *sc, char *reason) 183867555Smsmith{ 183967555Smsmith twe_print_controller(sc); 184067555Smsmith#ifdef TWE_DEBUG 184167555Smsmith panic(reason); 184267555Smsmith#else 184367555Smsmith twe_reset(sc); 184467555Smsmith#endif 184560894Smsmith} 184660894Smsmith 1847127415Svkashyap#if 0 184860894Smsmith/******************************************************************************** 184960894Smsmith * Print a request/command in human-readable format. 185060894Smsmith */ 185160894Smsmithstatic void 185260894Smsmithtwe_print_request(struct twe_request *tr) 185360894Smsmith{ 185467555Smsmith struct twe_softc *sc = tr->tr_sc; 1855118816Sps TWE_Command *cmd = TWE_FIND_COMMAND(tr); 185660894Smsmith int i; 185760894Smsmith 185867555Smsmith twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", 185967555Smsmith cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, 186067555Smsmith cmd->generic.unit, cmd->generic.host_id); 186167555Smsmith twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", 186267555Smsmith cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); 186367555Smsmith 186467555Smsmith switch(cmd->generic.opcode) { /* XXX add more opcodes? */ 186567555Smsmith case TWE_OP_READ: 186667555Smsmith case TWE_OP_WRITE: 186767555Smsmith twe_printf(sc, " lba %d\n", cmd->io.lba); 186867555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) 186967555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 187067555Smsmith i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); 187160894Smsmith break; 187260894Smsmith 187367555Smsmith case TWE_OP_GET_PARAM: 187467555Smsmith case TWE_OP_SET_PARAM: 187567555Smsmith for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) 187667555Smsmith twe_printf(sc, " %d: 0x%x/%d\n", 187767555Smsmith i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); 187860894Smsmith break; 187960894Smsmith 188067555Smsmith case TWE_OP_INIT_CONNECTION: 188167555Smsmith twe_printf(sc, " response queue pointer 0x%x\n", 188267555Smsmith cmd->initconnection.response_queue_pointer); 188367555Smsmith break; 188467555Smsmith 188560894Smsmith default: 188667555Smsmith break; 188760894Smsmith } 188867555Smsmith twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", 1889118816Sps tr, TWE_FIND_COMMANDPHYS(tr), tr->tr_data, tr->tr_dataphys, tr->tr_length); 189067555Smsmith twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", 189167555Smsmith tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); 189260894Smsmith} 189360894Smsmith 189460894Smsmith#endif 1895