diff --git a/sys/dev/twe/twe.c b/sys/dev/twe/twe.c index fadf767fb5aa..73717bfc4afd 100644 --- a/sys/dev/twe/twe.c +++ b/sys/dev/twe/twe.c @@ -1,1746 +1,1756 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Driver for the 3ware Escalade family of IDE RAID controllers. */ #include #include #include #include #define TWE_DEFINE_TABLES #include /* * Command submission. */ static int twe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result); static int twe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result); static int twe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result); static void *twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, void (* func)(struct twe_request *tr)); static int twe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value); static int twe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value); static int twe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value); static int twe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data); static int twe_init_connection(struct twe_softc *sc, int mode); static int twe_wait_request(struct twe_request *tr); static int twe_immediate_request(struct twe_request *tr); static void twe_completeio(struct twe_request *tr); static void twe_reset(struct twe_softc *sc); /* * Command I/O to controller. */ static int twe_start(struct twe_request *tr); static void twe_done(struct twe_softc *sc); static void twe_complete(struct twe_softc *sc); static int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout); static int twe_drain_response_queue(struct twe_softc *sc); static int twe_check_bits(struct twe_softc *sc, u_int32_t status_reg); static int twe_soft_reset(struct twe_softc *sc); /* * Interrupt handling. */ static void twe_host_intr(struct twe_softc *sc); static void twe_attention_intr(struct twe_softc *sc); static void twe_command_intr(struct twe_softc *sc); /* * Asynchronous event handling. */ static int twe_fetch_aen(struct twe_softc *sc); static void twe_handle_aen(struct twe_request *tr); static void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen); static int twe_dequeue_aen(struct twe_softc *sc); static int twe_drain_aen_queue(struct twe_softc *sc); static int twe_find_aen(struct twe_softc *sc, u_int16_t aen); /* * Command buffer management. */ static int twe_get_request(struct twe_softc *sc, struct twe_request **tr); static void twe_release_request(struct twe_request *tr); /* * Debugging. */ static char *twe_format_aen(struct twe_softc *sc, u_int16_t aen); static int twe_report_request(struct twe_request *tr); static void twe_panic(struct twe_softc *sc, char *reason); /******************************************************************************** ******************************************************************************** Public Interfaces ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Initialise the controller, set up driver data structures. */ int twe_setup(struct twe_softc *sc) { struct twe_request *tr; int i; debug_called(4); /* * Initialise request queues. */ twe_initq_free(sc); twe_initq_bio(sc); twe_initq_ready(sc); twe_initq_busy(sc); twe_initq_complete(sc); sc->twe_wait_aen = -1; /* * Allocate request structures up front. */ for (i = 0; i < TWE_Q_LENGTH; i++) { if ((tr = twe_allocate_request(sc)) == NULL) return(ENOMEM); /* * Set global defaults that won't change. */ tr->tr_command.generic.host_id = sc->twe_host_id; /* controller-assigned host ID */ tr->tr_command.generic.request_id = i; /* our index number */ sc->twe_lookup[i] = tr; /* * Put command onto the freelist. */ twe_release_request(tr); } /* * Wait for the controller to come ready. */ if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { twe_printf(sc, "microcontroller not ready\n"); return(ENXIO); } /* * Disable interrupts from the card. */ twe_disable_interrupts(sc); /* * Soft reset the controller, look for the AEN acknowledging the reset, * check for errors, drain the response queue. */ for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { if (i > 0) twe_printf(sc, "reset %d failed, trying again\n", i); if (!twe_soft_reset(sc)) break; /* reset process complete */ } /* did we give up? */ if (i >= TWE_MAX_RESET_TRIES) { twe_printf(sc, "can't initialise controller, giving up\n"); return(ENXIO); } return(0); } /******************************************************************************** * Locate disk devices and attach children to them. */ void twe_init(struct twe_softc *sc) { struct twe_drive *dr; int i, table; u_int16_t dsize; TWE_Param *drives, *param; TWE_Unit_Descriptor *ud; /* * The controller is in a safe state, so try to find drives attached to it. */ if ((drives = twe_get_param(sc, TWE_PARAM_UNITSUMMARY, TWE_PARAM_UNITSUMMARY_Status, TWE_MAX_UNITS, NULL)) == NULL) { twe_printf(sc, "can't detect attached units\n"); return; } /* * For each detected unit, create a child device. */ for (i = 0, dr = &sc->twe_drive[0]; i < TWE_MAX_UNITS; i++, dr++) { /* check that the drive is online */ if (!(drives->data[i] & TWE_PARAM_UNITSTATUS_Online)) continue; table = TWE_PARAM_UNITINFO + i; if (twe_get_param_4(sc, table, TWE_PARAM_UNITINFO_Capacity, &dr->td_size)) { twe_printf(sc, "error fetching capacity for unit %d\n", i); continue; } if (twe_get_param_1(sc, table, TWE_PARAM_UNITINFO_Status, &dr->td_state)) { twe_printf(sc, "error fetching state for unit %d\n", i); continue; } if (twe_get_param_2(sc, table, TWE_PARAM_UNITINFO_DescriptorSize, &dsize)) { twe_printf(sc, "error fetching descriptor size for unit %d\n", i); continue; } if ((param = twe_get_param(sc, table, TWE_PARAM_UNITINFO_Descriptor, dsize - 3, NULL)) == NULL) { twe_printf(sc, "error fetching descriptor for unit %d\n", i); continue; } ud = (TWE_Unit_Descriptor *)param->data; dr->td_type = ud->configuration; free(param, M_DEVBUF); /* build synthetic geometry as per controller internal rules */ if (dr->td_size > 0x200000) { dr->td_heads = 255; dr->td_sectors = 63; } else { dr->td_heads = 64; dr->td_sectors = 32; } dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors); dr->td_unit = i; twe_attach_drive(sc, dr); } free(drives, M_DEVBUF); /* * Initialise connection with controller. */ twe_init_connection(sc, TWE_INIT_MESSAGE_CREDITS); #ifdef TWE_SHUTDOWN_NOTIFICATION /* * Tell the controller we support shutdown notification. */ twe_set_param_1(sc, TWE_PARAM_FEATURES, TWE_PARAM_FEATURES_DriverShutdown, 1); #endif /* * Mark controller up and ready to run. */ sc->twe_state &= ~TWE_STATE_SHUTDOWN; /* * Finally enable interrupts. */ twe_enable_interrupts(sc); } /******************************************************************************** * Stop the controller */ void twe_deinit(struct twe_softc *sc) { /* * Mark the controller as shutting down, and disable any further interrupts. */ sc->twe_state |= TWE_STATE_SHUTDOWN; twe_disable_interrupts(sc); #ifdef TWE_SHUTDOWN_NOTIFICATION /* * Disconnect from the controller */ twe_init_connection(sc, TWE_SHUTDOWN_MESSAGE_CREDITS); #endif } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ void twe_intr(struct twe_softc *sc) { u_int32_t status_reg; debug_called(4); /* * Collect current interrupt status. */ status_reg = TWE_STATUS(sc); twe_check_bits(sc, status_reg); /* * Dispatch based on interrupt status */ if (status_reg & TWE_STATUS_HOST_INTERRUPT) twe_host_intr(sc); if (status_reg & TWE_STATUS_ATTENTION_INTERRUPT) twe_attention_intr(sc); if (status_reg & TWE_STATUS_COMMAND_INTERRUPT) twe_command_intr(sc); if (status_reg & TWE_STATUS_RESPONSE_INTERRUPT) twe_done(sc); }; /******************************************************************************** * Pull as much work off the softc's work queue as possible and give it to the * controller. */ void twe_startio(struct twe_softc *sc) { struct twe_request *tr; TWE_Command *cmd; twe_bio *bp; int error; debug_called(4); /* spin until something prevents us from doing any work */ for (;;) { /* try to get a command that's already ready to go */ tr = twe_dequeue_ready(sc); /* build a command from an outstanding bio */ if (tr == NULL) { /* see if there's work to be done */ if ((bp = twe_dequeue_bio(sc)) == NULL) break; /* get a command to handle the bio with */ if (twe_get_request(sc, &tr)) { twe_enqueue_bio(sc, bp); /* failed, put the bio back */ break; } /* connect the bio to the command */ tr->tr_complete = twe_completeio; tr->tr_private = bp; tr->tr_data = TWE_BIO_DATA(bp); tr->tr_length = TWE_BIO_LENGTH(bp); cmd = &tr->tr_command; if (TWE_BIO_IS_READ(bp)) { tr->tr_flags |= TWE_CMD_DATAIN; cmd->io.opcode = TWE_OP_READ; } else { tr->tr_flags |= TWE_CMD_DATAOUT; cmd->io.opcode = TWE_OP_WRITE; } /* build a suitable I/O command (assumes 512-byte rounded transfers) */ cmd->io.size = 3; cmd->io.unit = TWE_BIO_UNIT(bp); cmd->io.block_count = (tr->tr_length + TWE_BLOCK_SIZE - 1) / TWE_BLOCK_SIZE; cmd->io.lba = TWE_BIO_LBA(bp); /* map the command so the controller can work with it */ twe_map_request(tr); } /* did we find something to do? */ if (tr == NULL) break; /* try to give command to controller */ error = twe_start(tr); if (error != 0) { if (error == EBUSY) { twe_requeue_ready(tr); /* try it again later */ break; /* don't try anything more for now */ } /* we don't support any other return from twe_start */ twe_panic(sc, "twe_start returned nonsense"); } } } /******************************************************************************** * Write blocks from memory to disk, for system crash dumps. */ int twe_dump_blocks(struct twe_softc *sc, int unit, u_int32_t lba, void *data, int nblks) { struct twe_request *tr; TWE_Command *cmd; int error; if (twe_get_request(sc, &tr)) return(ENOMEM); tr->tr_data = data; tr->tr_status = TWE_CMD_SETUP; tr->tr_length = nblks * TWE_BLOCK_SIZE; tr->tr_flags = TWE_CMD_DATAOUT; cmd = &tr->tr_command; cmd->io.opcode = TWE_OP_WRITE; cmd->io.size = 3; cmd->io.unit = unit; cmd->io.block_count = nblks; cmd->io.lba = lba; twe_map_request(tr); error = twe_immediate_request(tr); if (error == 0) if (twe_report_request(tr)) error = EIO; twe_release_request(tr); return(error); } /******************************************************************************** * Handle controller-specific control operations. */ int twe_ioctl(struct twe_softc *sc, int cmd, void *addr) { struct twe_usercommand *tu = (struct twe_usercommand *)addr; struct twe_paramcommand *tp = (struct twe_paramcommand *)addr; union twe_statrequest *ts = (union twe_statrequest *)addr; TWE_Param *param; void *data; int *arg = (int *)addr; struct twe_request *tr; u_int8_t srid; int s, error; error = 0; switch(cmd) { /* handle a command from userspace */ case TWEIO_COMMAND: /* get a request */ if (twe_get_request(sc, &tr)) { error = EBUSY; goto cmd_done; } /* * Save the command's request ID, copy the user-supplied command in, * restore the request ID. */ srid = tr->tr_command.generic.request_id; bcopy(&tu->tu_command, &tr->tr_command, sizeof(TWE_Command)); tr->tr_command.generic.request_id = srid; /* if there's a data buffer, allocate and copy it in */ tr->tr_length = tu->tu_size; if (tr->tr_length > 0) { if ((tr->tr_data = malloc(tr->tr_length, M_DEVBUF, M_WAITOK)) == NULL) { error = ENOMEM; goto cmd_done; } if ((error = copyin(tu->tu_data, tr->tr_data, tr->tr_length)) != 0) goto cmd_done; tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT; } /* run the command */ twe_map_request(tr); twe_wait_request(tr); /* copy the command out again */ bcopy(&tr->tr_command, &tu->tu_command, sizeof(TWE_Command)); /* if there was a data buffer, copy it out */ if (tr->tr_length > 0) error = copyout(tr->tr_data, tu->tu_data, tr->tr_length); cmd_done: /* free resources */ if (tr->tr_data != NULL) free(tr->tr_data, M_DEVBUF); if (tr != NULL) twe_release_request(tr); break; /* fetch statistics counter */ case TWEIO_STATS: switch (ts->ts_item) { #ifdef TWE_PERFORMANCE_MONITOR case TWEQ_FREE: case TWEQ_BIO: case TWEQ_READY: case TWEQ_BUSY: case TWEQ_COMPLETE: bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat)); break; #endif default: error = ENOENT; break; } break; /* poll for an AEN */ case TWEIO_AEN_POLL: *arg = twe_dequeue_aen(sc); if (*arg == -1) error = ENOENT; break; /* wait for another AEN to show up */ case TWEIO_AEN_WAIT: s = splbio(); while ((*arg = twe_dequeue_aen(sc)) == -1) { error = tsleep(&sc->twe_aen_queue, PRIBIO | PCATCH, "tweaen", 0); if (error == EINTR) break; } splx(s); break; case TWEIO_GET_PARAM: if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) { twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n", tp->tp_table_id, tp->tp_param_id, tp->tp_size); error = EINVAL; } else { if (param->parameter_size_bytes > tp->tp_size) { twe_printf(sc, "TWEIO_GET_PARAM parameter too large (%d > %d)\n", param->parameter_size_bytes, tp->tp_size); error = EFAULT; } else { error = copyout(param->data, tp->tp_data, param->parameter_size_bytes); } free(param, M_DEVBUF); } break; case TWEIO_SET_PARAM: if ((data = malloc(tp->tp_size, M_DEVBUF, M_WAITOK)) == NULL) { error = ENOMEM; } else { error = copyin(tp->tp_data, data, tp->tp_size); if (error == 0) error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data); free(data, M_DEVBUF); } break; case TWEIO_RESET: twe_reset(sc); break; /* nothing we understand */ default: error = ENOTTY; } return(error); } /******************************************************************************** * Enable the useful interrupts from the controller. */ void twe_enable_interrupts(struct twe_softc *sc) { sc->twe_state |= TWE_STATE_INTEN; TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT | TWE_CONTROL_ENABLE_INTERRUPTS); } /******************************************************************************** * Disable interrupts from the controller. */ void twe_disable_interrupts(struct twe_softc *sc) { TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); sc->twe_state &= ~TWE_STATE_INTEN; } /******************************************************************************** ******************************************************************************** Command Submission ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Read integer parameter table entries. */ static int twe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) { TWE_Param *param; if ((param = twe_get_param(sc, table_id, param_id, 1, NULL)) == NULL) return(ENOENT); *result = *(u_int8_t *)param->data; free(param, M_DEVBUF); return(0); } static int twe_get_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t *result) { TWE_Param *param; if ((param = twe_get_param(sc, table_id, param_id, 2, NULL)) == NULL) return(ENOENT); *result = *(u_int16_t *)param->data; free(param, M_DEVBUF); return(0); } static int twe_get_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t *result) { TWE_Param *param; if ((param = twe_get_param(sc, table_id, param_id, 4, NULL)) == NULL) return(ENOENT); *result = *(u_int32_t *)param->data; free(param, M_DEVBUF); return(0); } /******************************************************************************** * Perform a TWE_OP_GET_PARAM command. If a callback function is provided, it * will be called with the command when it's completed. If no callback is * provided, we will wait for the command to complete and then return just the data. * The caller is responsible for freeing the data when done with it. */ static void * twe_get_param(struct twe_softc *sc, int table_id, int param_id, size_t param_size, void (* func)(struct twe_request *tr)) { struct twe_request *tr; TWE_Command *cmd; TWE_Param *param; int error; debug_called(4); tr = NULL; param = NULL; /* get a command */ if (twe_get_request(sc, &tr)) goto err; /* get a buffer */ if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) goto err; tr->tr_data = param; tr->tr_length = TWE_SECTOR_SIZE; tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; /* build the command for the controller */ cmd = &tr->tr_command; cmd->param.opcode = TWE_OP_GET_PARAM; cmd->param.size = 2; cmd->param.unit = 0; cmd->param.param_count = 1; /* map the command/data into controller-visible space */ twe_map_request(tr); /* fill in the outbound parameter data */ param->table_id = table_id; param->parameter_id = param_id; param->parameter_size_bytes = param_size; /* submit the command and either wait or let the callback handle it */ if (func == NULL) { /* XXX could use twe_wait_request here if interrupts were enabled? */ error = twe_immediate_request(tr); if (error == 0) { if (twe_report_request(tr)) goto err; } twe_release_request(tr); return(param); } else { tr->tr_complete = func; error = twe_start(tr); if (error == 0) return(func); } /* something failed */ err: debug(1, "failed"); if (tr != NULL) twe_release_request(tr); if (param != NULL) free(param, M_DEVBUF); return(NULL); } /******************************************************************************** * Set integer parameter table entries. */ static int twe_set_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t value) { return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); } static int twe_set_param_2(struct twe_softc *sc, int table_id, int param_id, u_int16_t value) { return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); } static int twe_set_param_4(struct twe_softc *sc, int table_id, int param_id, u_int32_t value) { return(twe_set_param(sc, table_id, param_id, sizeof(value), &value)); } /******************************************************************************** * Perform a TWE_OP_SET_PARAM command, returns nonzero on error. */ static int twe_set_param(struct twe_softc *sc, int table_id, int param_id, int param_size, void *data) { struct twe_request *tr; TWE_Command *cmd; TWE_Param *param; int error; debug_called(4); tr = NULL; param = NULL; error = ENOMEM; /* get a command */ if (twe_get_request(sc, &tr)) goto out; /* get a buffer */ if ((param = (TWE_Param *)malloc(TWE_SECTOR_SIZE, M_DEVBUF, M_NOWAIT)) == NULL) goto out; tr->tr_data = param; tr->tr_length = TWE_SECTOR_SIZE; tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; /* build the command for the controller */ cmd = &tr->tr_command; cmd->param.opcode = TWE_OP_SET_PARAM; cmd->param.size = 2; cmd->param.unit = 0; cmd->param.param_count = 1; /* map the command/data into controller-visible space */ twe_map_request(tr); /* fill in the outbound parameter data */ param->table_id = table_id; param->parameter_id = param_id; param->parameter_size_bytes = param_size; bcopy(data, param->data, param_size); /* XXX could use twe_wait_request here if interrupts were enabled? */ error = twe_immediate_request(tr); if (error == 0) { if (twe_report_request(tr)) error = EIO; } out: if (tr != NULL) twe_release_request(tr); if (param != NULL) free(param, M_DEVBUF); return(error); } /******************************************************************************** * Perform a TWE_OP_INIT_CONNECTION command, returns nonzero on error. * * Typically called with interrupts disabled. */ static int twe_init_connection(struct twe_softc *sc, int mode) { struct twe_request *tr; TWE_Command *cmd; int error; debug_called(4); /* get a command */ if (twe_get_request(sc, &tr)) return(NULL); /* build the command */ cmd = &tr->tr_command; cmd->initconnection.opcode = TWE_OP_INIT_CONNECTION; cmd->initconnection.size = 3; cmd->initconnection.host_id = 0; cmd->initconnection.message_credits = mode; cmd->initconnection.response_queue_pointer = 0; /* map the command into controller-visible space */ twe_map_request(tr); /* submit the command */ error = twe_immediate_request(tr); /* XXX check command result? */ twe_unmap_request(tr); twe_release_request(tr); if (mode == TWE_INIT_MESSAGE_CREDITS) sc->twe_host_id = cmd->initconnection.host_id; return(error); } /******************************************************************************** * Start the command (tr) and sleep waiting for it to complete. * * Successfully completed commands are dequeued. */ static int twe_wait_request(struct twe_request *tr) { int s; debug_called(4); tr->tr_flags |= TWE_CMD_SLEEPER; tr->tr_status = TWE_CMD_BUSY; twe_enqueue_ready(tr); twe_startio(tr->tr_sc); s = splbio(); while (tr->tr_status == TWE_CMD_BUSY) tsleep(tr, PRIBIO, "twewait", 0); splx(s); return(0); } /******************************************************************************** * Start the command (tr) and busy-wait for it to complete. * This should only be used when interrupts are actually disabled (although it * will work if they are not). */ static int twe_immediate_request(struct twe_request *tr) { int error; debug_called(4); error = 0; if ((error = twe_start(tr)) != 0) return(error); while (tr->tr_status == TWE_CMD_BUSY){ twe_done(tr->tr_sc); } return(tr->tr_status != TWE_CMD_COMPLETE); } /******************************************************************************** * Handle completion of an I/O command. */ static void twe_completeio(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; twe_bio *bp = (twe_bio *)tr->tr_private; debug_called(4); if (tr->tr_status == TWE_CMD_COMPLETE) { if (twe_report_request(tr)) TWE_BIO_SET_ERROR(bp, EIO); } else { twe_panic(sc, "twe_completeio on incomplete command"); } tr->tr_private = NULL; twed_intr(bp); twe_release_request(tr); } /******************************************************************************** * Reset the controller and pull all the active commands back onto the ready * queue. Used to restart a controller that's exhibiting bad behaviour. */ static void twe_reset(struct twe_softc *sc) { struct twe_request *tr; int i, s; twe_printf(sc, "controller reset in progress...\n"); /* * Disable interrupts from the controller, and mask any accidental entry * into our interrupt handler. */ twe_disable_interrupts(sc); s = splbio(); /* * Try to soft-reset the controller. */ for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { if (i > 0) twe_printf(sc, "reset %d failed, trying again\n", i); if (!twe_soft_reset(sc)) break; /* reset process complete */ } /* did we give up? */ if (i >= TWE_MAX_RESET_TRIES) { twe_printf(sc, "can't reset controller, giving up\n"); goto out; } /* * Move all of the commands that were busy back to the ready queue. */ i = 0; while ((tr = twe_dequeue_busy(sc)) != NULL) { twe_enqueue_ready(tr); i++; } /* * Kick the controller to start things going again, then re-enable interrupts. */ twe_startio(sc); twe_enable_interrupts(sc); twe_printf(sc, "controller reset done, %d commands restarted\n", i); out: splx(s); twe_enable_interrupts(sc); } /******************************************************************************** ******************************************************************************** Command I/O to Controller ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Try to deliver (tr) to the controller. * * Can be called at any interrupt level, with or without interrupts enabled. */ static int twe_start(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; int i, s, done; u_int32_t status_reg; debug_called(4); /* mark the command as currently being processed */ tr->tr_status = TWE_CMD_BUSY; /* * Spin briefly waiting for the controller to come ready * * XXX it might be more efficient to return EBUSY immediately * and let the command be rescheduled. */ for (i = 100000, done = 0; (i > 0) && !done; i--) { s = splbio(); /* check to see if we can post a command */ status_reg = TWE_STATUS(sc); twe_check_bits(sc, status_reg); if (!(status_reg & TWE_STATUS_COMMAND_QUEUE_FULL)) { TWE_COMMAND_QUEUE(sc, tr->tr_cmdphys); done = 1; /* move command to work queue */ twe_enqueue_busy(tr); #ifdef TWE_DEBUG if (tr->tr_complete != NULL) { debug(3, "queued request %d with callback %p", tr->tr_command.generic.request_id, tr->tr_complete); } else if (tr->tr_flags & TWE_CMD_SLEEPER) { debug(3, "queued request %d with wait channel %p", tr->tr_command.generic.request_id, tr); } else { debug(3, "queued request %d for polling caller", tr->tr_command.generic.request_id); } #endif } splx(s); /* drop spl to allow completion interrupts */ } /* command is enqueued */ if (done) return(0); /* * We couldn't get the controller to take the command; try submitting it again later. * This should only happen if something is wrong with the controller, or if we have * overestimated the number of commands it can accept. (Should we actually reject * the command at this point?) */ return(EBUSY); } /******************************************************************************** * Poll the controller (sc) for completed commands. * * Can be called at any interrupt level, with or without interrupts enabled. */ static void twe_done(struct twe_softc *sc) { TWE_Response_Queue rq; struct twe_request *tr; int s, found; u_int32_t status_reg; debug_called(5); /* loop collecting completed commands */ found = 0; s = splbio(); for (;;) { status_reg = TWE_STATUS(sc); twe_check_bits(sc, status_reg); /* XXX should this fail? */ if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { found = 1; rq = TWE_RESPONSE_QUEUE(sc); tr = sc->twe_lookup[rq.u.response_id]; /* find command */ if (tr->tr_status != TWE_CMD_BUSY) twe_printf(sc, "completion event for nonbusy command\n"); tr->tr_status = TWE_CMD_COMPLETE; debug(3, "completed request id %d with status %d", tr->tr_command.generic.request_id, tr->tr_command.generic.status); /* move to completed queue */ twe_remove_busy(tr); twe_enqueue_complete(tr); } else { break; /* no response ready */ } } splx(s); /* if we've completed any commands, try posting some more */ if (found) twe_startio(sc); /* handle completion and timeouts */ twe_complete(sc); /* XXX use deferred completion? */ } /******************************************************************************** * Perform post-completion processing for commands on (sc). * * This is split from twe_done as it can be safely deferred and run at a lower * priority level should facilities for such a thing become available. */ static void twe_complete(struct twe_softc *sc) { struct twe_request *tr; debug_called(5); /* * Pull commands off the completed list, dispatch them appropriately */ while ((tr = twe_dequeue_complete(sc)) != NULL) { /* unmap the command's data buffer */ twe_unmap_request(tr); /* dispatch to suit command originator */ if (tr->tr_complete != NULL) { /* completion callback */ debug(2, "call completion handler %p", tr->tr_complete); tr->tr_complete(tr); } else if (tr->tr_flags & TWE_CMD_SLEEPER) { /* caller is asleep waiting */ debug(2, "wake up command owner on %p", tr); wakeup_one(tr); } else { /* caller is polling command */ debug(2, "command left for owner"); } } } /******************************************************************************** * Wait for (status) to be set in the controller status register for up to * (timeout) seconds. Returns 0 if found, nonzero if we time out. * * Note: this busy-waits, rather than sleeping, since we may be called with * eg. clock interrupts masked. */ static int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout) { time_t expiry; u_int32_t status_reg; debug_called(4); expiry = time_second + timeout; do { status_reg = TWE_STATUS(sc); if (status_reg & status) /* got the required bit(s)? */ return(0); DELAY(100000); } while (time_second <= expiry); return(1); } /******************************************************************************** * Drain the response queue, which may contain responses to commands we know * nothing about. */ static int twe_drain_response_queue(struct twe_softc *sc) { TWE_Response_Queue rq; u_int32_t status_reg; debug_called(4); for (;;) { /* XXX give up eventually? */ status_reg = TWE_STATUS(sc); if (twe_check_bits(sc, status_reg)) return(1); if (status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) return(0); rq = TWE_RESPONSE_QUEUE(sc); } } /******************************************************************************** * Soft-reset the controller */ static int twe_soft_reset(struct twe_softc *sc) { u_int32_t status_reg; debug_called(2); TWE_SOFT_RESET(sc); if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 15)) { twe_printf(sc, "no attention interrupt\n"); return(1); } if (twe_drain_aen_queue(sc)) { twe_printf(sc, "can't drain AEN queue\n"); return(1); } if (twe_find_aen(sc, TWE_AEN_SOFT_RESET)) { twe_printf(sc, "reset not reported\n"); return(1); } status_reg = TWE_STATUS(sc); if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { twe_printf(sc, "controller errors detected\n"); return(1); } if (twe_drain_response_queue(sc)) { twe_printf(sc, "can't drain response queue\n"); return(1); } return(0); } /******************************************************************************** ******************************************************************************** Interrupt Handling ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Host interrupt. * * XXX what does this mean? */ static void twe_host_intr(struct twe_softc *sc) { debug_called(4); twe_printf(sc, "host interrupt\n"); TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); } /******************************************************************************** * Attention interrupt. * * Signalled when the controller has one or more AENs for us. */ static void twe_attention_intr(struct twe_softc *sc) { debug_called(4); /* instigate a poll for AENs */ if (twe_fetch_aen(sc)) { twe_printf(sc, "error polling for signalled AEN\n"); } else { TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); } } /******************************************************************************** * Command interrupt. * * Signalled when the controller can handle more commands. */ static void twe_command_intr(struct twe_softc *sc) { debug_called(4); /* * We don't use this, rather we try to submit commands when we receive * them, and when other commands have completed. Mask it so we don't get * another one. */ twe_printf(sc, "command interrupt\n"); TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); } /******************************************************************************** ******************************************************************************** Asynchronous Event Handling ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Request an AEN from the controller. */ static int twe_fetch_aen(struct twe_softc *sc) { debug_called(4); if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) return(EIO); return(0); } /******************************************************************************** * Handle an AEN returned by the controller. */ static void twe_handle_aen(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; TWE_Param *param; u_int16_t aen; debug_called(4); /* XXX check for command success somehow? */ param = (TWE_Param *)tr->tr_data; aen = *(u_int16_t *)(param->data); free(tr->tr_data, M_DEVBUF); twe_release_request(tr); twe_enqueue_aen(sc, aen); /* XXX poll for more AENs? */ } /******************************************************************************** * Pull AENs out of the controller and park them in the queue, in a context where * interrupts aren't active. Return nonzero if we encounter any errors in the * process of obtaining all the available AENs. */ static int twe_drain_aen_queue(struct twe_softc *sc) { u_int16_t aen; for (;;) { if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) return(1); if (aen == TWE_AEN_QUEUE_EMPTY) return(0); twe_enqueue_aen(sc, aen); } } /******************************************************************************** * Push an AEN that we've received onto the queue. * * Note that we have to lock this against reentrance, since it may be called * from both interrupt and non-interrupt context. * * If someone is waiting for the AEN we have, wake them up. */ static void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) { char *msg; int s, next, nextnext; debug_called(4); if ((msg = twe_format_aen(sc, aen)) != NULL) twe_printf(sc, "AEN: <%s>\n", msg); s = splbio(); /* enqueue the AEN */ next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH); nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH); /* check to see if this is the last free slot, and subvert the AEN if it is */ if (nextnext == sc->twe_aen_tail) aen = TWE_AEN_QUEUE_FULL; /* look to see if there's room for this AEN */ if (next != sc->twe_aen_tail) { sc->twe_aen_queue[sc->twe_aen_head] = aen; sc->twe_aen_head = next; } /* wake up anyone asleep on the queue */ wakeup(&sc->twe_aen_queue); /* anyone looking for this AEN? */ if (sc->twe_wait_aen == aen) { sc->twe_wait_aen = -1; wakeup(&sc->twe_wait_aen); } splx(s); } /******************************************************************************** * Pop an AEN off the queue, or return -1 if there are none left. * * We are more or less interrupt-safe, so don't block interrupts. */ static int twe_dequeue_aen(struct twe_softc *sc) { int result; debug_called(4); if (sc->twe_aen_tail == sc->twe_aen_head) { result = -1; } else { result = sc->twe_aen_queue[sc->twe_aen_tail]; sc->twe_aen_tail = ((sc->twe_aen_tail + 1) % TWE_Q_LENGTH); } return(result); } /******************************************************************************** * Check to see if the requested AEN is in the queue. * * XXX we could probably avoid masking interrupts here */ static int twe_find_aen(struct twe_softc *sc, u_int16_t aen) { int i, s, missing; missing = 1; s = splbio(); for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) { if (sc->twe_aen_queue[i] == aen) missing = 0; } splx(s); return(missing); } #if 0 /* currently unused */ /******************************************************************************** * Sleep waiting for at least (timeout) seconds until we see (aen) as * requested. Returns nonzero on timeout or failure. * * XXX: this should not be used in cases where there may be more than one sleeper * without a mechanism for registering multiple sleepers. */ static int twe_wait_aen(struct twe_softc *sc, int aen, int timeout) { time_t expiry; int found, s; debug_called(4); expiry = time_second + timeout; found = 0; s = splbio(); sc->twe_wait_aen = aen; do { twe_fetch_aen(sc); tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz); if (sc->twe_wait_aen == -1) found = 1; } while ((time_second <= expiry) && !found); splx(s); return(!found); } #endif /******************************************************************************** ******************************************************************************** Command Buffer Management ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Get a new command buffer. * * This will return NULL if all command buffers are in use. */ static int twe_get_request(struct twe_softc *sc, struct twe_request **tr) { debug_called(4); /* try to reuse an old buffer */ *tr = twe_dequeue_free(sc); /* initialise some fields to their defaults */ if (*tr != NULL) { (*tr)->tr_data = NULL; (*tr)->tr_private = NULL; (*tr)->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ (*tr)->tr_flags = 0; (*tr)->tr_complete = NULL; (*tr)->tr_command.generic.status = 0; /* before submission to controller */ (*tr)->tr_command.generic.flags = 0; /* not used */ } return(*tr == NULL); } /******************************************************************************** * Release a command buffer for reuse. * */ static void twe_release_request(struct twe_request *tr) { debug_called(4); if (tr->tr_private != NULL) twe_panic(tr->tr_sc, "tr_private != NULL"); twe_enqueue_free(tr); } /******************************************************************************** ******************************************************************************** Debugging ******************************************************************************** ********************************************************************************/ /******************************************************************************** * Print some information about the controller */ void twe_describe_controller(struct twe_softc *sc) { TWE_Param *p[6]; u_int8_t ports; u_int32_t size; int i; debug_called(2); /* get the port count */ twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports); /* get version strings */ p[0] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_Mon, 16, NULL); p[1] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_FW, 16, NULL); p[2] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_BIOS, 16, NULL); p[3] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCB, 8, NULL); p[4] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_ATA, 8, NULL); p[5] = twe_get_param(sc, TWE_PARAM_VERSION, TWE_PARAM_VERSION_PCI, 8, NULL); twe_printf(sc, "%d ports, Firmware %.16s, BIOS %.16s\n", ports, p[1]->data, p[2]->data); if (bootverbose) twe_printf(sc, "Monitor %.16s, PCB %.8s, Achip %.8s, Pchip %.8s\n", p[0]->data, p[3]->data, p[4]->data, p[5]->data); free(p[0], M_DEVBUF); free(p[1], M_DEVBUF); free(p[2], M_DEVBUF); free(p[3], M_DEVBUF); free(p[4], M_DEVBUF); free(p[5], M_DEVBUF); /* print attached drives */ if (bootverbose) { p[0] = twe_get_param(sc, TWE_PARAM_DRIVESUMMARY, TWE_PARAM_DRIVESUMMARY_Status, 16, NULL); for (i = 0; i < ports; i++) { if (p[0]->data[i] != TWE_PARAM_DRIVESTATUS_Present) continue; twe_get_param_4(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Size, &size); p[1] = twe_get_param(sc, TWE_PARAM_DRIVEINFO + i, TWE_PARAM_DRIVEINFO_Model, 40, NULL); if (p[1] != NULL) { twe_printf(sc, "port %d: %.40s %dMB\n", i, p[1]->data, size / 2048); free(p[1], M_DEVBUF); } else { twe_printf(sc, "port %d, drive status unavailable\n", i); } } free(p[0], M_DEVBUF); } } /******************************************************************************** * Complain if the status bits aren't what we're expecting. * * Rate-limit the complaints to at most one of each every five seconds, but * always return the correct status. */ static int twe_check_bits(struct twe_softc *sc, u_int32_t status_reg) { int result; static time_t lastwarn[2] = {0, 0}; /* * This can be a little problematic, as twe_panic may call twe_reset if * TWE_DEBUG is not set, which will call us again as part of the soft reset. */ if ((status_reg & TWE_STATUS_PANIC_BITS) != 0) { twe_printf(sc, "FATAL STATUS BIT(S) %b\n", status_reg & TWE_STATUS_PANIC_BITS, TWE_STATUS_BITS_DESCRIPTION); twe_panic(sc, "fatal status bits"); } result = 0; if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { if (time_second > (lastwarn[0] + 5)) { twe_printf(sc, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, TWE_STATUS_BITS_DESCRIPTION); lastwarn[0] = time_second; } result = 1; } if ((status_reg & TWE_STATUS_UNEXPECTED_BITS) != 0) { if (time_second > (lastwarn[1] + 5)) { twe_printf(sc, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, TWE_STATUS_BITS_DESCRIPTION); lastwarn[1] = time_second; } result = 1; } return(result); } /******************************************************************************** * Return a string describing (aen). * * The low 8 bits of the aen are the code, the high 8 bits give the unit number * where an AEN is specific to a unit. * * Note that we could expand this routine to handle eg. up/downgrading the status * of a drive if we had some idea of what the drive's initial status was. */ static char * twe_format_aen(struct twe_softc *sc, u_int16_t aen) { static char buf[80]; device_t child; char *code, *msg; code = twe_describe_code(twe_table_aen, TWE_AEN_CODE(aen)); msg = code + 2; switch (*code) { case 'q': if (!bootverbose) return(NULL); /* FALLTHROUGH */ case 'p': return(msg); case 'c': if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) { sprintf(buf, "twed%d: %s", device_get_unit(child), msg); } else { sprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev), msg, TWE_AEN_UNIT(aen)); } return(buf); case 'x': default: break; } sprintf(buf, "unknown AEN 0x%x", aen); return(buf); } /******************************************************************************** * Print a diagnostic if the status of the command warrants it, and return * either zero (command was ok) or nonzero (command failed). */ static int twe_report_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; TWE_Command *cmd = &tr->tr_command; - int result; + int result = 0; - switch (cmd->generic.flags) { - case TWE_FLAGS_SUCCESS: - result = 0; - break; - case TWE_FLAGS_INFORMATIONAL: - case TWE_FLAGS_WARNING: - twe_printf(sc, "command completed - %s\n", - twe_describe_code(twe_table_status, cmd->generic.status)); - result = 0; - break; - - case TWE_FLAGS_FATAL: - default: - twe_printf(sc, "command failed - %s\n", - twe_describe_code(twe_table_status, cmd->generic.status)); + /* + * Check the command status value and handle accordingly. + */ + if (cmd->generic.status == TWE_STATUS_RESET) { + /* + * The status code 0xff requests a controller reset. + */ + twe_printf(sc, "command returned with controller rest request\n"); + twe_reset(sc); result = 1; - + } else if (cmd->generic.status > TWE_STATUS_FATAL) { /* - * The status code 0xff requests a controller reset + * Fatal errors that don't require controller reset. */ - if (cmd->generic.status == 0xff) - twe_reset(sc); - break; + twe_printf(sc, "command returned fatal status - %s (flags = 0x%x)\n", + twe_describe_code(twe_table_status, cmd->generic.status), + cmd->generic.flags); + result = 1; + } else if (cmd->generic.status > TWE_STATUS_WARNING) { + /* + * Warning level status. + */ + twe_printf(sc, "command returned warning status - %s (flags = 0x%x)\n", + twe_describe_code(twe_table_status, cmd->generic.status), + cmd->generic.flags); + } else if (cmd->generic.status > 0x40) { + /* + * Info level status. + */ + twe_printf(sc, "command returned info status: %s (flags = 0x%x)\n", + twe_describe_code(twe_table_status, cmd->generic.status), + cmd->generic.flags); } + return(result); } /******************************************************************************** * Print some controller state to aid in debugging error/panic conditions */ void twe_print_controller(struct twe_softc *sc) { u_int32_t status_reg; status_reg = TWE_STATUS(sc); twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); twe_printf(sc, " current max\n"); twe_printf(sc, "free %04d %04d\n", sc->twe_qstat[TWEQ_FREE].q_length, sc->twe_qstat[TWEQ_FREE].q_max); twe_printf(sc, "ready %04d %04d\n", sc->twe_qstat[TWEQ_READY].q_length, sc->twe_qstat[TWEQ_READY].q_max); twe_printf(sc, "busy %04d %04d\n", sc->twe_qstat[TWEQ_BUSY].q_length, sc->twe_qstat[TWEQ_BUSY].q_max); twe_printf(sc, "complete %04d %04d\n", sc->twe_qstat[TWEQ_COMPLETE].q_length, sc->twe_qstat[TWEQ_COMPLETE].q_max); twe_printf(sc, "bioq %04d %04d\n", sc->twe_qstat[TWEQ_BIO].q_length, sc->twe_qstat[TWEQ_BIO].q_max); twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); } static void twe_panic(struct twe_softc *sc, char *reason) { twe_print_controller(sc); #ifdef TWE_DEBUG panic(reason); #else twe_reset(sc); #endif } #if 0 /******************************************************************************** * Print a request/command in human-readable format. */ static void twe_print_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; TWE_Command *cmd = &tr->tr_command; int i; twe_printf(sc, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", cmd->generic.request_id, twe_describe_code(twe_table_opcode, cmd->generic.opcode), cmd->generic.size, cmd->generic.unit, cmd->generic.host_id); twe_printf(sc, " status %d flags 0x%x count %d sgl_offset %d\n", cmd->generic.status, cmd->generic.flags, cmd->generic.count, cmd->generic.sgl_offset); switch(cmd->generic.opcode) { /* XXX add more opcodes? */ case TWE_OP_READ: case TWE_OP_WRITE: twe_printf(sc, " lba %d\n", cmd->io.lba); for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->io.sgl[i].length != 0); i++) twe_printf(sc, " %d: 0x%x/%d\n", i, cmd->io.sgl[i].address, cmd->io.sgl[i].length); break; case TWE_OP_GET_PARAM: case TWE_OP_SET_PARAM: for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->param.sgl[i].length != 0); i++) twe_printf(sc, " %d: 0x%x/%d\n", i, cmd->param.sgl[i].address, cmd->param.sgl[i].length); break; case TWE_OP_INIT_CONNECTION: twe_printf(sc, " response queue pointer 0x%x\n", cmd->initconnection.response_queue_pointer); break; default: break; } twe_printf(sc, " tr_command %p/0x%x tr_data %p/0x%x,%d\n", tr, tr->tr_cmdphys, tr->tr_data, tr->tr_dataphys, tr->tr_length); twe_printf(sc, " tr_status %d tr_flags 0x%x tr_complete %p tr_private %p\n", tr->tr_status, tr->tr_flags, tr->tr_complete, tr->tr_private); } #endif diff --git a/sys/dev/twe/twe_compat.h b/sys/dev/twe/twe_compat.h index c2f75e1a8708..ed82631a3aa6 100644 --- a/sys/dev/twe/twe_compat.h +++ b/sys/dev/twe/twe_compat.h @@ -1,179 +1,182 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Portability and compatibility interfaces. */ #ifdef __FreeBSD__ /****************************************************************************** * FreeBSD */ #define TWE_SUPPORTED_PLATFORM #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * These macros allows us to build a version of the driver which can * safely be loaded into a kernel which already contains a 'twe' driver, * and which will override it in all things. * * All public symbols must be listed here. */ #ifdef TWE_OVERRIDE #define twe_setup Xtwe_setup #define twe_init Xtwe_init #define twe_deinit Xtwe_deinit #define twe_intr Xtwe_intr #define twe_submit_bio Xtwe_submit_bio #define twe_ioctl Xtwe_ioctl #define twe_describe_controller Xtwe_describe_controller #define twe_print_controller Xtwe_print_controller #define twe_enable_interrupts Xtwe_enable_interrupts #define twe_disable_interrupts Xtwe_disable_interrupts #define twe_attach_drive Xtwe_attach_drive #define twed_intr Xtwed_intr #define twe_allocate_request Xtwe_allocate_request #define twe_free_request Xtwe_free_request #define twe_map_request Xtwe_map_request #define twe_unmap_request Xtwe_unmap_request #define twe_describe_code Xtwe_describe_code #define twe_table_status Xtwe_table_status #define twe_table_unitstate Xtwe_table_unitstate #define twe_table_unittype Xtwe_table_unittype #define twe_table_aen Xtwe_table_aen #define TWE_DRIVER_NAME Xtwe #define TWED_DRIVER_NAME Xtwed #define TWE_MALLOC_CLASS M_XTWE #else #define TWE_DRIVER_NAME twe #define TWED_DRIVER_NAME twed #define TWE_MALLOC_CLASS M_TWE #endif /* * Wrappers for bus-space actions */ #define TWE_CONTROL(sc, val) bus_space_write_4(sc->twe_btag, sc->twe_bhandle, 0x0, (u_int32_t)val) #define TWE_STATUS(sc) (u_int32_t)bus_space_read_4(sc->twe_btag, sc->twe_bhandle, 0x4) #define TWE_COMMAND_QUEUE(sc, val) bus_space_write_4(sc->twe_btag, sc->twe_bhandle, 0x8, (u_int32_t)val) #define TWE_RESPONSE_QUEUE(sc) (TWE_Response_Queue)bus_space_read_4(sc->twe_btag, sc->twe_bhandle, 0xc) /* * FreeBSD-specific softc elements */ #define TWE_PLATFORM_SOFTC \ device_t twe_dev; /* bus device */ \ dev_t twe_dev_t; /* control device */ \ struct resource *twe_io; /* register interface window */ \ bus_space_handle_t twe_bhandle; /* bus space handle */ \ bus_space_tag_t twe_btag; /* bus space tag */ \ bus_dma_tag_t twe_parent_dmat; /* parent DMA tag */ \ bus_dma_tag_t twe_buffer_dmat; /* data buffer DMA tag */ \ struct resource *twe_irq; /* interrupt */ \ void *twe_intr; /* interrupt handle */ \ struct intr_config_hook twe_ich; /* delayed-startup hook */ /* * FreeBSD-specific request elements */ #define TWE_PLATFORM_REQUEST \ bus_dmamap_t tr_cmdmap; /* DMA map for command */ \ u_int32_t tr_cmdphys; /* address of command in controller space */ \ bus_dmamap_t tr_dmamap; /* DMA map for data */ \ u_int32_t tr_dataphys; /* data buffer base address in controller space */ /* * Output identifying the controller/disk */ #define twe_printf(sc, fmt, args...) device_printf(sc->twe_dev, fmt , ##args) #define twed_printf(twed, fmt, args...) device_printf(twed->twed_dev, fmt , ##args) -#if __FreeBSD_version < 500003 /* old buf style */ -# include +#if __FreeBSD_version < 500003 +# include +# define INTR_ENTROPY 0 + +# include /* old buf style */ typedef struct buf twe_bio; typedef struct buf_queue_head twe_bioq; # define TWE_BIO_QINIT(bq) bufq_init(&bq); # define TWE_BIO_QINSERT(bq, bp) bufq_insert_tail(&bq, bp) # define TWE_BIO_QFIRST(bq) bufq_first(&bq) # define TWE_BIO_QREMOVE(bq, bp) bufq_remove(&bq, bp) # define TWE_BIO_IS_READ(bp) ((bp)->b_flags & B_READ) # define TWE_BIO_DATA(bp) (bp)->b_data # define TWE_BIO_LENGTH(bp) (bp)->b_bcount # define TWE_BIO_LBA(bp) (bp)->b_pblkno # define TWE_BIO_SOFTC(bp) (bp)->b_dev->si_drv1 # define TWE_BIO_UNIT(bp) *(int *)((bp)->b_dev->si_drv2) # define TWE_BIO_SET_ERROR(bp, err) do { (bp)->b_error = err; (bp)->b_flags |= B_ERROR;} while(0) # define TWE_BIO_HAS_ERROR(bp) ((bp)->b_flags & B_ERROR) # define TWE_BIO_RESID(bp) (bp)->b_resid # define TWE_BIO_DONE(bp) biodone(bp) # define TWE_BIO_STATS_START(bp) devstat_start_transaction(&((struct twed_softc *)TWE_BIO_SOFTC(bp))->twed_stats) # define TWE_BIO_STATS_END(bp) devstat_end_transaction_buf(&((struct twed_softc *)TWE_BIO_SOFTC(bp))->twed_stats, bp) #else # include typedef struct bio twe_bio; typedef struct bio_queue_head twe_bioq; # define TWE_BIO_QINIT(bq) bioq_init(&bq); # define TWE_BIO_QINSERT(bq, bp) bioq_insert_tail(&bq, bp) # define TWE_BIO_QFIRST(bq) bioq_first(&bq) # define TWE_BIO_QREMOVE(bq, bp) bioq_remove(&bq, bp) # define TWE_BIO_IS_READ(bp) ((bp)->bio_cmd == BIO_READ) # define TWE_BIO_DATA(bp) (bp)->bio_data # define TWE_BIO_LENGTH(bp) (bp)->bio_bcount # define TWE_BIO_LBA(bp) (bp)->bio_pblkno # define TWE_BIO_SOFTC(bp) (bp)->bio_dev->si_drv1 # define TWE_BIO_UNIT(bp) *(int *)((bp)->bio_dev->si_drv2) # define TWE_BIO_SET_ERROR(bp, err) do { (bp)->bio_error = err; (bp)->bio_flags |= BIO_ERROR;} while(0) # define TWE_BIO_HAS_ERROR(bp) ((bp)->bio_flags & BIO_ERROR) # define TWE_BIO_RESID(bp) (bp)->bio_resid # define TWE_BIO_DONE(bp) biodone(bp) # define TWE_BIO_STATS_START(bp) devstat_start_transaction(&((struct twed_softc *)TWE_BIO_SOFTC(bp))->twed_stats) # define TWE_BIO_STATS_END(bp) devstat_end_transaction_bio(&((struct twed_softc *)TWE_BIO_SOFTC(bp))->twed_stats, bp) #endif #endif /* FreeBSD */ #ifndef TWE_SUPPORTED_PLATFORM #error platform not supported #endif diff --git a/sys/dev/twe/twe_freebsd.c b/sys/dev/twe/twe_freebsd.c index 677e7af82a33..4b442e1127ff 100644 --- a/sys/dev/twe/twe_freebsd.c +++ b/sys/dev/twe/twe_freebsd.c @@ -1,1045 +1,1043 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * FreeBSD-specific code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static devclass_t twe_devclass; #ifdef TWE_DEBUG static u_int32_t twed_bio_in; #define TWED_BIO_IN twed_bio_in++ static u_int32_t twed_bio_out; #define TWED_BIO_OUT twed_bio_out++ #else #define TWED_BIO_IN #define TWED_BIO_OUT #endif /******************************************************************************** ******************************************************************************** Control device interface ******************************************************************************** ********************************************************************************/ static d_open_t twe_open; static d_close_t twe_close; static d_ioctl_t twe_ioctl_wrapper; #define TWE_CDEV_MAJOR 146 static struct cdevsw twe_cdevsw = { twe_open, twe_close, noread, nowrite, twe_ioctl_wrapper, nopoll, nommap, nostrategy, "twe", TWE_CDEV_MAJOR, nodump, nopsize, - 0, - -1 + 0 }; /******************************************************************************** * Accept an open operation on the control device. */ static int twe_open(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); sc->twe_state |= TWE_STATE_OPEN; return(0); } /******************************************************************************** * Accept the last close on the control device. */ static int twe_close(dev_t dev, int flags, int fmt, struct proc *p) { int unit = minor(dev); struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); sc->twe_state &= ~TWE_STATE_OPEN; return (0); } /******************************************************************************** * Handle controller-specific control operations. */ static int twe_ioctl_wrapper(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) { struct twe_softc *sc = (struct twe_softc *)dev->si_drv1; return(twe_ioctl(sc, cmd, addr)); } /******************************************************************************** ******************************************************************************** PCI device interface ******************************************************************************** ********************************************************************************/ static int twe_probe(device_t dev); static int twe_attach(device_t dev); static void twe_free(struct twe_softc *sc); static int twe_detach(device_t dev); static int twe_shutdown(device_t dev); static int twe_suspend(device_t dev); static int twe_resume(device_t dev); static void twe_pci_intr(void *arg); static void twe_intrhook(void *arg); static device_method_t twe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, twe_probe), DEVMETHOD(device_attach, twe_attach), DEVMETHOD(device_detach, twe_detach), DEVMETHOD(device_shutdown, twe_shutdown), DEVMETHOD(device_suspend, twe_suspend), DEVMETHOD(device_resume, twe_resume), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), { 0, 0 } }; static driver_t twe_pci_driver = { "twe", twe_methods, sizeof(struct twe_softc) }; #ifdef TWE_OVERRIDE DRIVER_MODULE(Xtwe, pci, twe_pci_driver, twe_devclass, 0, 0); #else DRIVER_MODULE(twe, pci, twe_pci_driver, twe_devclass, 0, 0); #endif /******************************************************************************** * Match a 3ware Escalade ATA RAID controller. */ static int twe_probe(device_t dev) { debug_called(4); if ((pci_get_vendor(dev) == TWE_VENDOR_ID) && ((pci_get_device(dev) == TWE_DEVICE_ID) || (pci_get_device(dev) == TWE_DEVICE_ID_ASIC))) { device_set_desc(dev, TWE_DEVICE_NAME); #ifdef TWE_OVERRIDE return(0); #else return(-10); #endif } return(ENXIO); } /******************************************************************************** * Allocate resources, initialise the controller. */ static int twe_attach(device_t dev) { struct twe_softc *sc; int rid, error; u_int32_t command; debug_called(4); /* * Initialise the softc structure. */ sc = device_get_softc(dev); sc->twe_dev = dev; /* * Make sure we are going to be able to talk to this board. */ command = pci_read_config(dev, PCIR_COMMAND, 2); if ((command & PCIM_CMD_PORTEN) == 0) { twe_printf(sc, "register window not available\n"); return(ENXIO); } /* * Force the busmaster enable bit on, in case the BIOS forgot. */ command |= PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, command, 2); /* * Allocate the PCI register window. */ rid = TWE_IO_CONFIG_REG; if ((sc->twe_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE)) == NULL) { twe_printf(sc, "can't allocate register window\n"); twe_free(sc); return(ENXIO); } sc->twe_btag = rman_get_bustag(sc->twe_io); sc->twe_bhandle = rman_get_bushandle(sc->twe_io); /* * Allocate the parent bus DMA tag appropriate for PCI. */ if (bus_dma_tag_create(NULL, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, TWE_MAX_SGL_LENGTH, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ &sc->twe_parent_dmat)) { twe_printf(sc, "can't allocate parent DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Allocate and connect our interrupt. */ rid = 0; if ((sc->twe_irq = bus_alloc_resource(sc->twe_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { twe_printf(sc, "can't allocate interrupt\n"); twe_free(sc); return(ENXIO); } if (bus_setup_intr(sc->twe_dev, sc->twe_irq, INTR_TYPE_BIO | INTR_ENTROPY, twe_pci_intr, sc, &sc->twe_intr)) { twe_printf(sc, "can't set up interrupt\n"); twe_free(sc); return(ENXIO); } /* * Create DMA tag for mapping objects into controller-addressable space. */ if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, TWE_MAX_SGL_LENGTH,/* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ &sc->twe_buffer_dmat)) { twe_printf(sc, "can't allocate data buffer DMA tag\n"); twe_free(sc); return(ENOMEM); } /* * Initialise the controller and driver core. */ if ((error = twe_setup(sc))) return(error); /* * Print some information about the controller and configuration. */ twe_describe_controller(sc); /* * Create the control device. */ sc->twe_dev_t = make_dev(&twe_cdevsw, device_get_unit(sc->twe_dev), UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR, "twe%d", device_get_unit(sc->twe_dev)); sc->twe_dev_t->si_drv1 = sc; /* * Schedule ourselves to bring the controller up once interrupts are available. * This isn't strictly necessary, since we disable interrupts while probing the * controller, but it is more in keeping with common practice for other disk * devices. */ sc->twe_ich.ich_func = twe_intrhook; sc->twe_ich.ich_arg = sc; if (config_intrhook_establish(&sc->twe_ich) != 0) { twe_printf(sc, "can't establish configuration hook\n"); twe_free(sc); return(ENXIO); } return(0); } /******************************************************************************** * Free all of the resources associated with (sc). * * Should not be called if the controller is active. */ static void twe_free(struct twe_softc *sc) { struct twe_request *tr; debug_called(4); /* throw away any command buffers */ while ((tr = twe_dequeue_free(sc)) != NULL) twe_free_request(tr); /* destroy the data-transfer DMA tag */ if (sc->twe_buffer_dmat) bus_dma_tag_destroy(sc->twe_buffer_dmat); /* disconnect the interrupt handler */ if (sc->twe_intr) bus_teardown_intr(sc->twe_dev, sc->twe_irq, sc->twe_intr); if (sc->twe_irq != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IRQ, 0, sc->twe_irq); /* destroy the parent DMA tag */ if (sc->twe_parent_dmat) bus_dma_tag_destroy(sc->twe_parent_dmat); /* release the register window mapping */ if (sc->twe_io != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IOPORT, TWE_IO_CONFIG_REG, sc->twe_io); /* destroy control device */ if (sc->twe_dev_t != (dev_t)NULL) destroy_dev(sc->twe_dev_t); } /******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ static int twe_detach(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int s, error; debug_called(4); error = EBUSY; s = splbio(); if (sc->twe_state & TWE_STATE_OPEN) goto out; /* * Shut the controller down. */ if ((error = twe_shutdown(dev))) goto out; twe_free(sc); error = 0; out: splx(s); return(error); } /******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int twe_shutdown(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int i, s, error; debug_called(4); s = splbio(); error = 0; /* * Delete all our child devices. */ for (i = 0; i < TWE_MAX_UNITS; i++) { if (sc->twe_drive[i].td_disk != 0) { if ((error = device_delete_child(sc->twe_dev, sc->twe_drive[i].td_disk)) != 0) goto out; sc->twe_drive[i].td_disk = 0; } } /* * Bring the controller down. */ twe_deinit(sc); out: splx(s); return(error); } /******************************************************************************** * Bring the controller to a quiescent state, ready for system suspend. */ static int twe_suspend(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int s; debug_called(4); s = splbio(); sc->twe_state |= TWE_STATE_SUSPEND; twe_disable_interrupts(sc); splx(s); return(0); } /******************************************************************************** * Bring the controller back to a state ready for operation. */ static int twe_resume(device_t dev) { struct twe_softc *sc = device_get_softc(dev); debug_called(4); sc->twe_state &= ~TWE_STATE_SUSPEND; twe_enable_interrupts(sc); return(0); } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ static void twe_pci_intr(void *arg) { twe_intr((struct twe_softc *)arg); } /******************************************************************************** * Delayed-startup hook */ static void twe_intrhook(void *arg) { struct twe_softc *sc = (struct twe_softc *)arg; /* pull ourselves off the intrhook chain */ config_intrhook_disestablish(&sc->twe_ich); /* call core startup routine */ twe_init(sc); } /******************************************************************************** * Given a detected drive, attach it to the bio interface. * * This is called from twe_init. */ void twe_attach_drive(struct twe_softc *sc, struct twe_drive *dr) { char buf[80]; int error; dr->td_disk = device_add_child(sc->twe_dev, NULL, -1); if (dr->td_disk == NULL) { twe_printf(sc, "device_add_child failed\n"); return; } device_set_ivars(dr->td_disk, dr); /* * XXX It would make sense to test the online/initialising bits, but they seem to be * always set... */ sprintf(buf, "%s, %s", twe_describe_code(twe_table_unittype, dr->td_type), twe_describe_code(twe_table_unitstate, dr->td_state & TWE_PARAM_UNITSTATUS_MASK)); device_set_desc_copy(dr->td_disk, buf); if ((error = bus_generic_attach(sc->twe_dev)) != 0) twe_printf(sc, "bus_generic_attach returned %d\n", error); } /******************************************************************************** ******************************************************************************** Disk device ******************************************************************************** ********************************************************************************/ /* * Disk device softc */ struct twed_softc { device_t twed_dev; dev_t twed_dev_t; struct twe_softc *twed_controller; /* parent device softc */ struct twe_drive *twed_drive; /* drive data in parent softc */ struct disk twed_disk; /* generic disk handle */ struct devstat twed_stats; /* accounting */ struct disklabel twed_label; /* synthetic label */ int twed_flags; #define TWED_OPEN (1<<0) /* drive is open (can't shut down) */ }; /* * Disk device bus interface */ static int twed_probe(device_t dev); static int twed_attach(device_t dev); static int twed_detach(device_t dev); static device_method_t twed_methods[] = { DEVMETHOD(device_probe, twed_probe), DEVMETHOD(device_attach, twed_attach), DEVMETHOD(device_detach, twed_detach), { 0, 0 } }; static driver_t twed_driver = { "twed", twed_methods, sizeof(struct twed_softc) }; static devclass_t twed_devclass; #ifdef TWE_OVERRIDE DRIVER_MODULE(Xtwed, Xtwe, twed_driver, twed_devclass, 0, 0); #else DRIVER_MODULE(twed, twe, twed_driver, twed_devclass, 0, 0); #endif /* * Disk device control interface. */ static d_open_t twed_open; static d_close_t twed_close; static d_strategy_t twed_strategy; static d_dump_t twed_dump; #define TWED_CDEV_MAJOR 147 static struct cdevsw twed_cdevsw = { twed_open, twed_close, physread, physwrite, noioctl, nopoll, nommap, twed_strategy, "twed", TWED_CDEV_MAJOR, twed_dump, nopsize, - D_DISK, - -1 + D_DISK }; static struct cdevsw tweddisk_cdevsw; #ifdef FREEBSD_4 static int disks_registered = 0; #endif /******************************************************************************** * Handle open from generic layer. * * Note that this is typically only called by the diskslice code, and not * for opens on subdevices (eg. slices, partitions). */ static int twed_open(dev_t dev, int flags, int fmt, struct proc *p) { struct twed_softc *sc = (struct twed_softc *)dev->si_drv1; struct disklabel *label; debug_called(4); if (sc == NULL) return (ENXIO); /* check that the controller is up and running */ if (sc->twed_controller->twe_state & TWE_STATE_SHUTDOWN) return(ENXIO); /* build synthetic label */ label = &sc->twed_disk.d_label; bzero(label, sizeof(*label)); label->d_type = DTYPE_ESDI; label->d_secsize = TWE_BLOCK_SIZE; label->d_nsectors = sc->twed_drive->td_sectors; label->d_ntracks = sc->twed_drive->td_heads; label->d_ncylinders = sc->twed_drive->td_cylinders; label->d_secpercyl = sc->twed_drive->td_sectors * sc->twed_drive->td_heads; label->d_secperunit = sc->twed_drive->td_size; sc->twed_flags |= TWED_OPEN; return (0); } /******************************************************************************** * Handle last close of the disk device. */ static int twed_close(dev_t dev, int flags, int fmt, struct proc *p) { struct twed_softc *sc = (struct twed_softc *)dev->si_drv1; debug_called(4); if (sc == NULL) return (ENXIO); sc->twed_flags &= ~TWED_OPEN; return (0); } /******************************************************************************** * Handle an I/O request. */ static void twed_strategy(twe_bio *bp) { struct twed_softc *sc = (struct twed_softc *)TWE_BIO_SOFTC(bp); debug_called(4); TWED_BIO_IN; /* bogus disk? */ if (sc == NULL) { TWE_BIO_SET_ERROR(bp, EINVAL); printf("twe: bio for invalid disk!\n"); TWE_BIO_DONE(bp); TWED_BIO_OUT; return; } /* perform accounting */ TWE_BIO_STATS_START(bp); /* queue the bio on the controller */ twe_enqueue_bio(sc->twed_controller, bp); /* poke the controller to start I/O */ twe_startio(sc->twed_controller); return; } /******************************************************************************** * System crashdump support */ int twed_dump(dev_t dev) { struct twed_softc *twed_sc = (struct twed_softc *)dev->si_drv1; struct twe_softc *twe_sc = (struct twe_softc *)twed_sc->twed_controller; u_int count, blkno, secsize; vm_offset_t addr = 0; long blkcnt; int dumppages = MAXDUMPPGS; int error; int i; if ((error = disk_dumpcheck(dev, &count, &blkno, &secsize))) return(error); if (!twed_sc || !twe_sc) return(ENXIO); blkcnt = howmany(PAGE_SIZE, secsize); while (count > 0) { caddr_t va = NULL; if ((count / blkcnt) < dumppages) dumppages = count / blkcnt; for (i = 0; i < dumppages; ++i) { vm_offset_t a = addr + (i * PAGE_SIZE); if (is_physical_memory(a)) va = pmap_kenter_temporary(trunc_page(a), i); else va = pmap_kenter_temporary(trunc_page(0), i); } if ((error = twe_dump_blocks(twe_sc, twed_sc->twed_drive->td_unit, blkno, va, (PAGE_SIZE * dumppages) / TWE_BLOCK_SIZE)) != 0) return(error); if (dumpstatus(addr, (long)(count * DEV_BSIZE)) < 0) return(EINTR); blkno += blkcnt * dumppages; count -= blkcnt * dumppages; addr += PAGE_SIZE * dumppages; } return(0); } /******************************************************************************** * Handle completion of an I/O request. */ void twed_intr(twe_bio *bp) { debug_called(4); /* if no error, transfer completed */ if (!TWE_BIO_HAS_ERROR(bp)) TWE_BIO_RESID(bp) = 0; TWE_BIO_STATS_END(bp); TWE_BIO_DONE(bp); TWED_BIO_OUT; } /******************************************************************************** * Default probe stub. */ static int twed_probe(device_t dev) { return (0); } /******************************************************************************** * Attach a unit to the controller. */ static int twed_attach(device_t dev) { struct twed_softc *sc; device_t parent; dev_t dsk; debug_called(4); /* initialise our softc */ sc = device_get_softc(dev); parent = device_get_parent(dev); sc->twed_controller = (struct twe_softc *)device_get_softc(parent); sc->twed_drive = device_get_ivars(dev); sc->twed_dev = dev; /* report the drive */ twed_printf(sc, "%uMB (%u sectors)\n", sc->twed_drive->td_size / ((1024 * 1024) / TWE_BLOCK_SIZE), sc->twed_drive->td_size); devstat_add_entry(&sc->twed_stats, "twed", device_get_unit(dev), TWE_BLOCK_SIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_ARRAY); /* attach a generic disk device to ourselves */ dsk = disk_create(device_get_unit(dev), &sc->twed_disk, 0, &twed_cdevsw, &tweddisk_cdevsw); dsk->si_drv1 = sc; dsk->si_drv2 = &sc->twed_drive->td_unit; sc->twed_dev_t = dsk; #ifdef FREEBSD_4 disks_registered++; #endif /* set the maximum I/O size to the theoretical maximum allowed by the S/G list size */ dsk->si_iosize_max = (TWE_MAX_SGL_LENGTH - 1) * PAGE_SIZE; return (0); } /******************************************************************************** * Disconnect ourselves from the system. */ static int twed_detach(device_t dev) { struct twed_softc *sc = (struct twed_softc *)device_get_softc(dev); debug_called(4); if (sc->twed_flags & TWED_OPEN) return(EBUSY); devstat_remove_entry(&sc->twed_stats); #ifdef FREEBSD_4 if (--disks_registered == 0) cdevsw_remove(&tweddisk_cdevsw); #else disk_destroy(sc->twed_dev_t); #endif return(0); } /******************************************************************************** ******************************************************************************** Misc ******************************************************************************** ********************************************************************************/ static void twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); static void twe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); /******************************************************************************** * Allocate a command buffer */ MALLOC_DEFINE(TWE_MALLOC_CLASS, "twe commands", "twe commands"); struct twe_request * twe_allocate_request(struct twe_softc *sc) { struct twe_request *tr; if ((tr = malloc(sizeof(struct twe_request), TWE_MALLOC_CLASS, M_NOWAIT)) == NULL) return(NULL); bzero(tr, sizeof(*tr)); tr->tr_sc = sc; if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_cmdmap)) { twe_free_request(tr); return(NULL); } if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_dmamap)) { bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_cmdmap); twe_free_request(tr); return(NULL); } return(tr); } /******************************************************************************** * Permanently discard a command buffer. */ void twe_free_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; debug_called(4); bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_cmdmap); bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_dmamap); free(tr, TWE_MALLOC_CLASS); } /******************************************************************************** * Map/unmap (tr)'s command and data in the controller's addressable space. * * These routines ensure that the data which the controller is going to try to * access is actually visible to the controller, in a machine-independant * fasion. Due to a hardware limitation, I/O buffers must be 512-byte aligned * and we take care of that here as well. */ static void twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct twe_request *tr = (struct twe_request *)arg; TWE_Command *cmd = &tr->tr_command; int i; debug_called(4); /* save base of first segment in command (applicable if there only one segment) */ tr->tr_dataphys = segs[0].ds_addr; /* correct command size for s/g list size */ tr->tr_command.generic.size += 2 * nsegments; /* * Due to the fact that parameter and I/O commands have the scatter/gather list in * different places, we need to determine which sort of command this actually is * before we can populate it correctly. */ switch(cmd->generic.opcode) { case TWE_OP_GET_PARAM: case TWE_OP_SET_PARAM: cmd->generic.sgl_offset = 2; for (i = 0; i < nsegments; i++) { cmd->param.sgl[i].address = segs[i].ds_addr; cmd->param.sgl[i].length = segs[i].ds_len; } for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ cmd->param.sgl[i].address = 0; cmd->param.sgl[i].length = 0; } break; case TWE_OP_READ: case TWE_OP_WRITE: cmd->generic.sgl_offset = 3; for (i = 0; i < nsegments; i++) { cmd->io.sgl[i].address = segs[i].ds_addr; cmd->io.sgl[i].length = segs[i].ds_len; } for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ cmd->io.sgl[i].address = 0; cmd->io.sgl[i].length = 0; } break; default: /* no s/g list, nothing to do */ } } static void twe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) { struct twe_request *tr = (struct twe_request *)arg; debug_called(4); /* command can't cross a page boundary */ tr->tr_cmdphys = segs[0].ds_addr; } void twe_map_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; debug_called(4); /* * Map the command into bus space. */ bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_cmdmap, &tr->tr_command, sizeof(tr->tr_command), twe_setup_request_dmamap, tr, 0); bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_cmdmap, BUS_DMASYNC_PREWRITE); /* * If the command involves data, map that too. */ if (tr->tr_data != NULL) { /* * Data must be 64-byte aligned; allocate a fixup buffer if it's not. */ if (((vm_offset_t)tr->tr_data % TWE_ALIGNMENT) != 0) { tr->tr_realdata = tr->tr_data; /* save pointer to 'real' data */ tr->tr_flags |= TWE_CMD_ALIGNBUF; tr->tr_data = malloc(tr->tr_length, TWE_MALLOC_CLASS, M_NOWAIT); /* XXX check result here */ } /* * Map the data buffer into bus space and build the s/g list. */ bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_dmamap, tr->tr_data, tr->tr_length, twe_setup_data_dmamap, tr, 0); if (tr->tr_flags & TWE_CMD_DATAIN) bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREREAD); if (tr->tr_flags & TWE_CMD_DATAOUT) { /* if we're using an alignment buffer, and we're writing data, copy the real data out */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) bcopy(tr->tr_realdata, tr->tr_data, tr->tr_length); bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREWRITE); } } } void twe_unmap_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; debug_called(4); /* * Unmap the command from bus space. */ bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_cmdmap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_cmdmap); /* * If the command involved data, unmap that too. */ if (tr->tr_data != NULL) { if (tr->tr_flags & TWE_CMD_DATAIN) { bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTREAD); /* if we're using an alignment buffer, and we're reading data, copy the real data in */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) bcopy(tr->tr_data, tr->tr_realdata, tr->tr_length); } if (tr->tr_flags & TWE_CMD_DATAOUT) bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_dmamap); } /* free alignment buffer if it was used */ if (tr->tr_flags & TWE_CMD_ALIGNBUF) { free(tr->tr_data, TWE_MALLOC_CLASS); tr->tr_data = tr->tr_realdata; /* restore 'real' data pointer */ } } #ifdef TWE_DEBUG /******************************************************************************** * Print current controller status, call from DDB. */ void twe_report(void) { struct twe_softc *sc; int i, s; s = splbio(); for (i = 0; (sc = devclass_get_softc(twe_devclass, i)) != NULL; i++) twe_print_controller(sc); printf("twed: total bio count in %u out %u\n", twed_bio_in, twed_bio_out); splx(s); } #endif diff --git a/sys/dev/twe/twereg.h b/sys/dev/twe/twereg.h index 3d6d6686827d..e7136df2ac55 100644 --- a/sys/dev/twe/twereg.h +++ b/sys/dev/twe/twereg.h @@ -1,456 +1,462 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Register names, bit definitions, structure names and members are * identical with those in the Linux driver where possible and sane * for simplicity's sake. (The TW_ prefix has become TWE_) * Some defines that are clearly irrelevant to FreeBSD have been * removed. */ /* control register bit definitions */ #define TWE_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000 #define TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000 #define TWE_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000 #define TWE_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000 #define TWE_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000 #define TWE_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000 #define TWE_CONTROL_CLEAR_ERROR_STATUS 0x00000200 #define TWE_CONTROL_ISSUE_SOFT_RESET 0x00000100 #define TWE_CONTROL_ENABLE_INTERRUPTS 0x00000080 #define TWE_CONTROL_DISABLE_INTERRUPTS 0x00000040 #define TWE_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020 #define TWE_SOFT_RESET(sc) TWE_CONTROL(sc, TWE_CONTROL_ISSUE_SOFT_RESET | \ TWE_CONTROL_CLEAR_HOST_INTERRUPT | \ TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT | \ TWE_CONTROL_MASK_COMMAND_INTERRUPT | \ TWE_CONTROL_MASK_RESPONSE_INTERRUPT | \ TWE_CONTROL_CLEAR_ERROR_STATUS | \ TWE_CONTROL_DISABLE_INTERRUPTS) /* status register bit definitions */ #define TWE_STATUS_MAJOR_VERSION_MASK 0xF0000000 #define TWE_STATUS_MINOR_VERSION_MASK 0x0F000000 #define TWE_STATUS_PCI_PARITY_ERROR 0x00800000 #define TWE_STATUS_QUEUE_ERROR 0x00400000 #define TWE_STATUS_MICROCONTROLLER_ERROR 0x00200000 #define TWE_STATUS_PCI_ABORT 0x00100000 #define TWE_STATUS_HOST_INTERRUPT 0x00080000 #define TWE_STATUS_ATTENTION_INTERRUPT 0x00040000 #define TWE_STATUS_COMMAND_INTERRUPT 0x00020000 #define TWE_STATUS_RESPONSE_INTERRUPT 0x00010000 #define TWE_STATUS_COMMAND_QUEUE_FULL 0x00008000 #define TWE_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000 #define TWE_STATUS_MICROCONTROLLER_READY 0x00002000 #define TWE_STATUS_COMMAND_QUEUE_EMPTY 0x00001000 #define TWE_STATUS_ALL_INTERRUPTS 0x000F0000 #define TWE_STATUS_CLEARABLE_BITS 0x00D00000 #define TWE_STATUS_EXPECTED_BITS 0x00002000 #define TWE_STATUS_UNEXPECTED_BITS 0x00F80000 /* XXX this is a little harsh, but necessary to chase down firmware problems */ #define TWE_STATUS_PANIC_BITS (TWE_STATUS_MICROCONTROLLER_ERROR) /* for use with the %b printf format */ #define TWE_STATUS_BITS_DESCRIPTION \ "\20\15CQEMPTY\16UCREADY\17RQEMPTY\20CQFULL\21RINTR\22CINTR\23AINTR\24HINTR\25PCIABRT\26MCERR\27QERR\30PCIPERR\n" /* detect inconsistencies in the status register */ #define TWE_STATUS_ERRORS(x) \ (((x & TWE_STATUS_PCI_ABORT) || \ (x & TWE_STATUS_PCI_PARITY_ERROR) || \ (x & TWE_STATUS_QUEUE_ERROR) || \ (x & TWE_STATUS_MICROCONTROLLER_ERROR)) && \ (x & TWE_STATUS_MICROCONTROLLER_READY)) /* Response queue bit definitions */ #define TWE_RESPONSE_ID_MASK 0x00000FF0 /* PCI related defines */ #define TWE_IO_CONFIG_REG 0x10 #define TWE_DEVICE_NAME "3ware Storage Controller" #define TWE_VENDOR_ID 0x13C1 #define TWE_DEVICE_ID 0x1000 #define TWE_DEVICE_ID_ASIC 0x1001 /* command packet opcodes */ #define TWE_OP_NOP 0x00 #define TWE_OP_INIT_CONNECTION 0x01 #define TWE_OP_READ 0x02 #define TWE_OP_WRITE 0x03 #define TWE_OP_READVERIFY 0x04 #define TWE_OP_VERIFY 0x05 #define TWE_OP_ZEROUNIT 0x08 #define TWE_OP_REPLACEUNIT 0x09 #define TWE_OP_HOTSWAP 0x0a #define TWE_OP_SETATAFEATURE 0x0c #define TWE_OP_FLUSH 0x0e #define TWE_OP_ABORT 0x0f #define TWE_OP_CHECKSTATUS 0x10 #define TWE_OP_GET_PARAM 0x12 #define TWE_OP_SET_PARAM 0x13 #define TWE_OP_CREATEUNIT 0x14 #define TWE_OP_DELETEUNIT 0x15 #define TWE_OP_REBUILDUNIT 0x17 #define TWE_OP_SECTOR_INFO 0x1a #define TWE_OP_AEN_LISTEN 0x1c #define TWE_OP_CMD_PACKET 0x1d +/* command status values */ +#define TWE_STATUS_RESET 0xff /* controller requests reset */ +#define TWE_STATUS_FATAL 0xc0 /* fatal errors not requiring reset */ +#define TWE_STATUS_WARNING 0x80 /* warnings */ +#define TWE_STAUS_INFO 0x40 /* informative status */ + /* misc defines */ #define TWE_ALIGNMENT 0x200 #define TWE_MAX_UNITS 16 #define TWE_COMMAND_ALIGNMENT_MASK 0x1ff #define TWE_INIT_MESSAGE_CREDITS 0xff /* older firmware has issues with 256 commands */ #define TWE_SHUTDOWN_MESSAGE_CREDITS 0x001 #define TWE_INIT_COMMAND_PACKET_SIZE 0x3 #define TWE_MAX_SGL_LENGTH 62 #define TWE_Q_LENGTH TWE_INIT_MESSAGE_CREDITS #define TWE_Q_START 0 #define TWE_MAX_RESET_TRIES 3 #define TWE_BLOCK_SIZE 0x200 /* 512-byte blocks */ #define TWE_SECTOR_SIZE 0x200 /* generic I/O bufffer */ #define TWE_IOCTL 0x80 #define TWE_MAX_AEN_TRIES 100 #define TWE_UNIT_ONLINE 1 /* scatter/gather list entry */ typedef struct { u_int32_t address; u_int32_t length; } TWE_SG_Entry __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_INITCONNECTION */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t res2:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int16_t message_credits; u_int32_t response_queue_pointer; } TWE_Command_INITCONNECTION __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_READ/TWE_OP_WRITE */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int16_t block_count; u_int32_t lba; TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH]; } TWE_Command_IO __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_HOTSWAP */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int8_t action; #define TWE_OP_HOTSWAP_REMOVE 0x00 /* remove assumed-degraded unit */ #define TWE_OP_HOTSWAP_ADD_CBOD 0x01 /* add CBOD to empty port */ #define TWE_OP_HOTSWAP_ADD_SPARE 0x02 /* add spare to empty port */ u_int8_t aport; } TWE_Command_HOTSWAP __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_SETATAFEATURE */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int8_t feature; #define TWE_OP_SETATAFEATURE_WCE 0x02 #define TWE_OP_SETATAFEATURE_DIS_WCE 0x82 u_int8_t feature_mode; u_int16_t all_units; u_int16_t persistence; } TWE_Command_SETATAFEATURE __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_CHECKSTATUS */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t res2:4; u_int8_t status; u_int8_t flags; u_int16_t target_status; /* set low byte to target request's ID */ } TWE_Command_CHECKSTATUS __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_GETPARAM, TWE_OP_SETPARAM */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int16_t param_count; TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH]; } TWE_Command_PARAM __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; /* TWE_OP_REBUILDUNIT */ u_int8_t res1:3; u_int8_t size; u_int8_t request_id; u_int8_t src_unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; u_int8_t action:7; #define TWE_OP_REBUILDUNIT_NOP 0 #define TWE_OP_REBUILDUNIT_STOP 2 /* stop all rebuilds */ #define TWE_OP_REBUILDUNIT_START 4 /* start rebuild with lowest unit */ #define TWE_OP_REBUILDUNIT_STARTUNIT 5 /* rebuild src_unit (not supported) */ u_int8_t cs:1; /* request state change on src_unit */ u_int8_t logical_subunit; /* for RAID10 rebuild of logical subunit */ } TWE_Command_REBUILDUNIT __attribute__ ((packed)); typedef struct { u_int8_t opcode:5; u_int8_t sgl_offset:3; u_int8_t size; u_int8_t request_id; u_int8_t unit:4; u_int8_t host_id:4; u_int8_t status; u_int8_t flags; #define TWE_FLAGS_SUCCESS 0x00 #define TWE_FLAGS_INFORMATIONAL 0x01 #define TWE_FLAGS_WARNING 0x02 #define TWE_FLAGS_FATAL 0x03 #define TWE_FLAGS_PERCENTAGE (1<<8) /* bits 0-6 indicate completion percentage */ u_int16_t count; /* block count, parameter count, message credits */ } TWE_Command_Generic __attribute__ ((packed)); /* command packet - must be TWE_ALIGNMENT aligned */ typedef union { TWE_Command_INITCONNECTION initconnection; TWE_Command_IO io; TWE_Command_PARAM param; TWE_Command_CHECKSTATUS checkstatus; TWE_Command_REBUILDUNIT rebuildunit; TWE_Command_SETATAFEATURE setatafeature; TWE_Command_Generic generic; u_int8_t pad[512]; } TWE_Command; /* response queue entry */ typedef union { struct { u_int32_t undefined_1:4; u_int32_t response_id:8; u_int32_t undefined_2:20; } u; u_int32_t value; } TWE_Response_Queue; /* * From 3ware's documentation: * All parameters maintained by the controller are grouped into related tables. * Tables are are accessed indirectly via get and set parameter commands. * To access a specific parameter in a table, the table ID and parameter index * are used to uniquely identify a parameter. Table 0xffff is the directory * table and provides a list of the table IDs and sizes of all other tables. * Index zero in each table specifies the entire table, and index one specifies * the size of the table. An entire table can be read or set by using index zero. */ #define TWE_PARAM_PARAM_ALL 0 #define TWE_PARAM_PARAM_SIZE 1 #define TWE_PARAM_DIRECTORY 0xffff /* size is 4 * number of tables */ #define TWE_PARAM_DIRECTORY_TABLES 2 /* 16 bits * number of tables */ #define TWE_PARAM_DIRECTORY_SIZES 3 /* 16 bits * number of tables */ #define TWE_PARAM_DRIVESUMMARY 0x0002 #define TWE_PARAM_DRIVESUMMARY_Num 2 /* number of physical drives [2] */ #define TWE_PARAM_DRIVESUMMARY_Status 3 /* array giving drive status per aport */ #define TWE_PARAM_DRIVESTATUS_Missing 0x00 #define TWE_PARAM_DRIVESTATUS_NotSupp 0xfe #define TWE_PARAM_DRIVESTATUS_Present 0xff #define TWE_PARAM_UNITSUMMARY 0x0003 #define TWE_PARAM_UNITSUMMARY_Num 2 /* number of logical units [2] */ #define TWE_PARAM_UNITSUMMARY_Status 3 /* array giving unit status [16] */ #define TWE_PARAM_UNITSTATUS_Online (1<<0) #define TWE_PARAM_UNITSTATUS_Complete (1<<1) #define TWE_PARAM_UNITSTATUS_MASK 0xfc #define TWE_PARAM_UNITSTATUS_Normal 0xfc #define TWE_PARAM_UNITSTATUS_Initialising 0xf4 /* cannot be incomplete */ #define TWE_PARAM_UNITSTATUS_Degraded 0xec #define TWE_PARAM_UNITSTATUS_Rebuilding 0xdc /* cannot be incomplete */ #define TWE_PARAM_UNITSTATUS_Verifying 0xcc /* cannot be incomplete */ #define TWE_PARAM_UNITSTATUS_Corrupt 0xbc /* cannot be complete */ #define TWE_PARAM_UNITSTATUS_Missing 0x00 /* cannot be complete or online */ #define TWE_PARAM_DRIVEINFO 0x0200 /* add drive number 0x00-0x0f XXX docco confused 0x0100 vs 0x0200 */ #define TWE_PARAM_DRIVEINFO_Size 2 /* size in blocks [4] */ #define TWE_PARAM_DRIVEINFO_Model 3 /* drive model string [40] */ #define TWE_PARAM_DRIVEINFO_Serial 4 /* drive serial number [20] */ #define TWE_PARAM_DRIVEINFO_PhysCylNum 5 /* physical geometry [2] */ #define TWE_PARAM_DRIVEINFO_PhysHeadNum 6 /* [2] */ #define TWE_PARAM_DRIVEINFO_PhysSectorNym 7 /* [2] */ #define TWE_PARAM_DRIVEINFO_LogCylNum 8 /* logical geometry [2] */ #define TWE_PARAM_DRIVEINFO_LogHeadNum 9 /* [2] */ #define TWE_PARAM_DRIVEINFO_LogSectorNum 10 /* [2] */ #define TWE_PARAM_DRIVEINFO_UnitNum 11 /* unit number this drive is associated with or 0xff [1] */ #define TWE_PARAM_DRIVEINFO_DriveFlags 12 /* N/A [1] */ #define TWE_PARAM_APORTTIMEOUT 0x02c0 /* add (aport_number * 3) to parameter index */ #define TWE_PARAM_APORTTIMEOUT_READ 2 /* read timeouts last 24hrs [2] */ #define TWE_PARAM_APORTTIMEOUT_WRITE 3 /* write timeouts last 24hrs [2] */ #define TWE_PARAM_APORTTIMEOUT_DEGRADE 4 /* degrade threshold [2] */ #define TWE_PARAM_UNITINFO 0x0300 /* add unit number 0x00-0x0f */ #define TWE_PARAM_UNITINFO_Number 2 /* unit number [1] */ #define TWE_PARAM_UNITINFO_Status 3 /* unit status [1] */ #define TWE_PARAM_UNITINFO_Capacity 4 /* unit capacity in blocks [4] */ #define TWE_PARAM_UNITINFO_DescriptorSize 5 /* unit descriptor size + 3 bytes [2] */ #define TWE_PARAM_UNITINFO_Descriptor 6 /* unit descriptor, TWE_UnitDescriptor or TWE_Array_Descriptor */ #define TWE_PARAM_UNITINFO_Flags 7 /* unit flags [1] */ #define TWE_PARAM_UNITFLAGS_WCE (1<<0) #define TWE_PARAM_AEN 0x0401 #define TWE_PARAM_AEN_UnitCode 2 /* (unit number << 8) | AEN code [2] */ #define TWE_AEN_QUEUE_EMPTY 0x00 #define TWE_AEN_SOFT_RESET 0x01 #define TWE_AEN_DEGRADED_MIRROR 0x02 /* reports unit */ #define TWE_AEN_CONTROLLER_ERROR 0x03 #define TWE_AEN_REBUILD_FAIL 0x04 /* reports unit */ #define TWE_AEN_REBUILD_DONE 0x05 /* reports unit */ #define TWE_AEN_INCOMP_UNIT 0x06 /* reports unit */ #define TWE_AEN_INIT_DONE 0x07 /* reports unit */ #define TWE_AEN_UNCLEAN_SHUTDOWN 0x08 /* reports unit */ #define TWE_AEN_APORT_TIMEOUT 0x09 /* reports unit, rate limited to 1 per 2^16 errors */ #define TWE_AEN_DRIVE_ERROR 0x0a /* reports unit */ #define TWE_AEN_REBUILD_STARTED 0x0b /* reports unit */ #define TWE_AEN_QUEUE_FULL 0xff #define TWE_AEN_TABLE_UNDEFINED 0x15 #define TWE_AEN_CODE(x) ((x) & 0xff) #define TWE_AEN_UNIT(x) ((x) >> 8) #define TWE_PARAM_VERSION 0x0402 #define TWE_PARAM_VERSION_Mon 2 /* monitor version [16] */ #define TWE_PARAM_VERSION_FW 3 /* firmware version [16] */ #define TWE_PARAM_VERSION_BIOS 4 /* BIOSs version [16] */ #define TWE_PARAM_VERSION_PCB 5 /* PCB version [8] */ #define TWE_PARAM_VERSION_ATA 6 /* A-chip version [8] */ #define TWE_PARAM_VERSION_PCI 7 /* P-chip version [8] */ #define TWE_PARAM_VERSION_CtrlModel 8 /* N/A */ #define TWE_PARAM_VERSION_CtrlSerial 9 /* N/A */ #define TWE_PARAM_VERSION_SBufSize 10 /* N/A */ #define TWE_PARAM_VERSION_CompCode 11 /* compatibility code [4] */ #define TWE_PARAM_CONTROLLER 0x0403 #define TWE_PARAM_CONTROLLER_DCBSectors 2 /* # sectors reserved for DCB per drive [2] */ #define TWE_PARAM_CONTROLLER_PortCount 3 /* number of drive ports [1] */ #define TWE_PARAM_FEATURES 0x404 #define TWE_PARAM_FEATURES_DriverShutdown 2 /* set to 1 if driver supports shutdown notification [1] */ typedef struct { u_int8_t num_subunits; /* must be zero */ u_int8_t configuration; #define TWE_UD_CONFIG_CBOD 0x0c /* JBOD with DCB, used for mirrors */ #define TWE_UD_CONFIG_SPARE 0x0d /* same as CBOD, but firmware will use as spare */ #define TWE_UD_CONFIG_SUBUNIT 0x0e /* drive is a subunit in an array */ #define TWE_UD_CONFIG_JBOD 0x0f /* plain drive */ u_int8_t phys_drv_num; /* may be 0xff if port can't be determined at runtime */ u_int8_t log_drv_num; /* must be zero for configuration == 0x0f */ u_int32_t start_lba; u_int32_t block_count; /* actual drive size if configuration == 0x0f, otherwise less DCB size */ } TWE_Unit_Descriptor __attribute__ ((packed)); typedef struct { u_int8_t flag; /* must be 0xff */ u_int8_t res1; u_int8_t mirunit_status[4]; /* bitmap of functional subunits in each mirror */ u_int8_t res2[6]; } TWE_Mirror_Descriptor __attribute__ ((packed)); typedef struct { u_int8_t num_subunits; /* number of subunits, or number of mirror units in RAID10 */ u_int8_t configuration; #define TWE_UD_CONFIG_RAID0 0x00 #define TWE_UD_CONFIG_RAID1 0x01 #define TWE_UD_CONFIG_TwinStor 0x02 #define TWE_UD_CONFIG_RAID5 0x05 #define TWE_UD_CONFIG_RAID10 0x06 u_int8_t stripe_size; #define TWE_UD_STRIPE_4k 0x03 #define TWE_UD_STRIPE_8k 0x04 #define TWE_UD_STRIPE_16k 0x05 #define TWE_UD_STRIPE_32k 0x06 #define TWE_UD_STRIPE_64k 0x07 u_int8_t log_drv_status; /* bitmap of functional subunits, or mirror units in RAID10 */ u_int32_t start_lba; u_int32_t block_count; /* actual drive size if configuration == 0x0f, otherwise less DCB size */ TWE_Unit_Descriptor subunit[0]; /* subunit descriptors, in RAID10 mode is [mirunit][subunit] */ } TWE_Array_Descriptor __attribute__ ((packed)); typedef struct { u_int16_t table_id; u_int8_t parameter_id; u_int8_t parameter_size_bytes; u_int8_t data[0]; } TWE_Param __attribute__ ((packed)); diff --git a/sys/dev/twe/twevar.h b/sys/dev/twe/twevar.h index 639a4253f8b8..3e06bb5fefdb 100644 --- a/sys/dev/twe/twevar.h +++ b/sys/dev/twe/twevar.h @@ -1,266 +1,266 @@ /*- * Copyright (c) 2000 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifdef TWE_DEBUG #define debug(level, fmt, args...) \ do { \ if (level <= TWE_DEBUG) printf("%s: " fmt "\n", __FUNCTION__ , ##args); \ } while(0) #define debug_called(level) \ do { \ if (level <= TWE_DEBUG) printf(__FUNCTION__ ": called\n"); \ } while(0) #else #define debug(level, fmt, args...) #define debug_called(level) #endif /* * Structure describing a logical unit as attached to the controller */ struct twe_drive { /* unit properties */ u_int32_t td_size; int td_cylinders; int td_heads; int td_sectors; int td_unit; /* unit state and type */ u_int8_t td_state; u_int8_t td_type; /* handle for attached driver */ device_t td_disk; }; /* * Per-command control structure. * * Note that due to alignment constraints on the tc_command field, this *must* be 64-byte aligned. * We achieve this by placing the command at the beginning of the structure, and using malloc() * to allocate each structure. */ struct twe_request { /* controller command */ TWE_Command tr_command; /* command as submitted to controller */ /* command payload */ void *tr_data; /* data buffer */ void *tr_realdata; /* copy of real data buffer pointer for alignment fixup */ size_t tr_length; TAILQ_ENTRY(twe_request) tr_link; /* list linkage */ struct twe_softc *tr_sc; /* controller that owns us */ int tr_status; /* command status */ #define TWE_CMD_SETUP 0 /* being assembled */ #define TWE_CMD_BUSY 1 /* submitted to controller */ #define TWE_CMD_COMPLETE 2 /* completed by controller (maybe with error) */ int tr_flags; #define TWE_CMD_DATAIN (1<<0) #define TWE_CMD_DATAOUT (1<<1) #define TWE_CMD_ALIGNBUF (1<<2) /* data in bio is misaligned, have to copy to/from private buffer */ #define TWE_CMD_SLEEPER (1<<3) /* owner is sleeping on this command */ void (* tr_complete)(struct twe_request *tr); /* completion handler */ void *tr_private; /* submitter-private data or wait channel */ TWE_PLATFORM_REQUEST /* platform-specific request elements */ }; /* * Per-controller state. */ struct twe_softc { /* controller queues and arrays */ TAILQ_HEAD(, twe_request) twe_free; /* command structures available for reuse */ twe_bioq twe_bioq; /* outstanding I/O operations */ TAILQ_HEAD(, twe_request) twe_ready; /* requests ready for the controller */ TAILQ_HEAD(, twe_request) twe_busy; /* requests busy in the controller */ TAILQ_HEAD(, twe_request) twe_complete; /* active commands (busy or waiting for completion) */ struct twe_request *twe_lookup[TWE_Q_LENGTH]; /* commands indexed by request ID */ struct twe_drive twe_drive[TWE_MAX_UNITS]; /* attached drives */ /* asynchronous event handling */ u_int16_t twe_aen_queue[TWE_Q_LENGTH]; /* AENs queued for userland tool(s) */ int twe_aen_head, twe_aen_tail; /* ringbuffer pointers for AEN queue */ int twe_wait_aen; /* wait-for-aen notification */ /* controller status */ int twe_state; #define TWE_STATE_INTEN (1<<0) /* interrupts have been enabled */ #define TWE_STATE_SHUTDOWN (1<<1) /* controller is shut down */ #define TWE_STATE_OPEN (1<<2) /* control device is open */ #define TWE_STATE_SUSPEND (1<<3) /* controller is suspended */ int twe_host_id; struct twe_qstat twe_qstat[TWEQ_COUNT]; /* queue statistics */ TWE_PLATFORM_SOFTC /* platform-specific softc elements */ }; /* * Interface betwen driver core and platform-dependant code. */ extern int twe_setup(struct twe_softc *sc); /* do early driver/controller setup */ extern void twe_init(struct twe_softc *sc); /* init controller */ extern void twe_deinit(struct twe_softc *sc); /* stop controller */ extern void twe_intr(struct twe_softc *sc); /* hardware interrupt signalled */ extern void twe_startio(struct twe_softc *sc); extern int twe_dump_blocks(struct twe_softc *sc, int unit, /* crashdump block write */ u_int32_t lba, void *data, int nblks); extern int twe_ioctl(struct twe_softc *sc, int cmd, void *addr); /* handle user request */ extern void twe_describe_controller(struct twe_softc *sc); /* print controller info */ extern void twe_print_controller(struct twe_softc *sc); extern void twe_enable_interrupts(struct twe_softc *sc); /* enable controller interrupts */ extern void twe_disable_interrupts(struct twe_softc *sc); /* disable controller interrupts */ extern void twe_attach_drive(struct twe_softc *sc, struct twe_drive *dr); /* attach drive when found in twe_init */ extern void twed_intr(twe_bio *bp); /* return bio from core */ extern struct twe_request *twe_allocate_request(struct twe_softc *sc); /* allocate request structure */ extern void twe_free_request(struct twe_request *tr); /* free request structure */ extern void twe_map_request(struct twe_request *tr); /* make request visible to controller, do s/g */ extern void twe_unmap_request(struct twe_request *tr); /* cleanup after transfer, unmap */ /******************************************************************************** * Queue primitives */ #define TWEQ_ADD(sc, qname) \ do { \ struct twe_qstat *qs = &(sc)->twe_qstat[qname]; \ \ qs->q_length++; \ if (qs->q_length > qs->q_max) \ qs->q_max = qs->q_length; \ } while(0) #define TWEQ_REMOVE(sc, qname) (sc)->twe_qstat[qname].q_length-- #define TWEQ_INIT(sc, qname) \ do { \ sc->twe_qstat[qname].q_length = 0; \ sc->twe_qstat[qname].q_max = 0; \ } while(0) #define TWEQ_REQUEST_QUEUE(name, index) \ static __inline void \ twe_initq_ ## name (struct twe_softc *sc) \ { \ TAILQ_INIT(&sc->twe_ ## name); \ TWEQ_INIT(sc, index); \ } \ static __inline void \ twe_enqueue_ ## name (struct twe_request *tr) \ { \ int s; \ \ s = splbio(); \ TAILQ_INSERT_TAIL(&tr->tr_sc->twe_ ## name, tr, tr_link); \ TWEQ_ADD(tr->tr_sc, index); \ splx(s); \ } \ static __inline void \ twe_requeue_ ## name (struct twe_request *tr) \ { \ int s; \ \ s = splbio(); \ TAILQ_INSERT_HEAD(&tr->tr_sc->twe_ ## name, tr, tr_link); \ TWEQ_ADD(tr->tr_sc, index); \ splx(s); \ } \ static __inline struct twe_request * \ twe_dequeue_ ## name (struct twe_softc *sc) \ { \ struct twe_request *tr; \ int s; \ \ s = splbio(); \ if ((tr = TAILQ_FIRST(&sc->twe_ ## name)) != NULL) { \ TAILQ_REMOVE(&sc->twe_ ## name, tr, tr_link); \ TWEQ_REMOVE(sc, index); \ } \ splx(s); \ return(tr); \ } \ static __inline void \ twe_remove_ ## name (struct twe_request *tr) \ { \ int s; \ \ s = splbio(); \ TAILQ_REMOVE(&tr->tr_sc->twe_ ## name, tr, tr_link); \ TWEQ_REMOVE(tr->tr_sc, index); \ splx(s); \ } TWEQ_REQUEST_QUEUE(free, TWEQ_FREE) TWEQ_REQUEST_QUEUE(ready, TWEQ_READY) TWEQ_REQUEST_QUEUE(busy, TWEQ_BUSY) TWEQ_REQUEST_QUEUE(complete, TWEQ_COMPLETE) /* * outstanding bio queue */ static __inline void twe_initq_bio(struct twe_softc *sc) { TWE_BIO_QINIT(sc->twe_bioq); TWEQ_INIT(sc, TWEQ_BIO); } static __inline void -twe_enqueue_bio(struct twe_softc *sc, struct bio *bp) +twe_enqueue_bio(struct twe_softc *sc, twe_bio *bp) { int s; s = splbio(); TWE_BIO_QINSERT(sc->twe_bioq, bp); TWEQ_ADD(sc, TWEQ_BIO); splx(s); } -static __inline struct bio * +static __inline twe_bio * twe_dequeue_bio(struct twe_softc *sc) { int s; - struct bio *bp; + twe_bio *bp; s = splbio(); if ((bp = TWE_BIO_QFIRST(sc->twe_bioq)) != NULL) { TWE_BIO_QREMOVE(sc->twe_bioq, bp); TWEQ_REMOVE(sc, TWEQ_BIO); } splx(s); return(bp); }